1. 程式人生 > >Ext JS 5的宣告式事件監聽

Ext JS 5的宣告式事件監聽

在前文《在Ext JS 5使用ViewControllers》中,簡單的介紹了Ext JS 5的一項重要改進——宣告式事件監聽。在本文,將深度探討如何使用宣告式事件監聽啦簡化應用程式的檢視並減少自定義元件的樣板程式碼。

注意:文章假設你使用的是Ext JS 5.0.1或更高版本。

什麼是宣告式事件監聽?

所謂的“宣告式事件監聽”,就是指定義在類主體中的監聽或在例項的配置物件中使用了listeners配置項。以這種方式來宣告事件監聽不是Ext JS 5的新功能。在Ext JS 4,可以正在類中宣告事件監聽,不過只適於處理函式或作用域已定義的情況,例如:

    Ext.define('MyApp.view.User', {
        extend: 'Ext.panel.Panel',
 
        listeners: {
            // 函式必須內聯或在之前已被定義:
            collapse: function() {
                // respond to panel collapse here
            }
        },
 
        //該方法不能被定義為collapse的處理函式:
        onCollapse: function() {
        }
    });

由於所需的處理函式不能使用類中定義的方法,因而宣告式監聽在Ext JS 4中的使用有限。開發人員通常會通過重寫initComponent方法並使用on方法來新增監聽:
    Ext.define('MyApp.view.User', {
        extend: 'Ext.panel.Panel',
 
        initComponent: function() {
            this.callParent();
 
            this.on({
                collapse: this.onCollapse,
                scope: this
            });
        },
 
        onCollapse: function() {
            console.log(this); // the panel instance
        }
    });
 

作用域解析

在Ext JS 5,對listeners配置項做了改進,允許將事件處理指定為字串來對應方法名。在執行時(觸發事件的任何時候),框架會將這些方面解析為實際的函式引用。我們將這一過程稱為事件監聽作用域解析。

在Ext JS 4,如果明確給出了“作用域”,才能解析字串處理程式。而在Ext JS 5,在宣告“字串”處理程式而沒有明確宣告作用域的時候,為預設作用域解析添加了一些特殊規則。

作用域解析有兩種結果:元件或檢視控制器(ViewController)。無論是哪種結果,都會從元件開始搜尋。作用域可能是元件,也可能是檢視控制器,如果不是,框架會“爬”到元件的上層直到找到適合的元件或檢視控制器。

解析作用域為元件


框架解析作用域的第一種方式是尋找defaultListenerScope配置項為true的元件。對於類中的事件監聽宣告,搜尋會從元件自身開始。

    Ext.define('MyApp.view.user.User', {
        extend: 'Ext.panel.Panel',
        xtype: 'user',
        defaultListenerScope: true,
 
        listeners: {
            save: 'onUserSave'
        },
 
        onUserSave: function() {
            console.log('user saved');
        }
    });
 

監聽被定義在了使用者檢視的類主體,這意味著框架在提升層次之前會先檢查使用者檢視自身的defaultListenerScope。在當前示例,使用者檢視將defaultListenerScope設定為了true,那當前監聽的作用域將會被解析為使用者檢視。

對於事件監聽被宣告在例項配置項的情況,將會條過元件自身,框架會從父容器開始搜尋,請參考以下程式碼:
    Ext.define('MyApp.view.main.Main', {
        extend: 'Ext.container.Container',
        defaultListenerScope: true,
 
        items: [{
            xtype: 'user',
            listeners: {
                remove: 'onUserRemove'
            }
        }],
 
        onUserRemove: function() {
            console.log('user removed');
        }
    });

對於使用者檢視的監聽是在例項的配置物件中宣告的,這意味著框架會跳過使用者檢視(儘管它定義了defaultListenerScope為true),且會解析為主檢視。

解析作用域為檢視控制器



在Ext JS 5,引入了新的控制器型別——Ext.app.ViewController。在《在Ext JS 5使用ViewControllers》中詳細介紹了檢視控制器,因此這裡只討論與檢視控制器與事件監聽有關的部分。

與Ext.app.Controller可以管理許多檢視不同,每一個檢視控制器例項只能繫結一個檢視例項。檢視與檢視控制器之間之間一對一的關係允許檢視控制器作為檢視或檢視的條目中事件監聽宣告的預設作用域。

對於defaultListenerScope,規則同樣適用於檢視控制器。類層的監聽總是會在搜尋元件的上層之前先搜尋元件自身的檢視控制器。

    Ext.define('MyApp.view.user.User', {
        extend: 'Ext.panel.Panel',
        controller: 'user',
        xtype: 'user',
 
        listeners: {
            save: 'onUserSave'
        }
    });
 
    Ext.define('MyApp.view.user.UserController', {
        extend: 'Ext.app.ViewController',
        alias: 'controller.user',
 
        onUserSave: function() {
            console.log('user saved');
        }
    });

上述監聽被宣告在使用者檢視的類主體內,由於使用者檢視有它自己的控制器,框架會解析作用域為UserController。如果使用者檢視沒有自己的控制器,那麼作用域會解析到上層。


另一方面,例項層監聽會跳過元件並解析為檢視控制器上層的父容器,例如:
    Ext.define('MyApp.view.main.Main', {
        extend: 'Ext.container.Container',
        controller: 'main',
 
        items: [{
            xtype: 'user',
            listeners: {
                remove: 'onUserRemove'
            }
        }]
    });
 
    Ext.define('MyApp.view.main.MainController', {
        extend: 'Ext.app.ViewController',
        alias: 'controller.main',
 
        onUserRemove: function() {
            console.log('user removed');
        }
    });

合併listeners配置項



在Ext JS 4,在基類宣告的監聽會被子類或例項的listeners配置項的宣告完全重寫。在Ext JS 5,改進了listeners的API,可適當的合併在基類、子類和例項中的事件監聽宣告。要想了解其中的行為,可檢視以下示例:

    Ext.define('BaseClass', {
        extend: 'Ext.Component',
        listeners: {
            foo: function() {
                console.log('foo fired');
            }
        }
    });
 
    Ext.define('SubClass', {
        extend: 'BaseClass',
        listeners: {
            bar: function() {
                console.log('bar fired');
            }
        }
    });
 
    var instance = new SubClass({
        listeners: {
            baz: function() {
                console.log('baz fired');
            }
        }
    });
 
    instance.fireEvent('foo');
    instance.fireEvent('bar');
    instance.fireEvent('baz');

在Ext JS 4,上面示例只會輸出“baz”,但在Ext JS 5,listeners配置項會被正確的合併並輸出“foo bar baz”。這就允許類在需要的時候才去宣告監聽而不需要知道超類是否已經有了監聽。


小結


我們任務宣告式的監聽可大大簡化應用程式中的事件監聽定義。結合檢視控制器用於處理應用程式的邏輯和檢視模型的雙向繫結,還可以儘可能的改進應用程式的開發體驗。嘗試去讓我們知道你的想法。

作者:Phil Guerrant
Phil is a Sencha software engineer who works on Ext JS. He has over 10 years of experience as a developer and specializes in HTML5 and web development, UI, and agile methodologies.