dataServices
数据服务的管理器;首先看下具体的代码
//数据服务 dataServices = st.factory({ name: "dataServices", proto: { //通过操作方法;type:操作类型; op:操作参数 operate: function(type, op) {…………}, //查询方法;op:操作参数 search: function(op) {…………}, //更新方法;op:操作参数 update: function(op) {……} }, base: { //查询接口 search: function(op) {}, //更新接口 update: function(op) {}, //通用初始化参数接口 initOptions : function(op){} } })
使用factory创建,加入了三个方法operate,search,update。使用的时候,直接通过这三个方法来操作具体注册的数据服务(在op中设定dsType)。
同时base定义了三个数据服务的三个基类接口,search,update,initOptions;
op:既设置的参数(options,在smartjs中统一做op的简写),只有六个固定参数。其他都是都是由具体的数据服务定义;
op = { //数据服务类型 dsType : str, //过滤参数 param :obj, //过滤器 fitler : fn | obj //更新的数据 data :obj, //成功以后执行的方法 success : success, //失败以后执行的方法 error : error };
其中,param与filter可以根据不同的数据服务类型来区别使用,比如只是客户端的可以使用filter,服务端的可以使用params;
注意:虽然op中有了success,error,但添加dataService的时候,尽量使用promise的处理。
代码示例
通过dataServices的add(factory内置)的方法来注册数据服务;实现了search和update两个接口。另外一个initOptions是需要对op初始化的是重写
首先注册一个模拟后台异步数据服务 - server;只接受params来过滤数据:
var dataServices = st.dataServices, dataManager = st.dataManager, _db = [], _cache = []; //将params解析成过滤方法 function buildFilterByParams(params) { if (params) { return function(item) { var check = true; $.each(params, function(name, value) { if (item[name] !== value) { check = false; return check; } }) return check; } } } //取对象数据,测试使用array只取第一条 function getData(data) { return $.isArray(data) ? data[0] : data; } function buildFitler(filter) { if (filter && typeof filter === 'object') { return buildFilterByParams(filter); } return filter; } //模拟服务端异步返回数据,只接受params dataServices.add("server", { search: function(op) { //模拟异步查询 setTimeout(function() { var result, filter = op.filter; result = filter ? _db.filter(filter) : _db; op.success && op.success(result); }, 100); }, update: function(op) { //模拟异步更新 setTimeout(function() { var filter = op.filter, data = getData(op.data); if (filter) { //测试使用,只更新第一条匹配数据 $.each(_db, function(i, item) { if (filter(item)) { _db[i] = data; return false; } }) } else { _db = op.data || []; } op.success && op.success(op.data); }, 100); }, initOptions: function(op) { //初始化设置参数将params编译成filter过滤方法 op.filter = buildFilterByParams(op.params); } });
然后在注册一个模拟客户端取数据的缓存服务服务 - cache,使用filter进行过滤。
//模拟客户端本地存储 dataServices.add("cache", { search: function(op) { var result, filter = op.filter; result = filter ? _cache.filter(filter) : _cache; op.success && op.success(result); }, update: function(op) { var filter = op.filter, data = getData(op.data); if (filter) { //测试使用,只更新第一条匹配数据 $.each(_cache, function(i, item) { if (filter(item)) { _cache[i] = data; return false; } }) } else { _cache = op.data || []; } op.success && op.success(op.data); }, initOptions: function(op) { //生成fitler,当filter为obj类型时,编译成fn op.filter = buildFitler(op.filter); } });
看一下server的测试用例,直接使用dataServices对象进行操作,使用dsType来设置具体的数据类型;
describe('dataServices Test', function() { it("update", function(endTest) { //更新server的数据 dataServices.update({ dsType: 'server', data: [{ name: 'user1', age: 20 }, { name: 'user2', age: 30 }], success: function(result) { expect(_db.length).toBe(2); endTest(); } }); }) it("search", function(endTest) { //重新server的数据 dataServices.search({ dsType: 'server', params: { name: 'user1' }, success: function(result) { expect(result.length).toBe(1); expect(result[0].age).toBe(20); endTest() } }); }) });
dataManager
数据管理器,同样使用factory构建,但是选用的类型为'class',需动态初始化;扩展创建dm的方法-ceate和生成filter的方法-buildFilter;
另外在基类中,klassInit,get,set,onHandler,addHandler,fireHandler为实现方法;其他的都是接口,需要根据具体的数据管理进行实现;
//数据管理器 dataManager = st.factory({ name: "dataManager", type: "class", proto: { //创建dm方法 create: function(type, op) {}, //生成fitler方法 buildFilter: function(filter,conf) {} }, base: { klassInit: function(op) {}, //dm初始化方法 init: function(op) {}, //获取数据 get: function(conf) {}, //设置数据 set: function(conf) {}, //注册方法到事件委托,handler委托名称:get,set,trigger onHandler: function(handler, fnName, fn, priority, mode) {}, //添加事件委托 addHandler: function() {}, //执行事件委托 fireHandler: function(name, args) {}, //dm内置查询 innerSearch: function(op) {}, //dm内置更新 innerUpdate: function(op) {}, //检查数据是否为空 checkEmpty: function(data, conf) {}, //验证方法 validate: function() {}, //清空方法 clear: function() {}, //初始化数据服务配置方法 setDataSerive : function(config){} } });
添加datamanger示例
添加一个简单的table类型的数据管理,(注只做测试演示,与真正的datamanger-table不是同一个)
//添加一个简单的table类型的数据管理 dataManager.add("Table", { init: function() { this._data = []; }, //dm内置查询 innerSearch: function(conf) { var filter = conf ? buildFitler(conf.filter) : null; return filter ? this._data.filter(filter) : this._data; }, //dm内置更新 innerUpdate: function(conf) { var isUpdate, _data = this._data, data = conf.data, updateData, filter; conf && (filter = buildFitler(conf.filter)); if (filter) { updateData = getData(data); //筛选数据 _data.forEach(function(item, i) { if (filter(item)) { _data[i] = updateData; isUpdate = true; return false; } }) isUpdate || _data.push(updateData); } else { this._data = data || []; } return data; }, //判断数据是否为空 checkEmpty: function(data, conf) { return data === undefined || data.length === 0; }, //清空数据 clear: function() { this._data = []; } });
在来看一下怎么使用这个dm,下面列子中使用了内置的查询和更新;
//创建一个tabel的manager var dm1 = dataManager.create("Table"); it("update", function() { dm1.innerUpdate({ data: [{ name: 'user1', age: 10 }] }); expect(dm1._data.length).toBe(1); expect(dm1._data[0].name).toBe('user1'); }) it("search", function() { var result = dm1.innerSearch(); expect(result.length).toBe(1); expect(result[0].name).toBe('user1'); }) it("update by filter", function() { //找不到匹配的数据,则插入新数据 dm1.innerUpdate({ data: { name: 'user3', age: 10 }, //方法过滤器 filter: function(user) { return user.name == 'user3'; } }); expect(dm1._data.length).toBe(2); expect(dm1._data[1].name).toBe('user3'); //更新数据 dm1.innerUpdate({ data: { name: 'user3', age: 40 }, //方法过滤器 filter: function(user) { return user.name == 'user3'; } }); expect(dm1._data.length).toBe(2); expect(dm1._data[1].age).toBe(40); }) it("search by filter", function() { var result = dm1.innerSearch({ //方法过滤器 filter: function(user) { return user.name == 'user3'; } }); expect(result.length).toBe(1); expect(result[0].age).toBe(40); }) it("search by params", function() { var result = dm1.innerSearch({ //参数过滤器 filter: { name: 'user3' } }); expect(result.length).toBe(1); expect(result[0].age).toBe(40); })
在结合dataService来看个查询的例子,在这里使用get操作,而不是innerSearch;get和set这两个动作都会进入数据管理流程,策略才会生效。而innerSearch和innerUpdate则是只查询dm内部。
在这个例子中,get动作会首先在dm内部查询,找不到数据,在会进入ds查询,然后将ds查询的数据同步到dm中。(详细的流程见dataManager介绍)
it("get from ds and update", function(endTest) { dm1.clear(); //首先会在dm内部查询,找不到数据然后在到server上查询 dm1.get({ //设置数据服务为server dataServices: { dsType: 'server' }, success: function(result) { expect(result).toBeDefined(); expect(result[0].name).toBe('user1'); expect(dm1._data[0].name).toBe('user1'); endTest(); } }) }) it("get from ds and no update", function(endTest) { dm1.clear(); dm1.get({ //设置查询不更新 update: false, dataServices: { dsType: 'server' }, success: function(result) { expect(dm1._data.length).toBe(0); endTest(); } }) })
在看一个set的例子:
it("set to ds", function(endTest) { //更新到ds dm1.set({ data: [{ name: "userUpdate" }], dataServices: { dsType: 'server' }, success: function(result) { expect(_db.length).toBe(1); expect(_db[0].name).toBe('userUpdate'); endTest(); } }) }) it("set to ds by params", function(endTest) { //根据条件更新到ds,条件同时在dm和ds中生效 dm1.set({ data: [{ name: "userUpdate" }], params: { id: 1 }, dataServices: { dsType: 'server' }, success: function(result) { expect(_db.length).toBe(2); expect(_db[0].name).toBe('userUpdate'); endTest(); } }) })
下篇详细介绍策略参数的api和场景分析