Dojo学习笔记——创建Dojo存储

来源:互联网 发布:易语言编程实例教程 编辑:程序博客网 时间:2024/05/16 07:08

难度:中级 Dojo版本:1.8
原文:http://dojotoolkit.org/documentation/tutorials/1.8/creating_stores/

开始
新的dojo/store系统想要代替旧的dojo.data系统,部分是基于新的W3C对象存储API,目的是使得创建数据存储组件尽可能简单。创建符合dojo/store的存储相当简单,大多数方法是可选的,只要需要其功能时才需实现。
基本的Dojo存储只实现部分IndexedDBAPI,基本上这些方法涉及到存储内外的数据获取。IndexedDBAPI的某些方面(例如索引和游标)没有实现,主要是因为它们在纯JavaScript环境中是不必要的。

创建第一个存储
Dojo中最简单的存储为Memory store,用于处理大多数原始的数据管理任务。这是dojo/store/Memory的基本API:
define(["../_base/declare", "dojo/store/util/SimpleQueryEngine"],function(declare, SimpleQueryEngine) {declare("dojo.store.Memory", null, {constructor: function(options){ },data: null,index: null,queryEngine: SimpleQueryEngine,// what follows is the actual API signatureidProperty: "id",get: function(id){ },getIdentity: function(object){ },put: function(object, options){ },add: function(object, options){ },remove: function(id){ },query: function(query, options){ },setData: function(data){ }});});
可见dojo/store/Memory的签名相当简单,它涉及获取任何种类的数据(getquery),确保解决一致性问题(idPropertygetIdentity),允许创建新的条目,删除条目和更新条目(分别是addremoveput)。另外,提供了设置初始数据集的方式(setData)。

内部数据结构
存储没有指定一组对象的实际数据结构,这是故意的,因为数据的结构通常依赖于数据的应用,存储没有事务来指定结构。
但是有一点应当总是在自定义存储中实现,即每一个数据对象唯一的标识符。存储的idProperty性质指明哪个项目属性作为唯一标识符。默认为id,但是可以是任何属性。
可以写一个存储来处理数据而不使用idProperty,但强烈建议不要这样。最终没有某种唯一标识符的存储,每次都要依赖于在数据结构的所有元素中查找,从性能的角度看这将非常昂贵。

query:存储中最重要的方法
目前任何存储中最重要的方法是query方法。这是主要的从存储获取信息的方式,而不改变任何内部数据结构。该方法必须接受两个参数,query对象和可选的options对象。
query对象包含查询的标准和依赖于底层的数据引擎。dojo/store带有一个内建的查询引擎dojo/store/util/SimpleQueryEngine,该引擎处理大多数基本的查询需求,也可作为一个模板来写更为复杂的查询引擎。

创建查询引擎
dojo/store/util/SimpleQueryEngine演示了创建查询引擎的基本方法,想法是创建和返回一个函数,在一个对象数组上做一些类型的过滤(需要的话和其它事情),使用初始的标准集合传递给查询引擎。
创建查询引擎,要遵循SimpleQueryEngine的结构,在一个闭包捕获查询参数,返回一个把一个元素数组作为唯一的参数的函数。基本的例子如下:
require(["dojo/_base/array"],function(arrayUtil){var myEngine = function(query, options){var filteringFunction = function(object){// do something here based on the passed query object};var execute = function(array){var results = arrayUtil.filter(array, filteringFunction);// do anything else needed, like sorting and paginationreturn results;}execute.matches = filteringFunction;return execute;}
可以总是基于SimpleQueryEngine写自定义查询引擎,来处理任何所要的明确需求。也可以直接在存储的query方法创建一些什么,如果想要的话,例如让查询方法与远程服务器通信和让服务器返回结果。
确保有一个查询引擎可以操作数组的数据只是在自定义存储中创建query方法的第一步,第二步是保证query方法包装dojo/store/util/QueryResults的返回值。

QueryResults是什么
dojo/store/util/QueryResults是一个简单的包装函数,用于查询的结果。其确保标准迭代方法存在于结果集,包括forEachmapfilter
传递给QueryResultsresults对象可以是数组或是promise,即可以传递一个promise对象给QueryResults函数,相同的迭代函数依然可以用。
下面在Dojo的两个存储中看一下query方法,Memory存储和JsonRest存储。首先是Memory存储:
define(["dojo/store/util/QueryResults"],function(QueryResults){....// from dojo/store/Memoryquery: function(query, options){return QueryResults((this.queryEngine(query, options))(this.data));}
Memory存储的内部数据结构是一个对象数组。通过调用QueryResults,所有重要的迭代方法直接加入到结果对象,这意味着最好直接调用迭代方法:
var results = myMemoryStore.query({ foo: "bar" });results.forEach(function(item){// do something with the item});
下面是JsonRest存储的query方法:
// from dojo/store/JsonRestquery: function(query, options){var headers = {Accept: "application/javascript, application/json"};options = options || {};if(options.start >= 0 || options.count >= 0){headers.Range = "items=" + (options.start || '0') + '-' +(("count" in options && options.count != Infinity) ?(options.count + (options.start || 0) - 1) : '');}// lang is from dojo/_base/langif(lang.isObject(query)){// ioQuery from dojo/io-queryquery = ioQuery.objectToQuery(query);query = query ? "?" + query: "";}if(options && options.sort){query += (query ? "&" : "?") + "sort(";for(var i = 0; i < options.sort.length; i++) {var sort = options.sort[i];query += (i > 0 ? "," : "")+ (sort.descending ? '-' : '+')+ encodeURIComponent(sort.attribute);}query += ")";}// request from dojo/requestvar results = request.get(this.target + (query || ""), {handleAs: "json",headers: headers});results.total = results.then(function(){var range = results.response.getHeaders("Content-Range");return range && (range=range.match(/\/(.*)/)) && +range[1];});return QueryResults(results);}
这里JsonRest存储没有使用查询引擎,而是使用dojo/request调用了REST服务,其本身返回一个promiseQueryResults函数确保在返回的promise上常见的迭代方法是可用的,以及这些方法看似以恰当的方式表现。
在内部QueryResults使用了dojo.when。要注意的是当写自定义存储时,应当总是确保query函数返回一个dojo/store/util/QueryResults包装的对象。

创建存储
为简单起见,该存储最终将看上去就像是Memory存储,因为将只是保存一个内部数组的数据以及操作它。
define(["dojo/store/util/QueryResults", "dojo/_base/declare", "dojo/_base/lang", "dojo/request", "dojo/store/util/SimpleQueryEngine"],function(QueryResults, declare, lang, request, SimpleQueryEngine){// Declare the initial storereturn declare(null, {data: [],index: {},idProperty: "id",queryEngine: SimpleQueryEngine,constructor: function(options){lang.mixin(this, options || {});this.setData(this.data || []);},query: function(query, options){return QueryResults((this.queryEngine(query, options))(this.data));},setData: function(data){this.data = data;// index our datathis.index = {};for(var i = 0, l = data.length; i < l; i++){var object = data[i];this.index[object[this.idProperty]] = object;}}});});
注意构造函数的lang.mixin声明,这是一个常见的做法,允许通过构造函数的options参数对实例属性进行规范。在这里,最常用来为dataidProperty设置值。

添加getters
示例存储已实现了最重要的方法:一种设置存储数据的方式,一种查询基于SimpleQueryEngine的数据的方式。也建立了索引机制,以便可以通过其身份快速返回一个条目,若是想要的话。
// in our declare from aboveget: function(id){return this.index[id];},getIdentity: function(object){return object[this.idProperty];}
这两个方法允许直接的数据访问,而不必经过查询,允许用户对一个给定的对象获得适当的唯一身份。若是存储的目的是(基本上)只读的,这都是需要在存储中定义的。

添加写入功能
大多数存储不是只读的。通常用户会想修改现有的对象,从自定义存储中添加和删除对象。为此将加入三个新方法:putaddremove
// in our declare from aboveput: function(object, options){var id = options && options.id|| object[this.idProperty];this.index[id] = object;var data = this.data,idProperty = this.idProperty;for(var i = 0, l = data.length; i < l; i++){if(data[i][idProperty] == id){data[i] = object;return id;}}this.data.push(object);return id;},add: function(object, options){var id = options && options.id|| object[this.idProperty];if(this.index[id]){throw new Error("Object already exists");}return this.put(object, options);},remove: function(id){delete this.index[id];for(var i = 0, l = this.data.length; i < l; i++){if(this.data[i][this.idProperty] == id){this.data.splice(i, 1);return;}}}
每次使用put方法修改一个对象,每次创建一个新对象并加入到存储中使用add方法,每次删除存储的一个对象使用remove方法。put方法是主要的,当改变一个对象时可以使用它,以便做类似更新的操作时,存储可以管理需要管理的事情。这里putadd的实现唯一不同的是add方法确保对象在存储中先前不存在。
最后的实现
这是最后的存储:
define(["dojo/store/util/QueryResults", "dojo/_base/declare", "dojo/store/util/SimpleQueryEngine"],function(QueryResults, declare, SimpleQueryEngine){// Declare the initial storereturn declare(null, {data: [],index: {},idProperty: "id",queryEngine: SimpleQueryEngine,constructor: function(options){lang.mixin(this, options || {});this.setData(this.data || []);},get: function(id){return this.index[id];},getIdentity: function(object){return object[this.idProperty];},put: function(object, options){var id = options && options.id|| object[this.idProperty];this.index[id] = object;var data = this.data,idProperty = this.idProperty;for(var i = 0, l = data.length; i < l; i++){if(data[i][idProperty] == id){data[i] = object;return id;}}this.data.push(object);return id;},add: function(object, options){var id = options && options.id|| object[this.idProperty];if(this.index[id]){throw new Error("Object already exists");}return this.put(object, options);},remove: function(id){delete this.index[id];for(var i = 0, l = this.data.length; i < l; i++){if(this.data[i][this.idProperty] == id){this.data.splice(i, 1);return;}}},query: function(query, options){return QueryResults((this.queryEngine(query, options))(this.data));},setData: function(data){this.data = data;// index our datathis.index = {};for(var i = 0, l = data.length; i < l; i++){var object = data[i];this.index[object[this.idProperty]] = object;}}});});
原创粉丝点击