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

 

posted @ 2017-10-25 10:53  你的笑忘书  阅读(350)  评论(0编辑  收藏  举报