Ext JS - Ext JS 4 MVC 模式 - 以一个 grid 来演示 MVC 之间的联系和作用,以及涉及到的部分函数、事件参数的应用
文件结构:
文件加载顺序:
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html> <head> <base href="<%=basePath%>" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="ext-4/bootstrap.js"></script> <script type="text/javascript" src="ext-4/locale/ext-lang-zh_CN.js"></script> <link type="text/css" rel="stylesheet" href="ext-4/resources/css/ext-all.css" /> <script type="text/javascript" src="extjs/app.js"></script> <title>Ext JS</title> <style type="text/css"> .x-grid-cell.greencard { background-color: #B6FFA8; } </style> </head> <body> </body> </html>
app.js
Ext.Loader.setConfig({ enabled: true }); Ext.Loader.setPath({ 'Ext.ux': 'ext-4/ux', 'Ext': 'ext-4' }); Ext.application({ /** * 全局命名空间,所有ExtJS4应用都需要有一个全局命名空间,以让所有应用中的类安放到其中 * 自动创建一个全局变"MyApp",并自动注册命名空间"MyApp"到Ext.Loader */ name: 'MyApp', appFolder: 'app', // 存放Application涉及的文件路径 controllers: ['userCtrl'], // 引用Controller /** * 应用从这里开始 */ launch: function () { /** * 初始化QuickTips就是为了使带有data-qtip属性的html标签能够在鼠标移上去的时候显示其内容,作用类似于HTML标签的title的功能 */ Ext.QuickTips.init(); /** * 创建一个 Viewport,在其中添加子项 userView */ Ext.create('Ext.container.Viewport', { layout: 'fit', // userView充满整个Viewport autoScroll: false, items: { xtype: 'userView' // alias: 'widget.userView' } }); } });
userCtrl.js
Ext.define('MyApp.controller.userCtrl', { extend: 'Ext.app.Controller', // refs的作用在于在Controller可以直接getUserView()可以直接获取到userView refs: [{ ref: 'userView', selector: 'userView' } ], // Controller下面的views数组里添加的对应的是View的别名(widget),这样控制器就可以对视图上的操作进行监控! // 同时views配置等同于requires: ['MyApp.view.userView', 'MyApp.view.editUser'], // 同理:stores,models配置同理也是等同于requires,动态加载组件! views: ['userView', 'editUserView'], stores: ['userStore'], // store会被自动加载到页面并赋予一个storeId,这让视图中使用store变的容易 models: ['userModel'], /** * 通过index.jsp查看应用时,userCtrl会被自动加载 * 因为在app.js的Application中增加了引用,并且userCtrl的init方法会在Application的launch方法之前调用 * init方法用来设置如何和View交互,通常都使用Controller的一个方法control,control方法使得监听View的事件变的容易 */ init: function () { // 全局变量userCtrl,当前Controller(this)赋值给userCtrl,则可以在View或者Store中,利用变量userCtrl进行操作! // 前提是在Controller里的Views和Stores配置里,将需要操作的项添加进去! userCtrl = this; /** * 使用this.control给视图设置监听器,这个controll方法,使用最新的组件查询引擎(ComponentQuery)可以快速方便的找到页面上的组件 */ this.control({ 'userView': { itemdblclick: this.editUser // 想了解可用参数,可以在Ext的API里搜索关键字itemdblclick查阅View的双击事件!! }, 'editUserView button[action=save]': { // xytpe是editUserView的组件下的所有action属性是save的按钮 click: this.updateUser // View下的Button,可以去查阅Button下面的Events的click单击事件 } }); }, /** * itemdblclick(this, record, item, index, e, eOpts) * * grid: Ext.view.View * 当前View视图 * record: Ext.data.Model * The record that belongs to the item (当前行的数据记录) * itme: HTMLElement * The item's element (HTML元素) * index: Number * The item's index (序号) * e: Ext.EventObject * The raw event object (事件对象) * eOpts: Object * The options object passed to Ext.util.Observable.addListener */ // !!!因为在此Controller中, this对象指代当前控制器userCtrl, 所以itemdblclick函数中的参数, 用grid来代替this // 替换 editUser: function (this, record, item, index, e)中的参数名称, this 替换为 grid editUser: function (grid, record, item, index, e) { /** * arguments: js参数的特殊性,一般是有一个内置的参数数组arguments存放参数 */ var argsLength = arguments.length; // 5 console.log('argsLength: ' + argsLength); console.log('=====打印arguments start====='); for(var i in arguments){ console.log('第 '+i+' 个参数: '); console.log(arguments[i]); } console.log('=====打印arguments end====='); /** * 参数: grid * getSelectionModel(): Ext.selection.Model -> Gets the selection model for this view * 所以是当前的View来调用getSelectionModel方法 * 此处的grid就是View的this(鉴于此文件下this已指代为Controller userCtrl对象,所以此处grid代替View的this) * 方法调用格式: viewObj.getSelectionModel(); */ console.log('=====grid====='); console.log(grid); var selectionModel = grid.getSelectionModel(); // 选择模式 var rec = selectionModel.getLastSelected();// 即record var name = rec.get('name'); console.log('grid.getSelectionModel().getLastSelected().get(\'name\'): ' + name); /** * 参数: record * record下面的属性: data,events,hasListeners,id,index,internalId,modified,phantom,raw,store,stores * record下面的属性下面还有属性,可以一层一层的引用来获取数据! * 例如: record.data.name,或者record.data['name'],这样就获得了当前grid双击行的name值 */ console.log('record.get(\'name\'): ' + record.get('name')); console.log('record.data.name: ' + record.data.name); console.log('record.data[\'name\']: ' + record.data['name']); console.log('record.raw.name: ' + record.raw.name); /** * refs配置里的view,自动赋予了ctrl的getter方法,this.getXxx()既可以直接获得! */ var $grid = this.getUserView(); // 获取到的$grid等同于参数中的grid! var $name = $grid.getSelectionModel().getLastSelected().get('name'); console.log('this.getUserView().getSelectionModel().getLastSelected().get(\'name\'): ' + $name); /** * refs配置的东西,则可以直接this.getUserView(this此处是userCtrl) * views和stores配置的东西,获得的方式则是this.getXxxView()或者this.getXxxStore() */ var $userView = this.getUserView(); console.log('this.getUserView(): '); console.log($userView); var $editUserView = this.getEditUserViewView(); console.log('this.getEditUserViewView(): '); console.log($editUserView); var userStore = this.getUserView().store; console.log('this.getUserView().store: '); console.log(userStore); var $store = this.getUserStoreStore(); console.log('this.getUserStoreStore(): '); console.log($store); /** * 双击事件处理程序在这里.. * 通过别名获得编辑用户信息的view,然后用down方法,向下寻找form组件,将grid选中行的数(record)据,loadRecord加载到form的文本框中! */ var view = Ext.widget('editUserView'); // 这个方法等同于Ext.create('widget.editUserView') /** * 区分下Ext.widget('#alias'),Ext.getCmp('#id') * Ext.widget('#alias'): 参数是组件的alias(别名),只要组件存在,就能获得到(所以这里需要用这个方式来获取编辑用户的View) * Ext.getCmp('#id'): 获得一个Ext组件(一个已经在页面中初始化了的Component或其子类的对象!!),唯一参数是组件的id */ console.log('Ext.widget(\'editUserView\'): '); console.log(view); /** * loadRecord() 圈起来,重点!! * 参数: record 选中行数据 */ view.down('form').loadRecord(record); }, /** * 点击保存按钮事件 */ updateUser: function (button) { console.log('button: '); console.log(button); var win = button.up('window'); // up()向上寻找window组件 var form = win.down('form'); // down()向下寻找window下面的form组件 var record = form.getRecord(); // 从form得到record,和editUser里面的record是同一个!! var values = form.getValues(); // 从form得到values(即可获取更新后的数据) record.set(values); // 给record赋值,这样修改后的数据就会显示在视图上了 win.close(); this.getUserStoreStore().sync(); } });
userModel.js
Ext.define('MyApp.model.userModel', { extend: 'Ext.data.Model', fields: [{ name: 'name', type: 'string' }, { name: 'email', type: 'string' }, { name: 'comments', type: 'string' }] });
userView.js
// 自定义grid选择模式 var selModel = Ext.create('Ext.selection.CheckboxModel', { checkOnly: false // 设置为true,只能点击复选框选中,默认情况下,单击数据行或者点击复选框均可! }); Ext.define('MyApp.view.userView', { extend: 'Ext.grid.Panel', alias: 'widget.userView', selModel: selModel, // 一个selection model实例或配置对象,在后一种情况下,selType配置选项确定此配置应用于哪种类型的选择模型。 // selType: 'rowmodel', // 所使用到的选择模型的xtype,默认为'rowmodel' renderTo: Ext.getBody(), title: 'All Users', store: 'userStore', // initComponent函数里,所有组件都是this.item的形式添加 initComponent: function () { /*this.store = { fields: ['name', 'email'], data: [{ name: 'Ed', email: 'ed@sencha.com' }, { name: 'Tommy', email: 'tommy@sencha.com' } ] };*/ this.columns = [{ text: 'Name', dataIndex: 'name', flex: 1, renderer: function(value, metaData, record, rowIndex, colIndex, store, view){ console.log('value:' + value + ', rowIndex:' + rowIndex + ', ' + ', colIndex:' + colIndex); console.log('metaData:'); console.log(metaData); console.log('record:'); console.log(record); console.log(record.get('name')); console.log(record.data.name); console.log(record.data['name']); console.log(record.raw.name); console.log(record.raw['name']); console.log('view:'); console.log(view); console.log('store'); console.log(store); return value; } }, { text: 'Email', dataIndex: 'email', flex: 1, renderer: function(value, metaData, record, rowIndex, colIndex, store, view){ metaData.tdCls = 'greencard'; return value; } }, { text: 'Comments', dataIndex: 'comments', flex: 1, renderer: function(value, metaData, record, rowIndex, colIndex, store, view){ console.log('..............'); console.log('view.getStore(): '); console.log(view.getStore()); console.log('..............'); return value; } } ]; this.dockedItems = [{ xtype: 'pagingtoolbar',// 分页工具栏 store: 'userStore', dock: 'bottom',// 停靠位置: 底部 displayInfo: true, // displayMsg: '显示 {0} - {1} 条,共 {2} 条',// displayInfo: true,这里不添加displayMsg也可以 emptyMsg: '没有数据' }]; // initComponent里面必加这句!! this.callParent(arguments); } });
editUserView.js
Ext.define('MyApp.view.editUserView', { extend : 'Ext.window.Window',// 继承自window,弹出窗口对话框 alias : 'widget.editUserView', id: 'editUserView', title : 'Edit User', layout : 'fit',// 布局: 填充 autoShow : true, /** * 用到初始化组件initComponent的话,都是this.items */ initComponent : function() { // window下子项form表单 this.items = [ { xtype : 'form', // 表单里2个文本框xtype: textfield,name属性值要和被编辑view的columns的dataIndex值一致,可以接收双击grid之后,传递过来的值! items : [ { xtype : 'textfield', name : 'name', fieldLabel : 'name' }, { xtype : 'textfield', name : 'email', fieldLabel : 'Email' } ] } ]; // window里面,设置按钮配置buttons数组,分配2个按钮 this.buttons = [ { text : 'Save', action : 'save' }, { text : 'Cancel', scope : this, handler : this.close } ]; this.callParent(arguments); } });
userStore.js
Ext.define('MyApp.store.userStore', { extend: 'Ext.data.Store', model: 'MyApp.model.userModel',// model的完整路径! autoLoad: true, proxy: { type: 'ajax', // url: 'data/users.json', api: { read: 'data/users.json',// 从users.json读取数据 update: 'data/updateUsers.json',// store数据发生变更(数据必须变化)会发送到updateUsers.json,返回updateUsers.json作为应答包 }, reader: { type: 'json', root: 'users', successProperty: 'success',// Defaults to: "success" totalProperty: 'total'// Defaults to: "total" } } // 内联数据 // data: [{ // name: 'Ed', // email: 'ed@sencha.com' // }, { // name: 'Tommy', // email: 'tommy@sencha.com' // } // ] });
users.json
{ "success": true, "total": 3, "users": [ { "id": 1, "name": "Ed", "email": "ed@sencha.com", "comments": "Edison" }, { "id": 2, "name": "Tommy", "email": "tommy@sencha.com", "comments": "Tom" }, { "id": 3, "name": "Red-Haired Shanks", "email": "shanks@sencha.com", "comments": "Shanks" } ] }
updateUsers.json
{ "success": true }
视图
itemdblclick 事件:
修改:
Save