【翻译】在Ext JS 5种使用ViewControllers

原文:Using ViewControllers in Ext JS 5

简介

在Ext JS 5中,在应用程序架构方面提供了一些令人兴奋的改进,如添加了ViewModels、MVVM以及viewControllers来加强MVC应用程序。最重要的是,这些选择并不互斥,因此,可以采用增量的方式来介绍这些功能,又或者将他们混合在一起。

回顾控制器

在Ext JS 4,控制器就是一个从Ext.app.Controller的派生的类。这些控制器会使用类似CSS选择器(称为组件查询)来查找组件并对他们的事件做出响应。还可以使用refs来选择或返回组件实例。

这些控制器会在应用程序启动时被创建,且会存在于整个应用程序的生命周期。在控制器的生命周期内,控制器所关注视图可以说是来了又去。甚至可能是一个控制器管理着很多实例。

挑战

对于大型应用程序,这技术可能会遇到某些挑战。

在这种环境中,视图和控制器可能是由多个开发团队开发并继承到最终的应用程序的。为来确保控制器只对他们关注的视图做出响应将会是一项艰难的工作。此外,对于开发人员来说,通常都会希望在应用程序启动时限制控制器创建的数量。虽然可以通过努力延迟控制器的创建,但是他们不能被销毁,因此在他们不再需要的时候仍然会保持在内存中。

ViewControllers

虽然Ext JS 5能向后兼容这些控制器,但它引入了一种新型的控制器,可设计来处理这些挑战: Ext.app.ViewController。ViewController通过以下方式来实现操作:

  • 简化视图的listeners和reference配置项的使用
  • 利用视图的生命周期来自动管理他们相关的ViewController
  • 基于一对一来关联管理视图以降低ViewController的复杂度
  • 提供封装以使嵌套视图更可靠
  • 为相关视图之上的任何层保留选择组件和监听他们事件的能力

Listeners

配置项listeners虽然不是新的,但是在Ext JS 5为它添加了新的能力。新的listeners功能会在未来的文章《Declarative Listeners in Ext JS 5》中进行全面的探讨。针对ViewControllers,我们来看两个示例。第一个是最基本的,在视图的子组件中使用listeners配置项:

Ext.define(‘MyApp.view.foo.Foo‘, {
    extend: ‘Ext.panel.Panel‘,
    xtype: ‘foo‘,
    controller: ‘foo‘,

    items: [{
        xtype: ‘textfield‘,
        fieldLabel: ‘Bar‘,
        listeners: {
            change: ‘onBarChange‘  // no scope given here
        }
    }]
});

Ext.define(‘MyApp.view.foo.FooController‘, {
    extend: ‘Ext.app.ViewController‘,
    alias: ‘controller.foo‘,

    onBarChange: function (barTextField) {
        // called by ‘change‘ event
    }
});
 

在上面代码的listeners中,使用了一个命名事件句柄(onBarChange),但没有指定作用域(scope)。在事件系统内部,会将Bar文本字段的作用域解析为其所属的ViewController。

在过去,listeners配置项会预留给组件的创建者使用,那么,如何让视图去监听自己的事件呢,而且又可以让它的基类来触发呢?解决之道就是使用显式的作用域:

Ext.define(‘MyApp.view.foo.Foo‘, {
    extend: ‘Ext.panel.Panel‘,
    xtype: ‘foo‘,
    controller: ‘foo‘,

    listeners: {
        collapse: ‘onCollapse‘,
        scope: ‘controller‘
    },

    items: [{
        ...
    }]
});

以上示例代码利用了两个Ext JS 5的新功能:命名的作用域和声明性监听。在这里的重点是命名的作用域。在这里可使用两个有效的作用域命名:this和controller。在写MVC应用程序的时候,基本上总是会使用controller,这不用说指的就是视图的ViewController(不是创建视图实例的那个视图的ViewController )。

由于视图是Ext.Component的一种类型,因而可为它定义一个xtype,以便其他视图以相同的方式来创建创建视图实例及其文本字段。为了探讨如何将这些整合在一起,考虑一下如何使用改视图,例如:

Ext.define(‘MyApp.view.bar.Bar‘, {
    extend: ‘Ext.panel.Panel‘,
    xtype: ‘bar‘,
    controller: ‘bar‘,

    items: [{
        xtype: ‘foo‘,
        listeners: {
            collapse: ‘onCollapse‘
        }
    }]
});

在这种情况下,Bar视图创建了Foo视图的一个实例作为它的一个子组件。此外,还为Foo视图绑定了collapse事件。在之前版本的Ext JS或Sencha Touch,这些声明将会产生冲突,而在Ext JS 5,正如预期的那样得到了解决。通过Foo视图声明的监听只会在Foo的ViewController内触发,而在Bar视图声明的监听只会在Bar的ViewController内触发。

Reference(引用)

在编写控制器逻辑的时候,最烦的就是需要获取所需的组件来完成只能的操作,例如:

Ext.define(‘MyApp.view.foo.Foo‘, {
    extend: ‘Ext.panel.Panel‘,
    xtype: ‘foo‘,
    controller: ‘foo‘,

    tbar: [{
        xtype: ‘button‘,
        text: ‘Add‘,
        handler: ‘onAdd‘
    }],

    items: [{
        xtype: ‘grid‘,
        ...
    }]
});

Ext.define(‘MyApp.view.foo.FooController‘, {
    extend: ‘Ext.app.ViewController‘,
    alias: ‘controller.foo‘,

    onAdd: function () {
        // ... get the grid and add a record ...
    }
});

现在应该如何起获取网格组件?在Ext JS 4,可以使用refs配置项或者一些其他方式来寻找组件。所以这些技术都要求在网格上放置一些可用来识别且能唯一标识网格的属性。老技术会使用id配置项(Ext.getCmp)或者itemId(适用于refs或一些组件查询语法)配置项。使用id的优势是可实现快速查找,但必须确保这标识在整个应用程序或DOM中是唯一的,该方法并不可取。使用itemId或其他一些查询会更灵活,但这需要执行搜索来寻找所需的组件。

在Ext JS 5可以使用新的reference配置项来实现,只需要添加reference到网格并使用lookupReference就可以来获取它:

Ext.define(‘MyApp.view.foo.Foo‘, {
    extend: ‘Ext.panel.Panel‘,
    xtype: ‘foo‘,
    controller: ‘foo‘,

    tbar: [{
        xtype: ‘button‘,
        text: ‘Add‘,
        handler: ‘onAdd‘
    }],

    items: [{
        xtype: ‘grid‘,
        reference: ‘fooGrid‘
        ...
    }]
});

Ext.define(‘MyApp.view.foo.FooController‘, {
    extend: ‘Ext.app.ViewController‘,
    alias: ‘controller.foo‘,

    onAdd: function () {
        var grid = this.lookupReference(‘fooGrid‘);
    }
});

这类似于分配了一个名为fooGrid的itemId并执行“this.down(‘#fooGrid‘)”。不过,这在底层的实现是不同的,而且区别很大。首先,reference配置项会将组件自身注册到它所属的视图(通常是ViewController中标识)。其次,lookupReference方法只去查询缓存以确定是否需要刷新引用(指的是容器的add或remove操作)。如果一切顺利,就可从缓存中返回引用。另外,伪代码如下:

lookupReference: (reference) {
    var cache = this.references;
    if (!cache) {
        Ext.fixReferences(); // fix all references
        cache = this.references; // now the cache is valid
    }
    return cache[reference];
}
 

也就是说,这样就不需要搜索且不会由于是一次成型的,会因容器的添加或删除子组件而出现问题。正如所看到的,这样的好处就是提供了效率。

封装(Encapsulation)

虽然在Ext JS 4种使用选择器的实现非常灵活,但同时也存在一定的风险。事实上,这些选择器可以“看到”所有的组件层次结构,既强大也容易出现错误。例如,一个控制器在隔离运行时可以运转正常,但一旦引入了其他视图,就可以会失败,原因是它的选择器可能会意外的匹配了新的视图。

这些问题可通过以下做法来进行管理,不过,当在ViewController中使用listeners或references时,要解决这些问题就变得不可能了。这是因为listeners和references配置项只能连接他们所属的ViewController。在视图内部,可以使用任意唯一的reference值,因为视图知道这些名字不会曝露给视图的创建者。

同样,listeners也可以保留给它所属的ViewController而不会意外的分发到其它使用错误的选择器的控制器的事件处理中。虽然监听会优先于选择器,但在需要以选择器为基础的时候,这两种机制可以一起工作。

要实现这个,视图需要通过它所属视图的ViewController来触发事件。在ViewController有一个辅助方法可以用了实现这个:fireViewEvent,例如:

Ext.define(‘MyApp.view.foo.FooController‘, {
    extend: ‘Ext.app.ViewController‘,
    alias: ‘controller.foo‘,

    onAdd: function () {
        var record = new MyApp.model.Thing();
        var grid = this.lookupReference(‘fooGrid‘);
        grid.store.add(record);

        this.fireViewEvent(‘addrecord‘, this, record);
    }
});
 

在视图的创建者可使用标准形式的监听:

Ext.define(‘MyApp.view.bar.Bar‘, {
    extend: ‘Ext.panel.Panel‘,
    xtype: ‘bar‘,
    controller: ‘bar‘,

    items: [{
        xtype: ‘foo‘,
        listeners: {
            collapse: ‘onCollapse‘,
            addrecord: ‘onAddRecord‘
        }
    }]
});

监听和事件域

在Ext JS 4.2,MVC事件分发会被一般化为对事件域的引用。事件域会在事件触发时截获事件并将他们分发到由选择器匹配所控制的控制器中。尽管其他域只有有限的选择器,但组件事件域具有完整的组件查询选择器。

在Ext JS 5,每一个ViewController会创建一个名为view的新的事件域的实例。该事件域允许ViewController去使用标准的隐式的限制了它的作用域为所属视图的listen或control方法。它还添加了一个特定的选择了来匹配视图自身。

Ext.define(‘MyApp.view.foo.FooController‘, {
    extend: ‘Ext.app.ViewController‘,
    alias: ‘controller.foo‘,

    control: {
        ‘#‘: {  // matches the view itself
            collapse: ‘onCollapse‘
        },
        button: {
            click: ‘onAnyButtonClick‘
        }
    }
});
 

从以上代码可以看出listeners和选择器之间的区别。选择器button可匹配视图及子视图(不论深度,即使是是重孙视图)中的任何按钮,也就是说,基于选择器的处理不会理会封装边界,改行为与以前的Ext.app.Controller的行为是一致的,在有限的情况下这是很实用的技术。

最后,事件域会遵循嵌套并有效的将事件依照视图层次往上冒泡,也就是说,当一个事件触发时,它首先会传递到任何标准的listeners中。然后,它会传递到它所属的ViewController,接着通过它的父ViewController(如果存在)传递到最顶层。最终,事件会被传递到标准的组件事件域,以便由Ext.app.Controller派生的控制器处理。

生命周期

大型应用程序常用的技术是会根据需要动态创建所需的控制器。这有助于减少应用程序的加载时间,且有助于在运行时通过不激活潜在的控制器已提供性能。这在之前的版本是不可能的,控制器一旦创建,就会在应用程序中保持活动状态,不会被销毁并释放资源。同样,这也没改变一个控制器可能包含任意数目的相关视图(包括没有视图)这一事实。

然而,ViewController会在组件生命周期的早期创建并将它的整个生命周期绑定在了视图。当视图被销毁的时候,ViewController同样也会销毁。这意味着,ViewController从此不用在没有视图或很多视图的时候被强迫去管理状态。

这种一对一的管理意味着引用跟踪被简化了,而且不再会轻易的因组件的销毁而出现泄漏。ViewController可在它生命周期内任何关键点内实现任何方法来处理事务。

beforeinit——该方法可被重写,它会在视图调用initComponent方法之前执行。该方法会在控制器创建之后立即被调用,而这会在组件的构造函数之内在调用initConfig方法期间发生。

init——在视图调用initComponent方法不久后会调用该方法。这通常会是视图初始化后,执行控制器初始化的时候,

initViewModel——在创建视图的ViewModel(如果存在)时会调用该方法。

destroy——清理任何资源(确保调用了callParent方法)。

小结

我们相信ViewController将会大大简化你的MVC应用程序。他们还可以与ViewModels一起使用。因此,你可以结合这些方法以及他们的各自优势。我们很兴奋即将发布正式版以及看到这些在你的应用程序中所做的改进。

作者:Don Griffin
Don Griffin is a member of the Ext JS core team. He was an Ext JS user for 2 years before joining Sencha and has over 20 years of software engineering experience on a broad range of platforms. His experience includes designing web application front-ends and back-ends, native GUI applications, network protocols and device drivers. Don’s passion is to build world class products that people love to use.

【翻译】在Ext JS 5种使用ViewControllers

时间: 2024-09-28 09:40:43

【翻译】在Ext JS 5种使用ViewControllers的相关文章

【翻译】Ext JS——高效的编码风格指南

原文:ExtJS - Efficient coding style guide 作者:Raja 切勿使用"new"关键字:在Ext JS中,使用"new"关键字来创建一个组件或类的实例是一种错误的做法,因为这没有遵循组件的生命周期.应该使用Ext.create方法来创建对象,例如: 错误: var obj = new Ext.panel.Panel(); 正确: var obj = Ext.create('Ext.panel.Panel'); 初始化直接量:不要直接

【翻译】Ext JS最新技巧——2014-5-12

原文:http://www.sencha.com/blog/top-support-tips-may-2014?mkt_tok=3RkMMJWWfF9wsRoluazJZKXonjHpfsX77OQlXK%2B%2FlMI%2F0ER3fOvrPUfGjI4AT8NjI%2BSLDwEYGJlv6SgFSbfBMbdlybgMWRA%3D Seth Lemmons:为网格行设置不同的高度 在整个网格中,行高通常都会保持为同一高度.然而,这并不适用于所有情形,有时候需要根据单元格中的数据来自动调整

【翻译】Ext JS 5的平板支持

原文:Ext JS 5 Tablet Support Ext JS已被公认为桌面Web应用程序的领先框架.自从平板开始在全球挑战PC的销售,无论是个人还是企业,电脑横向的应用已经产生急剧的变化.Sencha意识到了这种变化,并推出了包含新功能和进行优化了的Ext JS 5. Ext JS从Sencha Touch 2学到了一些新把戏.多年最好的移动Web应用程序框架经验的沉淀要应对现代平板电脑上的桌面显示,那是卓卓有余的.通过类系统.事件管理.窗口小部件和新的部署选项就可以了解到这些更新. 除了

【翻译】Ext JS 6有什么新东西?

工具包ToolKits 发布 包的命名 Fashion 图表 ItemEdit插件 网格 电子表格 可操作模式Actionable Mode和可访问性 LazyItems插件 屏幕阅读器支持可访问性 微加载Microloader Touch网格 原文:http://docs.sencha.com/extjs/6.0/whats_new/6.0.0/whats_new.html Ext JS在Sencha框架中引入了许多新的和令人兴奋的改进.这些变化为基于所有现代浏览器.设备和屏幕尺寸带来了新的功

【翻译】Ext JS最新技巧——2014-10-30

原文:Top Support Tips Greg Barry:Ext JS 5的ExtraParams Ext JS 4允许用户直接将extraParams添加到一个链接,类似如下代码: Ext.Ajax.extraParams = { foo: "bar" }; 由于Ext JS5对数据包进行了修改,该方法就不再可行了.现在,需要使用 setExtraParams和getExtraParams方法来为Ext.data.Connection提供参数,例如: Ext.Ajax.setEx

【翻译】Ext JS 6早期访问版本发布

早期访问版本是什么 如何参与 都包括什么 Sencha Ext JS 6 Sencha Pivot Grid Sencha Cmd 6 JetBrains IDE插件 反馈 原文:Announcing Ext JS 6 Early Access Release 在令人惊艳的SenchaCon 2015最后一周,我们非常兴奋,因为Ext JS 6早期访问版本要发布了.在Ext JS 6,可以使用单一的javascript框架来无缝的创建基于桌面.平板和智能手机的应用程序. 下载Ext JS 6早期

ext.js 两种局部刷新的方式

$.ajax({ type: "POST", url: "AjaxAlgorithmOptimzeHandler.ashx?cmd=yhtrafficFlow", data: 'encoded=' + encoded, dataType: 'json', success: function (msg) {//msg其实就是Car类的一个实例 等价 car1 alert(msg); } }); 1 listeners: { 2 click: function (a,

谈谈Ext JS的组件——容器与布局

概述 在页面中,比較棘手的地方就是布局.而要实现布局.就得有能维护布局的容器. 能够说,在我试过和使用过的Javascript框架中,Ext JS的布局是做得最优秀的一个,而这得益于它强大的容器类和丰富的布局类.在本文将介绍Ext JS的容器组合和布局类. 容器:Ext.container.Container 容器的主要功能是管理其内部的组件.因而在继承Ext.Component的所有功能的基础上.加入了相应的用来处理内部组件的方法add.insert.remove和removeAll. 在配置

【翻译】在Ext JS 5应用程序中如何使用路由

原文:How to Use Routing in Your Ext JS 5 Apps 简介 Ext JS 5是一个重要的发布版本,它提供了许多新特性来创建丰富的.企业级的Web应用程序.MVVM和双向数据绑定为开发人员承担了大量的繁重工作.在Ext JS 5种,另一个新特性就是路由,它可以在控制器内轻松的管理历史记录.前进和后退按钮是每个浏览器都会拥有的公共用户接口,现在,使用Ext JS 5在单页面应用程序中处理导航变得相当简单了. Ext JS 5路由 在Ext JS,已经可以使用Ext.