博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
extjs-mvc结构实践(四):导航菜单与控制器模块联动
阅读量:6885 次
发布时间:2019-06-27

本文共 18795 字,大约阅读时间需要 62 分钟。

前面几篇文档,我们基本实现了一个静态的extjs页面,本篇开始,实现左侧导航树与右侧内容的联动,也就是点击导航菜单,加载对应模块页面和业务逻辑,实现js文件的按需加载。

业务需求是这样的:

左侧的treelist,当点击某个节点的时候,系统根据tree数据里配置的模块信息,加载这个模块,并且把模块对应的主页面显示在中间区域的tabpanel里。

改造主控制器:app/luter/controller/MainController.js

监听导航树的node点击事件,进行后续处理:

'navlist': {    'itemclick':function(el, record, opt){        //可以通过如下方式获取点击节点的数据。        var nodeData = record.node.data;        }}

完整的代码如下:

Ext.define('luter.controller.MainController', {    extend: 'Ext.app.Controller',    views: ['main.ViewPort'],    stores: ['NavTreeStore'],    init: function (application) {        var me = this;        this.control({            'viewport': {//监听viewport的初始化事件,可以做点其他事情在这里,如有必要,记得viewport定义里的alias么?                'beforerender': function () {                    console.log('viewport begin render at:' + new Date());                },                'afterrender': function () {                    console.log('viewport  render finished at:' + new Date());                },            },            'syscontentpanel': {                'afterrender': function (view) {                    console.log('syscontentpanel rendered at:' + new Date());                }            },            'navlist': {                'itemclick': function (el, record, opt) {                    var nodeData = record.node.data;//当前点击节点的数据                    var tabpanel = Ext.getCmp('systabpanel');//中间tabpanel                    var tabcount = tabpanel.items.getCount();//当前tabpanel已经打开几个tab了。                    var maxTabCount = 5;//最大打开的tab个数                    if (tabcount && tabcount > 5) {                        showFailMesg({                            title: '为了更好的使用,最多允许打开5个页面',                            msg: '您打开的页面过多,请关掉一些!'                        });                        return false;                    }                    if (nodeData.leaf) {//是打开新模块,否则是展开树节点                        var moduleID = nodeData.module_id;//找到控制器ID,定义在tree的数据里modole_id                        if (!moduleID || moduleID == '') {                            showFailMesg({                                title: '创建模块失败.',                                msg: '模块加载错误,模块id为空,创建模块失败'                            });                            return false;                        }                        console.log('to add module with id:' + moduleID);                        //开始加载控制器                        try {                           //尝试加载这个控制器,这个过程就是按需ajax加载js文件的过程。                          //如果这个模块被加载过,则不会重复加载。                            var module = luterapp.getController(moduleID);                        } catch (error) {                            showFailMesg({                                msg: '根据模块ID:' + moduleID + '创建模块失败。' +                                '
可能的原因 :
1、该模块当前没有实现.' + '
2、模块文件名称与模块名称不一致,请检查' + '
Error: ' + error + '' }); return false; } finally { } //判断模块是否加载下来,因为是ajax加载,所以还是判断一下比较好 if (!module) { showFailMesg({ msg: 'B:load module fail,the module object is null.' + '
maybe :the module is Not available now.' }); return false; } //加载到之后,默认去获取控制器里views:[]数组里的第一个作为主视图 var viewName = module.views[0]; console.log('will create a tab with view ,id:' + viewName); var view = module.getView(viewName); console.log('get the view el:' + view); if (!view) { showFailMesg({ msg: 'Sorry ,to get the module view fail...' }); return false; } //判断一下这个视图是不是已经加载到tabpanel里去了 var tabid = me.getTabId(moduleID); console.log('will create a tab with id:' + tabid); var notab = tabpanel.getComponent(tabid); var viewEL = view.create(); if (!viewEL) { showFailMesg({ msg: 'Sorry ,to get the module viewEL fail...' }); return false; } if (!notab && null == notab) {//不存在新建 //不管是啥,都放到一个panel里面。 notab = tabpanel.add(Ext.create('Ext.panel.Panel', { tooltip: nodeData.text + ':' + nodeData.qtip, id: tabid, // tab的唯一id title: nodeData.text, // tab的标题 layout: 'fit', // 填充布局,它不会让load进来的东西改变大小 border: false, // 无边框 closable: true, // 有关闭选项卡按钮 iconCls: nodeData.iconCls, listeners: { // 侦听tab页被激活里触发的动作 scope: this, destroy: function () { console.log("tab :" + tabid + ",has been destroyed") } }, items: [view.create()] })); //新建之后focus tabpanel.setActiveTab(notab); } else {//如果这个tab已经存在了,则focus到这个tab tabpanel.setActiveTab(notab); } } else { //如果leaf =false,则说明这不是一个最底层节点,是目录,展开。 console.log('tree node expand') } } } }); }, //这个方法从tab id里分离出控制器名称 getTabId: function (mid) { var winid = mid; var c = winid.split('.'); winid = c.pop(); return winid + '-tab'; }});

左侧菜单树对应的测试数据:app/testdata/menu.json

一般情况下,这个菜单数据是保存在后端的,通过权限判断加载用户可访问的资源形成树结构返回给前端使用。leaf标明了这是一个目录还是一个模块,module_id对应的是控制器的路径。 比如下面这个测试数据中。 "leaf": true, "module_id": "sys.UserController", 在app.js中,我们配置了appFolder:‘app/luter’, leaf标明了这是一个控制器模块,点击后会去触发控制器加载动作。 所以这个模块的实际路径(也就是js文件)就是:${appFolder}/controller/${module_id}.js 即:app/luter/controller/sys.UserController.js

[  {    "id": "111",    "text": "系统管理",    "href": null,    "leaf": false,    "iconCls": "fa fa-home",    "module_id": "no sign",    "qtip": "这个地方显示鼠标悬停提示",    "children": [      {        "id": "11111",        "text": "用户管理",        "href": null,        "leaf": true,        "iconCls": "fa fa-user",        "module_id": "sys.UserController",        "qtip": "系统用户管理",        "children": []      }    ]  }]

导航菜单与tabpanel 联动完成,下面弄个控制器实验一下效果,以新建一个系统管理分类下的用户管理模块功能为例:

系统管理部分模块放在sys目录下,so:

  • 控制器:app/luter/controller/sys/UserController.js
  • 模型:app/luter/model/UserModel.js
  • Store:app/luter/store/UserStore.js
  • 视图主入口:app/luter/model/view/sys/user/User.js
  • 列表视图:app/luter/model/view/sys/user/UserList.js
  • ......

用户管理控制器 :app/luter/controller/sys/UserController.js

Ext.define('luter.controller.sys.UserController', {    extend: 'Ext.app.Controller',    stores: ['UserStore'], //用户store    views: ['sys.user.User'], //主view ,tab里会加载第一个视图。    init: function () {        this.control({            'userlistview': {                'beforerender': function (view) {                    console.log("beforerender   list......   ");                },                'afterrender': function (view) {                    console.log("afterrender   list......   ");                     // this.getUserStoreStore().load();//如果UserStore里没设置autoLoad: true,就可以在这里加载用户数据                }            }        });    }});

用户模型Model:app/luter/model/UserModel.js

Ext.define('luter.model.UserModel', {    extend: 'Ext.data.Model',    fields: [        {name: 'id', type: 'string'},        {name: 'username', type: 'string'},        {name: 'gender', type: 'string'},        {name: 'real_name', type: 'string'}    ]});

用户Store:app/luter/store/UserStore.js

Ext.define('luter.store.UserStore', {    extend: 'Ext.data.Store',    autoLoad: true,//自动加载数据    model: 'luter.model.UserModel',//使用的模型    pageSize: 15,//每页数据多少    proxy: {        type: 'ajax',//ajax获取数据        actionMethods: {            create: 'POST',            read: 'POST',            update: 'POST',            destroy: 'POST'        },        api: {            read: 'app/testdata/user.json'//从这个地方获取数据,当然,这里用测试数据        },        reader: {//返回数据解析器            type: 'json',            root: 'root',//用户列表数据在这个字段下            successProperty: 'success',//成功与失败的标志位是这个字段            totalProperty: 'total'//记录总数在这个字段        },        listeners: {            exception: function (proxy, response, operation, eOpts) {                DealAjaxResponse(response);//监听ajax异常提示错误            }        }    },    remoteSort: true,//服务器端排序    sortOnLoad: true,//加载就排序    sorters: {//拿ID排序        property: 'id',        direction: 'DESC'    }});

用户管理模块主视图:app/luter/model/view/sys/user/User.js

Ext.define('luter.view.sys.user.User', {    extend: 'Ext.panel.Panel',    alias: 'widget.userview',    layout: 'fit',    requires: ['luter.view.sys.user.UserList'],//引入用户列表模块    border: false,    initComponent: function () {        var me = this;        me.items = [{            xtype: 'userlistview',            layout: 'fit'        }]        me.callParent(arguments);    }});

用户列表视图:app/luter/model/view/sys/user/UserList.js

Ext.define('luter.view.sys.user.UserList', {    extend: 'Ext.grid.Panel',    alias: 'widget.userlistview',//其他地方就可以这么用:xtype:‘userlistview’    requires: [],    store: 'UserStore',//用到的store    itemId: 'userGrid',//自己的itemid    columnLines: true,//是否显示表格线    viewConfig: {        emptyText: '暂无数据'//store没数据的时候显示这个    },    initComponent: function () {        var me = this;        me.columns = [{            xtype: 'rownumberer',            text: '序号',            width: 60        }, {            header: "操作",            xtype: "actioncolumn",            width: 60,            sortable: false,            items: [{                text: "删除",                iconCls: 'icon-delete',                tooltip: "删除这条记录",                handler: function (grid, rowIndex, colIndex) {                    var record = grid.getStore().getAt(rowIndex);                    if (!record) {                        toast({                            msg: '请选中一条要删除的记录'                        })                    } else {                        showConfirmMesg({                            message: '确定删除这条记录?',                            fn: function (btn) {                                if (btn === 'yes') {                                    Ext.Ajax.request({                                        url: 'sys/user/delete',                                        method: 'POST',                                        params: {                                            id: record.get('id')                                        },                                        success: function (response, options) {                                            DealAjaxResponse(response);                                            Ext.data.StoreManager.lookup('User').load();                                        },                                        failure: function (response, options) {                                            DealAjaxResponse(response);                                        }                                    });                                } else {                                    return false;                                }                            }                        })                    }                }            }]        }, {                    header: baseConfig.model.user.id,            dataIndex: 'id',            hidden: false,            flex: 1        },            {                header: baseConfig.model.user.username,                dataIndex: 'username',                flex: 1            },            {                header: baseConfig.model.user.real_name,                dataIndex: 'real_name',                flex: 1            }        ]        me.bbar = Ext.create('Ext.PagingToolbar', {            store: me.store,            displayInfo: true,            displayMsg: '当前数据 {0} - {1} 总数: {2}',            emptyMsg: "没数据显示",            plugins: [new Ext.create('luter.ux.grid.PagingToolbarResizer', {                options: [5, 10, 15, 20, 25, 50, 100]            })]        })        me.dockedItems = [{            xtype: 'toolbar',            items: [{                text: '添加',                iconCls: baseConfig.appicon.add,                tooltip: '添加',                handler: function () {                    var win = Ext.create('luter.view.sys.user.UserAdd');                    win.loadView();                    win.show();                }            }]        }]        me.listeners = {            'itemdblclick': function (table, record, html, row, event, opt) {                if (record) {                    var id = record.get('id');                    var view = Ext.create('luter.view.sys.user.UserEdit', {title: '编辑数据'});                    view.loadView();                    loadFormDataFromDb(view, 'sys/user/view?id=' + id);                } else {                    showFailMesg({                        msg: '加载信息失败,请确认。'                    })                }            }        }        me.plugins = []        me.callParent(arguments);    }});//这里的baseConfig定义在公共配置文件config.js中,如下:

公共配置参数定义文件:app/luter/config.js

别忘记在app.html中app.js之前引入这个文件。

/** * icon_prefix font字体前缀定义 * baseConfig 全局配置 */var icon_prefix = " fa blue-color ", baseConfig = {    /**     * 全局常量定义     */    cons: {        noimage: 'app/resource/images/noimage.jpg',        /**         * 静态服务器的地址         */        static_server: ''    },    /**     * 渲染器,对Boolean类型的表格列的显示内容进行渲染     */    renders: {        trueText: '',        falseText: '',        cancel: ''    },    /**     * 图标定义     */    appicon: {        home: icon_prefix + 'fa-home',        add: icon_prefix + "fa-plus",        update: icon_prefix + "fa-edit",        trash: icon_prefix + "fa-trash",        delete: icon_prefix + "fa-remove red-color",        set_wallpaper: icon_prefix + "fa-image",        setting: icon_prefix + "fa-gears",        desktop: icon_prefix + "fa-desktop",        pailie: icon_prefix + "fa-cubes",        logout: icon_prefix + "fa-power-off",        avatar: icon_prefix + "fa-photo",        key: icon_prefix + "fa-key",        user: icon_prefix + "fa-user",        refresh: icon_prefix + "fa-refresh blue-color",        close: icon_prefix + "fa-close",        male: icon_prefix + 'fa-male',        female: icon_prefix + 'fa-female',        role: icon_prefix + 'fa-users',        user_add: icon_prefix + "fa-user-plus",        undo: icon_prefix + 'fa-undo',        search: icon_prefix + 'fa-search',        reset: icon_prefix + 'fa-retweet',        yes: icon_prefix + 'fa-check green-color',        no: icon_prefix + 'fa-close red-color',        list_ol: icon_prefix + ' fa-list-ol',        list_alt: icon_prefix + ' fa-list-alt',        ban: icon_prefix + "fa-ban",        log: icon_prefix + "fa-tty",        printer: icon_prefix + "fa-print",        fax: icon_prefix + "fa-fax",        download: icon_prefix + "fa-cloud-download",        upload: icon_prefix + "fa-cloud-upload",        comment: icon_prefix + " fa-commenting-o",        credit: icon_prefix + "fa fa-gift"    },    /**     * 模型定义     */    model: {        /**         * 系统用户模型         */        user: {            id: 'ID',            username: '用户名',            real_name: '真实姓名'        }    }};

最后,附上用户列表的测试数据(当然,瞎编的......):app/testdata/user.json

{  "total": 33,  "root": [    {      "id": "aaa",      "username": "user",      "real_name": "用户"    },    {      "id": "ccc",      "username": "user",      "real_name": "用户"    },    {      "id": "ddd",      "username": "user",      "real_name": "用户"    },    {      "id": "eee",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    },    {      "id": "fff",      "username": "user",      "real_name": "用户"    }  ],  "success": true}

如上,没问题的话,刷新页面,应该能看到如下所示:

图片描述

上图中,一些Extjs默认的样式经过了hack。不是默认样式。

最终,整个项目的目录结构如下:
图片描述

判断是不是动态按需加载?

1、打开chrome的开发控制台,切换到network面板的js下。

2、刷新页面
3、重复点击左侧用户管理,查看JS加载情况。正常情况下同一个模块的js只加载一次。

转载地址:http://ggnbl.baihongyu.com/

你可能感兴趣的文章
[Composite UI][OB之BuilderStrategy扩展(一)]CAB之BuilderStrategy概览
查看>>
Windows 8 Platform (四) Windows 8 Consumer Preview
查看>>
Oracle数据库中的违规策略规则的修正
查看>>
和我一起学CSLA.NET----设计模型及数据访问
查看>>
explain
查看>>
在纯AS工程中嵌入个别字体方法 (转载)
查看>>
c# 进程间通信
查看>>
C# Web程序打包部署问题
查看>>
linux下面某些常用命令的用法【转】
查看>>
js table操作 ------ 拖拽行为并且使其自动贴附
查看>>
DDD为何叫好不叫座?兼论DCI与业务分析的方法论
查看>>
06.SQLServer性能优化之---数据库级日记监控
查看>>
如何在 IIS 上搭建 mercurial server
查看>>
一.Select 函数详细介绍【转】
查看>>
GNU make manual 翻译( 一百三十五)
查看>>
2012注定是收获的一年,奋斗才刚刚开始
查看>>
Elasticsearch之marvel(集群管理、监控)插件安装之后的浏览详解
查看>>
SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)...
查看>>
构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(16)-权限管理系统-漂亮的验证码...
查看>>
ASP.NET MVC5+EF6+EasyUI 后台管理系统(17)-LinQ动态排序
查看>>