前几年做个社区的时候,一哥们用它做了后台,导航、Grid都很方便,他都搭好环境了,也就没有仔细研究了。最近自己想做一个项目,验证一下ExtJS。只是做了三个样例的验证:
1. 登录对话框
2. 基于Accordion Layout的首页
3. Grid分页
前面两个都比较顺利了,第三个Grid遇到了一个大家经常碰到的问题:Server回来数据,但Grid一直没有显示。这个问题纠结了我一下午。网上搜索了一大堆,这个问题比较常见。现在总结一下可能的原因有:
1. 返回的数据格式不正确
2. 定义的Ext.data.Model不正确
3. 并未按照ExtJS规范使用远程数据源
前面两个问题一般都是编码问题,这个简单的Human Error我们就不讨论了。第三个问题是我碰到的,也比较有意义,就跟大家探讨一番。我用的ExtJS版本是ext-4.0.7-gpl,新版本上有些类已经改变了名字。
我们知道,要构造一个Ext.grid.Panel需要一个Ext.data.Store。Ext.data.Store类封装了客户端的数据模型(Ext.data.Model),它通过Ext.data.proxy.Proxy来加载数据,Store自身也还提供了排序、过滤、查询数据的功能。我们看个示例:
Ext.define('User', { extend: 'Ext.data.Model', fields: [ {name: 'firstName', type: 'string'}, {name: 'lastName', type: 'string'}, {name: 'age', type: 'int'}, {name: 'eyeColor', type: 'string'} ] }); var store = Ext.create('Ext.data.Store', { autoLoad: true, model: "User", proxy: { type: 'ajax', url : 'users.json', reader: { type: 'json', root: 'users' } } });
前面首先定义了一个数据模型User,下面创建一个Store,通过model指定数据模型(我们也可以在创建Store时通过设置data属性来指定数据模型),通过proxy指定了数据访问代理。Ext.data.proxy.Proxy的type指定了Proxy的类型,reader指定了数据解析器,用来解析服务器端返回的数据。
Proxy的类型可以分为两大类:Client和Server。
Client | LocalStorageProxy - saves its data to localStorage if the browser supports it | type: 'localstorage' |
Client | SessionStorageProxy - saves its data to sessionStorage if the browsers supports it | type: 'sessionstorage' |
Client | MemoryProxy - holds data in memory only, any data is lost when the page is refreshed | type: 'memory' |
Server | Ajax - sends requests to a server on the same domain | type: 'ajax' |
Server | JsonP - uses JSON-P to send requests to a server on a different domain | type: 'jsonp' |
Server | Direct - uses Ext.direct.Manager to send requests | type: 'direct' |
Ext.data.reader.Reader比较简单,经常使用的两种JSON和XML。
我的Grid没有显示数据的原因在于使用了跨域的数据来源。如果你不是跟我类似的问题,也记得坚持Proxy是否配置正确。从上面Proxy的类型来看,跨域的访问只有选择JSONP了,我开始错误的使用了Ajax。JSON和JSONP有啥区别呢?这有篇文章介绍【注1】,JSONP的出现主要是解决Ajax跨域访问无权限的问题,JS具有跨域的能力。Ajax和JSONP的本质区别在于,Ajax的核心是通过XmlHttpRequest获取非本页内容,而JSONP的核心则是动态添加添加<script>标签来调用服务器提供的js脚本。
真正使用JSONP需要一个回调函数和<script>标签的,但ExtJS和jQuery都做了封装,让你可以像Ajax一样在客户端使用。服务器端并不能直接使用,对于使用JSONP类型的Proxy,Server端需要给JSON数据添加上括号('(',')')。
完整的Demo代码:
JS:
Ext.onReady(function(){ var itemsPerPage = 2; // Set up a model to use in our Store Ext.define('User', { extend: 'Ext.data.Model', fields: [ {name: 'email', type: 'string'}, {name: 'name', type: 'string'}, {name: 'phone', type: 'int'} ] }); var store = Ext.create('Ext.data.Store', { id:'simpsonsStore', autoLoad: false, model: 'User', pageSize: itemsPerPage, // items per page proxy: { type: 'jsonp', url: 'http://127.0.0.1:8080/services//servlet/ExtJSPageTest', // url that will load data with respect to start and limit params reader: { type: 'json', root: 'items', totalProperty: 'total', failure : function() { Ext.Msg.alert("数据加载失败!"); } } } }); // specify segment of data you want to load using params store.load({ params:{ start:0, limit: itemsPerPage }, callback: function(records, operation, success) { //the operation object contains all of the details of the load operation console.log(records); } }); Ext.create('Ext.grid.Panel', { title: 'Simpsons', store: store, columns: [ {header: 'Email', dataIndex: 'email'}, {header: 'Name', dataIndex: 'name'}, {header: 'Phone', dataIndex: 'phone'} ], width: 400, height: 125, loadMask: { msg: '正在加载数据,请稍侯……' }, dockedItems: [{ xtype: 'pagingtoolbar', store: store, // same store GridPanel is using dock: 'bottom', displayInfo: true }], renderTo: Ext.getBody() }); });
Java Server:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int start = Integer.parseInt(request.getParameter("start")); int limit = Integer.parseInt(request.getParameter("limit")); String cb = request.getParameter("callback"); List userList = new ArrayList(); User user1 = new User("Lisa", "[email protected]", "555-111-1224"); User user2 = new User("Lisa", "[email protected]", "555-111-1224"); User user3 = new User("Bart", "[email protected]", "555-111-1224"); User user4 = new User("Lisa", "[email protected]", "555-111-1224"); userList.add(user1); userList.add(user2); userList.add(user3); userList.add(user4); List currentList = new ArrayList(); if (start + limit <= userList.size()) { for (int i = start; i < start + limit; i++) { currentList.add(userList.get(i)); System.out.println(userList.get(i)); } } else { for (int i = start; i < userList.size(); i++) { currentList.add(userList.get(i)); System.out.println(userList.get(i)); } } boolean jsonP = false; if (cb != null) { jsonP = true; response.setContentType("text/javascript"); } else { response.setContentType("application/x-json"); } if (jsonP) { response.getWriter().write(cb + "("); } JSONArray jsonList = JSONArray.fromObject(currentList); response.setCharacterEncoding("utf-8"); response.getWriter().print( "{\"total\":" + limit + ",\"items\":" + jsonList + "}"); if (jsonP) { response.getWriter().write(");"); } }
注1:http://blog.jobbole.com/18012/