将 ExtJS MVC 控制器附加到 DOM 元素,而不是组件

Posted

技术标签:

【中文标题】将 ExtJS MVC 控制器附加到 DOM 元素,而不是组件【英文标题】:Attach ExtJS MVC controllers to DOM elements, not components 【发布时间】:2011-05-18 16:20:50 【问题描述】:

有没有办法使用 Ext.app.Controller control() 方法,但传入一个 DOM 查询?我有一个包含标准链接的页面,并且希望向它们添加点击处理程序,即使它们不是作为 Ext 按钮创建的。

我试过了

Ext.define('app.controller.TabController', 
    extend: 'Ext.app.Controller',

    init: function() 
        console.log("init");
        this.control(
            'a': 
                click: this.changeTab
               
        );
    ,

    changeTab: function() 
        alert("new tab!");
       
);

但点击链接不会触发警报。

有没有办法用 this.control 指定 CSS 选择器?还是只适用于组件?

【问题讨论】:

我相信你早就想出了一些其他的解决方法,但我确实找到了解决上述问题的可行解决方案(在下面回答)。也许将来会有所帮助? 【参考方案1】:

我在今年的 SenchaCon 上问了这个问题,Sencha 开发人员表示他们的意图是 DOM 侦听器应该附加到您的视图中,并且视图应该将它们抽象为更有意义的组件事件并重新触发它们。

例如,假设您正在创建一个名为 UserGallery 的视图,该视图显示了一个人脸网格。在您的 UserGallery 视图类中,您将侦听 <img> 标记上的 DOM 点击事件以接收事件和目标,然后视图可能会触发一个名为“userselected”的组件事件并传递被点击用户的模型实例而不是DOM 目标。

最终目标是只有您的视图应该关注界面事件和 DOM 元素等事情,而应用程序级控制器只处理有意义的用户意图。您的应用程序和控制器代码根本不应该与您的标记结构或接口实现耦合。

示例视图

Ext.define('MyApp.view.UserGallery', 
    extend: 'Ext.Component'
    ,xtype: 'usergallery'

    ,tpl: '<tpl for="users"><img src="avatar_src" data-ID="id"></tpl>'

    ,initComponent: function() 
        this.addEvents('userselected');

        this.callParent(arguments);
    

    ,afterRender: function() 
        this.mon(this.el, 'click', this.onUserClick, this, delegate: 'img');

        this.callParent(arguments);
    

    ,onUserClick: function(ev, t) 
        ev.stopEvent();

        var userId = Ext.fly(t).getAttribute('data-ID');

        this.fireEvent('userselected', this, userId, ev);
    
);

关于视图的说明

当你想要的只是一个托管的&lt;div&gt; 时扩展“Ext.Component”,Ext.Panel 要支持标题栏、工具栏、折叠等内容要重得多。 在将侦听器附加到组件中的 DOM 元素时使用“托管”侦​​听器(请参阅 Component.mon)。当组件被销毁时,由组件管理的监听器将自动释放 从多个 DOM 元素侦听同一事件时,请使用“委托”事件选项并将侦听器附加到它们的公共父级而不是单个元素。这表现得更好,让您可以任意创建/销毁子元素,而不必担心不断地将事件侦听器附加/删除到每个子元素。避免使用.select('img').on('click', handler)之类的东西 从视图触发事件时,Sencha 的约定是事件的第一个参数是scope——对触发事件的视图的引用。当从控制器处理事件时,这很方便,您需要将事件处理程序的实际范围作为控制器。

样品控制器

Ext.define('app.controller.myController', 
    extend: 'Ext.app.Controller'

    ,init: function() 
        this.control(
            'usergallery': 
                userselected: function(galleryView, userId, ev) 
                    this.openUserProfile(userID);
                
               
        );
    

    ,openUserProfile: function(userId) 
        alert('load another view here');
    
);

【讨论】:

很好的答案。这应该在事件指南中。 这是一些好东西!我已经在 Ext 工作了大约 9 个月了,你刚刚大大改进了我的代码。 我希望我能多次投票。这个答案写得很好,并用给出的例子进行了彻底的解释。这是一个很好的答案的典范。 我刚刚更新了这个以反映将事件范围作为其第一个参数传递的最佳实践 谢谢,你让我免于试错!【参考方案2】:

我已找到解决此问题的方法。它并不像人们希望的那样直接,但它会将您所有的“动作”代码留在控制器中。

要求:将页面的 html 部分包装在实际的 Ext.Component 中。无论哪种方式,情况都可能如此。例如,您可能有一个包含 HTML 的简单视图,如下所示:

Ext.define('app.view.myView', 
    extend: 'Ext.panel.Panel',
    alias: 'widget.myView',
    title: 'My Cool Panel',
    html: '<div><a href="#">This link will open a window</a></div><br /> <label for="myInput">Type here: </label><input name="myInput" type="text" value="" />',
    initComponent: function()
        var me = this;
        me.callParent(arguments);
    
);

然后在控制器中使用afterrender 事件将侦听器应用于您的DOM 元素。在下面的示例中,我说明了链接(一个元素)和输入元素:

Ext.define('app.controller.myController', 
extend: 'Ext.app.Controller',

    init: function() 
        this.control(
            'myView': 
               afterrender: function(cmp)
                    var me = this; //the controller
                    var inputs = cmp.getEl().select('input'); // will grab all DOM inputs
                    inputs.on('keyup', function(evt, el, o)
                        me.testFunction(el); //you can call a function here
                    );

                    var links = cmp.getEl().select('a'); //will grab all DOM a elements (links)
                    links.on('click', function(evt, el, o) 
                        //or you can write your code inline here
                        Ext.Msg.show(
                            title: 'OMG!',
                            msg: 'The controller handled the "a" element! OMG!'
                        );
                    );
                
               
        );
    ,
    testFunction: function(el) 
        var str = 'You typed ' + el.value;
        Ext.Msg.show(
            title: 'WOW!',
            msg: str
        );
       
);

你有它,在控制器中处理的 DOM 元素并遵守 MVC 架构!

【讨论】:

【参考方案3】:

不,这似乎是不可能的。 Ext.EventBus 监听 ExtJS 组件触发的事件。您的标准 DOM 元素不会触发这些事件。此外,使用 ExtJS 组件 is(String selector) 方法检查查询,DOM 元素不能调用该方法。如果我错了,有人可能会纠正我,但我很确定这是不可能的,不幸的是。

【讨论】:

我也查了一下,好像是这样 我错过了一件事情:Ext.core.Element 实际上 is(String selector) 方法。但我不知道如何以及是否有可能让事件触发......【参考方案4】:

我也有解决此问题的解决方案。无论如何我都会使用这种技术,因为它还有其他好处:我创建了一个应用程序范围的消息传递总线。它是我的应用程序中的一个对象,它扩展了 Observable,并定义了一些事件。然后我可以从我的应用程序中的任何地方触发这些事件,包括 html a 链接。任何想要监听这些事件的组件都可以中继它们,并且它们会像从该组件中触发一样触发。

Ext.define('Lib.MessageBus', 
    extend: 'Ext.util.Observable',

    constructor: function() 
        this.addEvents( 
                  "event1",
                  "event2"
                 );
        this.callParent(arguments);
    
);

然后,其他组件可以在初始化后添加:

this.relayEvents('Lib.MessageBus', ['event1','event2']);

然后收听这些事件。 您可以通过以下方式触发任何事件:

Lib.MessageBus.fireEvent('event1', 'param a', 'param b')

你可以通过任何东西来做到这一点,包括 html 链接。

将事件从应用程序的一个部分触发到另一部分非常方便。

【讨论】:

【参考方案5】:

我最近遇到了同样的问题(将 mvc 与一些非组件混合)。 只是想我会把这个作为答案,因为它看起来很简单并且对我有用:)

Ext.define('app.controller.TabController', 
    extend: 'Ext.app.Controller',

    init: function() 
        console.log("init");
        this.control(
           /* 'a': 
                click: this.changeTab
             */  
        );

    var link = Ext.dom.Query.selectNode('a');

    Ext.get(link).on('click', this.changeTab);

    ,

    changeTab: function() 
        alert("new tab!");
       
);

【讨论】:

以上是关于将 ExtJS MVC 控制器附加到 DOM 元素,而不是组件的主要内容,如果未能解决你的问题,请参考以下文章

如何将 DOM 元素附加到动态新添加的 DOM 元素

Extjs mvc 将记录添加到网格面板

将多个元素附加到 DOM 的 jquery 会丢失排序顺序

将函数中配置的 html 添加到 ExtJS 4 MVC

如何使用 Jquery 或 Javascript 定位附加元素,或者如何将该附加元素添加到 DOM?

将 Canvas 元素附加到 DOM