ViewController in ExtJS 5 verwenden

Ursprünglicher Autor: Don Griffin
  • Übersetzung
  • Tutorial
ExtJS 5 bringt einige aufregende Architekturverbesserungen mit sich: Wir haben Unterstützung für ViewModels, MVVMs und ViewController hinzugefügt, um MVC-Anwendungen zu verbessern. Das Beste daran ist, dass sich diese Funktionen nicht gegenseitig ausschließen, sodass Sie sie schrittweise eingeben oder gleichzeitig verwenden können.


Über Controller


In ExtJS 4 ist ein Controller eine Klasse, von der er geerbt wurde Ext.app.Controller. Diese Controller verwenden CSS-ähnliche Selektoren (als „Component Queries“ bezeichnet), um Komponenten und ihre Event-Handler abzubilden. Sie verwenden auch die sogenannten refsfür die Probenahme und die Beschaffung von Instanzen von Komponenten.

Diese Controller werden beim Start der Anwendung erstellt und arbeiten während des gesamten Lebenszyklus. Ansichten werden erstellt und gelöscht, es kann mehrere Instanzen geben und die Controller bedienen alle.

Schwierigkeiten

Für große Anwendungen können diese Techniken einige Schwierigkeiten mit sich bringen.

In solchen Fällen können Repräsentationen und Controller von verschiedenen Autoren in verschiedenen Entwicklungsteams erstellt und dann in die endgültige Anwendung integriert werden. Und sicher zu sein, dass Controller nur auf die für sie bestimmten Ideen reagieren, ist schon schwierig. Außerdem möchten Entwickler normalerweise die Anzahl der Controller reduzieren, die beim Starten der Anwendung erstellt werden. Da die Möglichkeit (mit einigem Aufwand), Controller zu erstellen, verschoben wird, besteht keine Möglichkeit, sie zu löschen, sodass sie auch dann erhalten bleiben, wenn sie nicht mehr benötigt werden.

ViewController


Da die Ext JS 5 mit dem Stromregler rückwärtskompatibel ist, bietet es eine neue Art von Controller einzurichten , solche Probleme zu lösen: Ext.app.ViewController. Die Lösung wird wie folgt erreicht:

  • Konfigurationseigenschaften werden verwendet listenersund die referenceKommunikation mit Ansichten wird vereinfacht.
  • Der View-Lebenszyklus wird verwendet, um die entsprechenden ViewController automatisch zu steuern.
  • Die Komplexität von ViewControllern wird so reduziert Eine Eins-zu-Eins-Beziehung wird mit der entsprechenden Darstellung verwendet.
  • Die Kapselung ermöglicht das Erstellen verschachtelter Ansichten.
  • Es ist weiterhin möglich, Komponenten auszuwählen und deren Ereignisse auf jeder Ebene unterhalb der entsprechenden Ansicht zu verfolgen.

Zuhörer

Die Konfiguration ist listeners nicht neu, hat aber in Ext JS 5 neue Funktionen. Eine ausführlichere Beschreibung neuer Listener finden Sie im kommenden Artikel - "Declarative Listeners in Ext JS 5". Zur Verwendung in ViewControllern können wir uns einige Beispiele ansehen. Das erste ist die übliche Verwendung der Konfiguration listenersin der verschachtelten Ansichtskomponente:

Ext.define('MyApp.view.foo.Foo', {
    extend: 'Ext.panel.Panel',
    xtype: 'foo',
    controller: 'foo',
    items: [{
        xtype: 'textfield',
        fieldLabel: 'Bar',
        listeners: {
            change: 'onBarChange'  // не задаём контекст (scope)
        }
    }]
});
Ext.define('MyApp.view.foo.FooController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.foo',
    onBarChange: function (barTextField) {
        // будет вызвано по событию 'change'
    }
});

In diesem Fall hat der genannte Handler onBarChange keinen bestimmten Kontext ( scope). Das Ereignissystem für ein Textfeld Barmacht seinen eigenen ViewController zum Standardkontext.

In der Vergangenheit listenerswar die Konfiguration für die Verwendung durch die übergeordnete Komponente reserviert, daher stellt sich die Frage: Wie kann eine Ansicht ihre eigenen Ereignisse oder sogar die von der Basisklasse aufgerufenen Ereignisse abhören? Die Antwort wäre, einen expliziten Kontext zu verwenden:

Ext.define('MyApp.view.foo.Foo', {
    extend: 'Ext.panel.Panel',
    xtype: 'foo',
    controller: 'foo',
    listeners: {
        collapse: 'onCollapse',
        scope: 'controller'
    },
    items: [{
        ...
    }]
});

In diesem Beispiel werden zwei neue Funktionen in ExtJS 5 verwendet: Named Scopes und Declarative Listener (ca. Transl. ). Wir werden uns auf den genannten Kontext konzentrieren. Für benannte Kontexte sind zwei Namen gültig: "this"und "controller". Wenn wir MVC-Anwendungen schreiben, verwenden wir fast immer "controller"eine, die offensichtlich unser eigener ViewController ist (und nicht der ViewController der Ansicht, die in der Hierarchie höher ist).

Weil Ist dies eine Ansicht Ext.Component, dann haben wir diese Ansicht zugewiesen xtype, sodass andere Ansichten sie auf die gleiche Weise instanziieren können, wie wir es getan haben textfield. Um zu sehen, wie alles zusammenpasst, erstellen Sie eine Hierarchie:

Ext.define('MyApp.view.bar.Bar', {
    extend: 'Ext.panel.Panel',
    xtype: 'bar',
    controller: 'bar',
    items: [{
        xtype: 'foo',
        listeners: {
            collapse: 'onCollapse'
        }
    }]
});

In diesem Fall erstellt die Ansicht Bar eine Instanz Fooals eines ihrer Elemente. Außerdem hört es das Ereignis collapseauf die gleiche Weise wie die Aufführung Foo. In früheren Versionen von Ext JS und Sencha Touch standen diese Definitionen in Konflikt. Dies ist jedoch in Ext. JS 5 behoben: Die in deklarierten Listener Foo funktionieren im ViewController Foound werden Bar im ViewController deklariert Bar.

Referenzen

Eine der in der Steuerungslogik weit verbreiteten Funktionen besteht darin, die zum Ausführen einer Aktion erforderliche Komponente zu erhalten. So etwas in der Art:

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 () {
        // ... получить grid и добавить запись ...
    }
});

Aber wie bekommen wir den Tisch? In ExtJS 4 können Sie die Konfiguration refsoder eine andere Methode verwenden, um die Komponente abzurufen. Bei allen Techniken müssen Sie einer Tabelle eine erkennbare Eigenschaft zuweisen, um sie eindeutig zu identifizieren. Ältere Techniker verwendeten die Konfiguration id(en Ext.getCmp) oder itemId(unter Verwendung refseiner anderen Auswahlmethode). Der Vorteil idliegt in der schnellen Probenahme, aber da Bezeichner müssen in der gesamten Anwendung eindeutig sein, und dies ist im DOM nicht immer erreichbar. Die Verwendung itemIdverschiedener Abfragetypen (Abfrage) ist flexibler, Sie müssen jedoch eine Suche durchführen, um die gewünschte Komponente zu finden.

Mit der neuen Konfiguration referencein Ext JS 5 fügen wir sie einfach der Tabelle hinzu und verwenden lookupReferencesie, um sie abzurufen:

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');
    }
});

Dies ist ähnlich der Zuordnung itemId = 'fooGrid'und weiter this.down('#fooGrid'). Aber "unter der Haube" ist der Unterschied erheblich. Zunächst referenceerfordert die Konfiguration, dass sich die Komponente in der Eigentümeransicht registriert. Zweitens lookupReference fragt die Methode den Cache nach dem Aktualisierungsbedarf (vermutlich aufgrund des Hinzufügens oder Entfernens im Container). Wenn alles in Ordnung ist, wird nur der Link aus dem Cache zurückgegeben. Im Pseudocode:

lookupReference: (reference) {
    var cache = this.references;
    if (!cache) {
        Ext.fixReferences(); // обновить ссылки
        cache = this.references; // теперь кэш валидный
    }
    return cache[reference];
}

Mit anderen Worten, es wird keine Suche durchgeführt, und die Verbindungen, die durch Hinzufügen oder Entfernen von Elementen aus dem Container beschädigt wurden, werden sofort repariert, wenn sie benötigt werden. Wie wir weiter unten sehen werden, bietet dieser Ansatz neben der Effizienz weitere Vorteile.

Verkapselung

Die Verwendung von Selektoren in der Ext JS 4 MVC-Implementierung war sehr flexibel, brachte jedoch auch einige Risiken mit sich. Die Tatsache, dass die Selektoren alle Ereignisse auf allen Ebenen „sahen“, war mächtig, aber fehleranfällig. Beispielsweise kann eine Steuerung, die 100% fehlerfrei arbeitet, Fehler erzeugen, sobald neue Ansichten mit unerwünscht übereinstimmenden Selektoren angezeigt werden.

Dies könnte durch Befolgen einiger Vorgehensweisen behoben werden. Durch die Verwendung von Listenern und Links in ViewControllern werden diese Probleme jedoch einfach beseitigt. Und das alles wegen configs referenceund listenersverbinden mit ihrer Präsentation nur ViewController'om. In verschachtelten Ansichten kann jeder Wert referenceinnerhalb der aktuellen verwendet werden, da diese Namen für die Ansicht mit der höheren Hierarchie nicht offen sind.

Dabei,listeners werden vom entsprechenden ViewController angefordert und können in anderen Controllern mit falschen Selektoren nicht verarbeitet werden. Angesichts der Tatsache, dass Zuhörer Selektoren vorzuziehen sind, können diese beiden Mechanismen an den Stellen gleichzeitig funktionieren, an denen die Verwendung eines auf Selektoren basierenden Ansatzes gerechtfertigt ist.

Um dieses Beispiel zu vervollständigen, betrachten Sie den Fall, in dem eine Ansicht ein Ereignis auslösen kann, das vom ViewController einer übergeordneten Ansicht verarbeitet wird. Um dies zu tun in ViewController'e haben Helfer-Methode: fireViewEvent. Zum Beispiel:

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 record = new MyApp.model.Thing();
        var grid = this.lookupReference('fooGrid');
        grid.store.add(record);
        this.fireViewEvent('addrecord', this, record);
    }
});

Dies ermöglicht die Verwendung eines Standard-Listeners in einer Ansicht über der Hierarchie:

Ext.define('MyApp.view.bar.Bar', {
    extend: 'Ext.panel.Panel',
    xtype: 'bar',
    controller: 'bar',
    items: [{
        xtype: 'foo',
        listeners: {
            collapse: 'onCollapse',
            addrecord: 'onAddRecord'
        }
    }]
});

Von einem Übersetzer: Es klingt verwirrend, aber im Wesentlichen fireViewEventkönnen Sie ein Ereignis für eine Ansicht im ViewController auslösen. Wenn in diesem Fall keine ViewController vorhanden wären, würde dies bedeuten, dass die übliche fireEventFoo-Ansicht im Code aufgerufen wird .

Listener und Ereignisdomänen

In Ext JS 4.2 wurden Ereignisdomänen im MVC-Ereignismanager eingeführt. Diese Domänen fangen Ereignisse ab, sobald sie aufgerufen werden, und übergeben sie an Controller, die sie durch Zufall von Selektoren verarbeiten. Die Ereignisdomäne component bietet vollständige Unterstützung für Komponentenselektoren, während andere nur eingeschränkte Unterstützung bieten.

In Ext JS 5 erstellt jeder ViewController eine Instanz eines neuen Ereignistyps mit dem Namen view. Diese Domain ViewController'am Ereignisse ermöglicht die Verwendung von Standardverfahren listen und controlsomit ihren Umfang , ihre eigenen Ideen implizit zu begrenzen. Er fügt auch einen neuen speziellen Selektor hinzu, der seiner eigenen Ansicht entspricht:

Ext.define('MyApp.view.foo.FooController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.foo',
    control: {
        '#': {  // совпадает с собственным представлением
            collapse: 'onCollapse'
        },
        button: {
            click: 'onAnyButtonClick'
        }
    }
});

Der Hauptunterschied zwischen Listenern und Selektoren ist im folgenden Beispiel zu sehen. Die Auswahl buttonstimmt mit einer beliebigen Schaltfläche in dieser Ansicht oder in einem beliebigen Unter-Unter-Unter überein, zumindest spielt dies keine Rolle. Mit anderen Worten, selektorbasierte Handler berücksichtigen keine Vererbungsgrenzen. Dieses Verhalten stimmt mit dem Verhalten der vorherigen überein Ext.app.Controllerund kann in einigen Situationen eine nützliche Technik sein.

Schließlich berücksichtigen diese Domänen die Verschachtelung und leiten das Ereignis effektiv in der Hierarchie der Darstellungen weiter ( ein Ereignis in die Luft sprudeln lassen, - ca.) Das heißt, wenn ein Ereignis ausgelöst wird, wird es zuerst an Standard-Listener übergeben. Dann wird es an den Eigentümer ViewController übergeben und anschließend die Hierarchie an den übergeordneten ViewController (falls vorhanden). Letztendlich wird das Ereignis componentzur Verarbeitung in den Controllern an die Standard-Ereignisdomäne übergeben Ext.app.Controller.

Lebenszyklus

Die Standardpraxis bei großen Anwendungen besteht darin, Steuerungen nach Bedarf dynamisch zu erstellen. Dies kann dazu beitragen, die Ladezeit von Anwendungen zu verkürzen und die Leistung zu verbessern, ohne andere Controller zu beeinträchtigen. Eine Einschränkung dieses Ansatzes in früheren Versionen bestand darin, dass diese Controller beim Erstellen während der gesamten Lebensdauer der Anwendung funktionsfähig bleiben. Es war unmöglich, sie zu zerstören, um Ressourcen freizusetzen. Es hat sich auch nichts geändert, da Controller nicht nur eine, sondern mehrere Instanzen von Ansichten bedienen können.

Aus diesem Grund wird der ViewController im Lebenszyklus der Ansicht sofort erstellt und während seiner gesamten Lebensdauer an diese Ansicht angehängt. Wenn die Ansicht gelöscht wird, tut dies auch der ViewController. Dies bedeutet, dass der ViewController nicht mehr arbeiten muss, wenn keine oder mehrere Ansichten vorhanden sind ( nicht mehr gezwungen, Zustände zu verwalten, in denen keine oder viele Ansichten vorhanden sind - offenbar gibt es einen Vergleich mit klassischen Controllern; ca. transl. ).

Die Eins-zu-Eins-Beziehung vereinfacht die Verbindungsverfolgung und macht es unmöglich, entfernte Komponenten zu verlieren. Die folgenden Schlüsselereignisse werden im ViewController-Lebenszyklus ausgelöst:

  • beforeInit ist eine Methode, die überschrieben werden kann, um die Ansicht zu steuern, bevor die Methode initComponentaufgerufen wird. Wird sofort nach dem Erstellen des Controllers aufgerufen, wenn die initConfig Komponente oder ihr Konstruktor ausgeführt wird .
  • init - wird unmittelbar nach Beendigung der Methode in der Ansicht aufgerufen initComponent. Dies ist ein typischer Punkt für die Initialisierung eines Controllers, dessen Präsentation bereits initialisiert ist.
  • initViewModel - Wird aufgerufen, als das ViewModel der Ansicht erstellt wurde (sofern eines definiert ist).
  • Zerstören - Bereinigen von Ressourcen (nicht vergessen, anzurufen callParent).


Fazit


ViewController eignen sich hervorragend für die Aktualisierung Ihrer Anwendungen. Sie funktionieren auch gut mit ViewModels, sodass Sie die Stärken beider Ansätze kombinieren können. Wir freuen uns über die bevorstehende Veröffentlichung und freuen uns auch über Verbesserungen in Ihren Anwendungen.

PS

Zurück nach: Ext JS 5: MVC, MVVM, etc.

Jetzt auch beliebt: