Ext4核心组件Grid的变化及学习(4):grid与服务端使用direct进行数据交互
大部分grid的数据都是要提交到后台服务端的,现在一种比较好用的方式就是direct。今天介绍一下这种交互方式的实现。
例子如下:
sqlserver数据库中两张表通过外键关联:
文件信息FileInfo
文件类别FileClass
需要做出的效果如下:
显示
下拉框修改
修改之后自动上传
1、服务端添加Ext.Direct.dll和Newtonsoft.Json.dll的引用
2、处理文件数据的类FileAction.cs,注意最后一个Update方法
using System; using System.Collections.Generic; using Ext.Direct; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Web.SessionState; using DBUtility; using System.Data; using System.Text; using System.Data.SqlClient; using Persister; /// <summary> ///FileAction 的摘要说明 /// </summary> [DirectAction] public class FileAction { [DirectMethod] public FileInfoSerializer Load(Dictionary<string,object> arg) { object[] sorters = (object[])arg["sort"]; Dictionary<string, object> sorter = (Dictionary<string, object>)sorters[0]; FileInfoCollection coll = new FileInfoCollection(); coll.OrderField = sorter["property"].ToString(); coll.OrderDirection = sorter["direction"].ToString().ToLower() == "desc" ? FileInfoCollection.Direction.DESC : FileInfoCollection.Direction.ASC; return new FileInfoSerializer(coll); } [DirectMethod] public FileInfoSerializer PagingLoad(string order, string direction, long start, long limit) { FileInfoCollection coll = new FileInfoCollection(); coll.OrderField = order; coll.OrderDirection = direction.ToLower() == "desc" ? FileInfoCollection.Direction.DESC : FileInfoCollection.Direction.ASC; return new FileInfoSerializer(coll, Convert.ToInt32(start), Convert.ToInt32(limit)); } [DirectMethod] [ParseAsJson] public JObject Destroy(JObject o) { JValue val = (JValue)o["data"]; FileInfo c = new FileInfo(Convert.ToString(val.Value)); c.Destroy(); return new JObject( new JProperty("data", new JArray()) ); } [DirectMethod] [ParseAsJson] public FileInfo Create(JObject o) { FileInfo c = JsonConvert.DeserializeObject<FileInfo>(o.ToString()); c.Save(); return c; } [DirectMethod] [ParseAsJson] public JObject Update(JObject o) { FileInfo c = JsonConvert.DeserializeObject<FileInfo>(o.ToString()); JProperty success = new JProperty("success","false"); try { c.Save(); success = new JProperty("success", "true"); } catch { } return new JObject( success, new JProperty("id",c.Id), new JProperty("title",c.Title) ); } }
3、统一调用接口Api.ashx
<%@ WebHandler Language="C#" Class="Api" %> using System; using System.Collections.Generic; using System.Linq; using System.Web.SessionState; using System.Web; using Ext.Direct; public class Api : DirectHandler, IRequiresSessionState { public override string ProviderName { get { return "Ext.app.USER_API"; } } public override string Namespace { get { return "MyApp"; } } protected override void ConfigureProvider(DirectProvider provider) { this.Configure(provider, new object[] { new CheckLogin(),new TreeAction(),new FileAction() }); } }
服务器端不是重点,主要看一下update方法返回的数据,之后会找时间系统讲一下服务器端direct,重点看下js文件:
因为是测试,所以就临时把fileClass建好了进行数据调用,正式使用的时候和file表一样要从服务端取,这个调用方式有很多种,之后会开篇帖子就这个进行深入讨论,不是今天的重点。
4、定义FileModel,里面的时间数据要进行转换
Ext.define('File', { extend: 'Ext.data.Model', fields: [ 'id','title', 'classId', {name: 'datetimeCreated',type: 'date',dateFormat: 'c'}, 'docAbstract','typeId' ] });
5、定义fileClass(临时内存数据,之后会开篇帖子就这个进行深入讨论)
//定义fileClassModel Ext.define('FileClass', { extend: 'Ext.data.Model', fields: ['id', 'name'] }); //创建fileClass下拉选择框的store new Ext.data.Store({ model: 'FileClass', storeId:'fileClassStore', proxy: {type: 'memory'}, data : [ {"id":"1e887509-dd86-49f4-9d63-4e8640409758", "name":".Net"}, {"id":"3384fcb7-3106-4fdd-ab65-31152eef9edb", "name":"java"} ] });
6、创建filesStore和filesStore的callback function,如果不需要callback,也可以直接在api里面写调用的函数。具体为什么这么写请参考我写的这篇帖子
Ext.data.DirectStore中DirectAction的回调函数问题
//创建filesStore的callback function var callback = function(response, e) { if(response.success=='true'){ Ext.getStore('filesStore').getById(response.id).commit() Ext.MessageBox.alert('callback',response.title+'已经更新') } else{ Ext.getStore('filesStore').getById(response.id).reject() Ext.MessageBox.alert('callback',response.title+'更新失败') }}; //创建filesStore Ext.create('Ext.data.DirectStore', { model: 'File', storeId: 'filesStore', autoLoad: true, autoSync: true, remoteSort: true, api: { create: MyApp.FileAction.Create, read: MyApp.FileAction.Load, update: function(rec){MyApp.FileAction.Update(rec,callback);}, destroy: MyApp.FileAction.Destroy }, writer: new Ext.data.JsonWriter({ encode: false, writeAllFields: true }), idProperty: 'id', totalProperty: 'total', root: 'data', sorters: [{ property: 'datetimeCreated', direction: 'DESC' }] });
有几个需要注意的config:
a)autoSync: true 每修改好一行就直接提交到服务端,我一般都选这种,可以节省客户的时间,不需要很长时间的等待。
b)storeId: 'filesStore' 会把这个store注册到ext的storemanager,之后可以使用Ext.getStore('filesStore')访问
接下来就是grid的设置:
//定义MainFileGrid Ext.define('App.MainFileGrid', { extend: 'Ext.grid.Panel', store: 'filesStore', alias: 'widget.MainFileGrid', columns: [ { header: '文档', dataIndex: 'title', flex: 1, renderer: function(value, o, record) { return Ext.String.format('<div class="topic"><b>{0}</b><span class="author">{1}</span></div>', value, record.get('author')); }, editor: { // defaults to textfield if no xtype is supplied allowBlank: false } }, { header: '类别', flex: 1, dataIndex: 'classId', width: 100, renderer: function(value) { return Ext.getStore('fileClassStore').getById(value).data.name; }, field: { xtype: 'combobox', store: 'fileClassStore', queryMode: 'local', //提前加载数据,防止延迟 autoRender: true, displayField: 'name', valueField: 'id', editable: false, allowBlank: false } }, { header: '文件类型', flex: 1, dataIndex: 'typeId', width: 70, align: 'right', editor: { // defaults to textfield if no xtype is supplied allowBlank: false } }, { header: '时间', dataIndex: 'datetimeCreated', renderer : Ext.util.Format.dateRenderer('Y年m月d日'), width: 150, field: { xtype: 'datefield', allowBlank: false, format: 'm/d/Y', minValue: '01/01/2006', minText: 'Cannot have a start date before the company existed!', maxValue: Ext.Date.format(new Date(), 'm/d/Y') } } ], plugins: [Ext.create('Ext.grid.plugin.RowEditing', { autoCancel: false, clicksToEdit: 2 })], initComponent: function(){ Ext.apply(this, { dockedItems: [{ xtype: 'toolbar', cls: 'x-docked-noborder-top', items: [{ text: '新文件', iconCls: 'icon-new-topic', handler: function(){ alert('Not implemented'); } }, '-', { text: '显示详细信息', iconCls: 'icon-preview', enableToggle: true, pressed: true, scope: this, toggleHandler: this.onPreviewChange }] },{ dock: 'bottom', xtype: 'pagingtoolbar', store: 'filesStore', displayInfo: true, displayMsg: '显示 {0} - {1} 共 {2} 文档', emptyMsg: '无文档' }], selModel: Ext.create('Ext.selection.RowModel', { mode: 'SINGLE', listeners: { scope: this, select: this.onSelect } }) }); this.callParent(); }, onSelect: function(selModel, rec){ this.ownerCt.onSelect(rec); }, loadForum: function(id){ var store = this.store; store.getProxy().extraParams.forumId = id; store.loadPage(1); }, onPreviewChange: function(btn, pressed){ this.ownerCt.togglePreview(pressed); }, onSummaryChange: function(btn, pressed){ this.getView().getPlugin('preview').toggleExpanded(pressed); } });
需要注意的是以下几点:
a)“类别”列自定义显示,根据类别id到类别store中找name
renderer: function(value) {
return Ext.getStore('fileClassStore').getById(value).data.name;
},如果不设置就会直接显示id,效果对比如下(其中文件类型就是没有自定义的,显示原始id)
b)“类别”列的修改模式采用下拉框,只允许选择不允许输入,不能为空,而且建议提前渲染,不然第一次点击会出现空值
field: { xtype: 'combobox', store: 'fileClassStore', queryMode: 'local', //提前加载数据,防止延迟 autoRender: true, displayField: 'name', valueField: 'id', editable: false, allowBlank: false }