关于extjs:动态管理Ext.app.Application.controllers

dynamically manage Ext.app.Application.controllers

目前,我们的团队正在评估使用 ExtJS 作为前端转换大型企业 Web 应用程序(一种 ERP 系统,600 个独特的屏幕)的可能性。该应用程序是基于我们的开源 eludia 引擎构建的

我们的引擎需要模型定义(当您编辑定义时它会变形数据库),有某种控制器
(内容模块)和演示文稿(带有生成实际 js html 混合代码的演示模块)

就像这个帖子中的一些人一样,我们的团队有一个问题:

我们希望在服务器端拥有模型和视图,并将 JSON 数据发送到前端

目前 eludia 核心开发人员(=我的团队,我们同时维护这个应用程序和 eludia)已经完成了一些步骤,以使用 ExtJS 作为前端的变形引擎

我的团队正在考虑:

  • 继续使用旧的内容模块作为服务器端代码
  • 使用服务器端模型定义即时为 ExtJS 生成模型文件,
  • 将 Presentation 模块转换为客户端 ExtJS 视图模块,并为每个屏幕编写客户端控制器
    但是现在又多了一个问题:ExtJS 需要枚举 Ext.app.Application 中的所有控制器
    每次一个人写新的/转换旧引擎的屏幕时,他都应该将它添加到这个列表中

Ext.app.Application.controllers ...可以动态生成吗?

因此这些问题按模糊程度排序:

  • 你能说出任何使用 ExtJS 作为前端的足够大(600 个屏幕,最好是开源的)MVC/非 MVC 应用程序吗?
  • 我们是否以正确的方式前进?

更新

我应该尽量缩小问题的范围

应用启动时不需要一次性加载所有控制器吗?

我想说的是,也许可以以更"动态"的方式加载控制器:

  • 为打开的屏幕生成一个控制器js
  • 将新的附加到 Ext.app.Application.controllers
    每当用户做某事(点击链接、按钮等)时:需要新屏幕时


最好的办法是依赖 Ext.Loader,但不要使用上面建议的 loadScriptFile() 方法。

Ext 4 引入了一个类系统。其(许多)好处之一是 dep-tree,它允许您管理所有组件间依赖项并根据需要加载类。

这里是你如何初始化加载器

1
2
3
Ext.Loader.setPath('App','/js/app');
Ext.Loader.setPath('WidgetsLibrary','/js/widgets');
Ext.Loader.setConfig({enabled: true});

定义一个动态加载的控制器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
Ext.define('App.controller.Menu', {
    extend: 'Ext.app.Controller',      // leave it as it is
    models: ['Event','User'],          // this assumes App.model.* prefix
    stores: ['Options','Permissions'], // this assumes App.store.* prefix
    views:  ['menu.Bar','SmartButton'],// this assumes App.view.* prefix
    requires: [
        'App.whatever.other.class',    // auto-load this class as a dependency
        'WidgetsLibrary.*',            // auto-load all classes in WidgetsLibrary
    ],

    init: function(){}

 // [...]

现在动态加载你的控制器类(一个模型):

1
2
3
4
5
6
7
8
9
Ext.require(                  
    'App.controller.Menu',     // this auto-loads all dependencies
    function(){
        // ... as soon as this class
        //    and all its dependencies have loaded...
        var controller = Ext.create('App.controller.Menu');  // create an instance
        controller.init();                                   // launch init() method
    }
);

奖励:通过使用类系统和加载器,您不必维护自己的控制器集合并检查控制器是否已加载。只需使用此处描述的 Ext.ClassManager.isCreated() 方法。

1
2
3
4
5
if(Ext.ClassManager.isCreated('App.controller.Menu')){
    // ... controller has already been loaded and init ...
}else{
    // we need to auto-load that controller using Ext.require()
}

更多阅读:

  • http://docs.sencha.com/ext-js/4-0/#!/guide/class_system
  • http://docs.sencha.com/ext-js/4-0/#!/guide/application_architecture

按需使用控制器的最佳方式是动态加载它们并在需要时创建控制器实例。您还需要将控制器放在单独的 js 文件中,因此架构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//let's say you need to use controller User:
var controller = ControllerManager.get('User');//I believe you have some controller manager, similar to Sencha's Ext.ControllerManager
if(controller){
//use it here
}
else {
  loadScript([url of your controller script], controllerLoaded);
}
...
function controllerLoaded(){
//create controller instance here if needed and use it then
}
...
function loadScript(url, callback){

    var script = document.createElement("script")
    script.type ="text/javascript";

    if (script.readyState){  //IE
        script.onreadystatechange = function(){
            if (script.readyState =="loaded" ||
                    script.readyState =="complete"){
                script.onreadystatechange = null;
                callback();
            }
        };
    } else {  //Others
        script.onload = function(){
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

更新
为了将新控制器集成到已经运行的 Ext.app.Application 中,您需要扩展 Ext.app.Application 并添加以下方法:

1
2
3
4
5
6
7
addController: function(name){
  //at this point your controller .js file should be already loaded into the DOM  
  var c = this.getController(name); //controller will be created automatically by name in this getter
  //perform the same initialization steps as it would have during normal ExtJs process
  c.init(this);
  ??.onLaunch(this);
}

顺便说一句,您可以使用 Ext.Loader:

的 ExtJs 内置方法,而不是使用自定义方法动态加载 javascript 文件

1
2
3
4
5
6
7
8
9
10
/**
         * Load a script file, supports both asynchronous and synchronous approaches
         *
         * @param {String} url
         * @param {Function} onLoad
         * @param {Object} scope
         * @param {Boolean} synchronous
         * @private
         */
        loadScriptFile: function(url, onLoad, onError, scope, synchronous)


我们刚刚使用我们的 web 应用程序完成了这个过程。约 250 个屏幕。是的,我们动态加载我们的控制器。

使用 Ext.JS 构建屏幕的时间比使用 YUI(我们之前的平台)快 400%。不错的是我们可以保留 YUI Connect 对象,它比 Ext.JS 版本更高效。

我们有一个 App 类来管理 Ext.JS 控制器的加载和初始化。在这里,我们在代码中使用了唯一的另一个 YUI 类,即 YUI.get (YUI.get.script)。只需确保您没有在同一会话中两次加载控制器即可。

我假设您想动态加载这些控制器以缩短加载时间(这也是我们最初也遇到的问题)。扩展,扩展,扩展。

1
2
3
4
5
Ext.define('namespace.classname', {
{
    extend: 'Ext.form.panel',
    // your code here
}

这将降低您的整体代码下载速度,并加快初始化速度。


我在这里问了同样的问题:http://www.sencha.com/forum/showthread.php?151220-Adding-controllers-views-dynamically-in-Ext-MVC-pattern

在当前的 Ext 4 中似乎没有很好地支持这一点。我已经切换了方向并且没有使用 Ext 提供的 MVC 架构,而是使用我在 Ext 3 中使用的相同技术,即添加/在某些处理程序上加载附加组件(按钮或链接点击、悬停、搜索等)。

在我的应用程序中,我的所有组件都是基 Ext 类的自定义扩展,并且实际上最终表现得非常像控制器,超类是视图。例如,如果我有 Ext.panel.Panel 的扩展,除了 itemslayout 的初始配置之外,我不会做任何影响视图的事情(这一切都在基类中处理)。大多数类都将处理程序和其他东西附加到面板以提供特定于应用程序的行为,这基本上是控制器的定义。在我看来,它并不完美,但它比当前的替代方案要好。

如果您想了解我所说的任何示例,或者对我的具体实施有疑问,请告诉我,我很乐意为您提供帮助。


虽然我不知道这是否可行(从未尝试过),但我会看的是 Ext.app.Application.getController() 方法。来自 api 参考:

Returns instance of a controller with the given name. When controller doesn't exist yet, it's created.

所以理论上,您可以使用动态定义的控制器调用 getController(),它会创建它。

我在查看 Ext 4 时了解到的一个旁注。控制器不能触发事件,因此您必须在控制器之间进行通信时触发 Application 对象上的事件,如本文所述