Erfassen Sie Ihre Fortschritte
Trailhead-Startseite
Trailhead-Startseite

Verbinden von Komponenten mit Ereignissen

Lernziele

Nachdem Sie diese Lektion abgeschlossen haben, sind Sie in der Lage, die folgenden Aufgaben auszuführen:
  • Definieren benutzerdefinierter Ereignisse für Ihre Anwendungen
  • Erstellen und Auslösen von Ereignissen im Controller einer Komponente
  • Erstellen von Aktionshandlern zum Abfragen und Verarbeiten von Ereignissen, die andere Komponenten senden
  • Umgestalten einer großen Komponente in kleinere Komponenten

Verbinden von Komponenten mit Ereignissen

In dieser Einheit gehen wir den letzten Teil noch unfertiger Funktionalität in unserer kleinen Spesenabrechnungsanwendung an: das Kontrollkästchen "Reimbursed?" (Erstattet). Sie denken bestimmt, dass das Hinzufügen eines Kontrollkästchens nur eine kurze Episode sein wird. Wir könnten sicherlich einige Abkürzungen nehmen, um es sehr kurz zu machen.

Doch in dieser Einheit geht es zusätzlich zum Erstellen eines funktionierenden Kontrollkästchens um das Beseitigen aller Abkürzungen, die wir bereits gewählt haben. Der Fokus in dieser Einheit liegt darauf, "es richtig zu machen". Das bedeutet mitunter, dass wir zuvor erstellten Code umgestalten.

Doch bevor wir loslegen, lassen Sie uns zunächst über die Abkürzungen sprechen, die wir gewählt haben, und auch über den "richtigen Weg" und warum der "richtige Weg" (ein bisschen) schwieriger, aber auch besser ist.

Zusammensetzung und Zerlegung

Wenn Sie sich im Quellcodeformular unsere kleine Spesenabrechungsanwendung ansehen und die einzelnen Codeartefakte auflisten, erhalten Sie Folgendes:

  • Komponente "expenses"
    • expenses.cmp
    • expensesController.js
    • expensesHelper.js
  • Komponente "expensesList"
    • expensesList.cmp
  • Komponente "expenseItem"
    • expenseItem.cmp
  • ExpensesController (serverseitig)
    • ExpensesController.apex

Hier wird beschrieben, wie alles zusammen kommt, mit den Ereignissen createExpense und updateExpense, die Sie später verknüpfen.

Die Spesenanwendung besteht aus vielen kleineren Komponenten.

Doch was fällt Ihnen auf, wenn Sie Anwendung auf dem Bildschirm betrachten? Was Sie sehen sollten und was Sie letztlich überall erkennen, wohin Sie blicken, ist, dass die Anwendung aus wesentlich mehr Komponenten besteht. Sie werden sehen, dass Sie unsere Anwendung weiter zerlegen können, und zwar in noch kleinere Teile als bisher. Zumindest hoffen wir, dass Sie erkennen, dass das Formular "Add Expense" (Aufwendung hinzufügen) tatsächlich eine eigene, getrennte Komponente sein soll. (Deswegen haben wir auf der Benutzeroberfläche ein Feld um es herum gezeichnet!)

Warum haben wir dieses Formular nicht als getrennte Komponente erstellt? Dies nicht zu tun, war die bei weitem größte Abkürzung, die wir im Verlauf dieses Moduls gewählt haben. Mit Blick auf den Softwareentwurf ist dies schlimmer als der Hack, den wir als "Disgusting" bezeichnet haben. Der richtige Ansatz zum Erstellen einer Lightning Components-Anwendung ist das Erstellen unabhängiger Komponenten, die anschließend zusammengesetzt werden, um neue Funktionen auf höherer Ebene zu entwickeln. Warum haben wir diesen Ansatz nicht gewählt?

Wir haben uns für die Abkürzung entschieden. Wir haben das Formular "Add Expense" in der Hauptkomponente expenses belassen, weil dadurch das Attribut der Array-Hauptkomponente expenses und der Controllercode, der sich darauf auswirkt, in derselben Komponente verbleiben können. Wir wollten, dass die Hilfsfunktion createExpense() sich direkt auf das Array expenses auswirken kann. Wenn wir das Formular "Add Expense" in eine getrennte Komponente verschoben hätten, wäre dies nicht möglich gewesen.

Warum nicht? Wir haben den Grund dafür schon sehr früh angesprochen, wollen ihn aber jetzt in aller Ausführlichkeit darlegen. Lightning-Komponenten sollen unabhängig sein. Es handelt sich um eigenständige Elemente, die alle ihre wesentliche Funktionalität kapseln. Eine Komponente darf nicht in eine andere Komponente hineingreifen (selbst nicht in eine untergeordnete Komponente) und ihre inneren Abläufe ändern.

Es gibt zwei grundsätzliche Möglichkeiten, mit einer anderen Komponente zu interagieren oder diese zu beeinflussen. Die erste haben wir bereits kennen gelernt und genutzt: das Festlegen von Attributen für das Tag der Komponente. Die öffentlichen Attribute einer Komponente stellen einen Teil ihrer API dar.

Die zweite Möglichkeit zur Interaktion mit einer Komponente sind Ereignisse. Ebenso wie Attribute deklarieren Komponenten die Ereignisse, die sie senden, und die Ereignisse, die sie verarbeiten können. Ebenso wie Attribute stellen diese öffentlichen Attribute einen Teil der öffentlichen API der Komponente dar. Wir haben Ereignisse bereits verwendet und verarbeitet, wobei die Ereignisse jedoch aus Bequemlichkeit versteckt waren. In dieser Einheit zerren wir Ereignisse ans Licht und erstellen selbst welche.

Nochmals die Metapher des Verschaltens eines Schaltkreises

Diese beiden Mechanismen (Attribute und Ereignisse) sind die API-"Kupplungen", mit deren Hilfe Komponenten verbunden werden, um vollständige Schaltkreise zu bilden. Ereignisse sind außerdem (im Hintergrund) die Elektronen, die durch diesen Schaltkreis fließen. Doch dies ist das einige Kriterium, anhand dessen sich Ereignisse von Attributen unterscheiden.

Wenn Sie das Attribut onclick für die Schaltfläche <lightning:button> auf einen Aktionshandler im Controller einer Komponente festlegen, richten Sie eine direkte Beziehung zwischen diesen beiden Komponenten ein. Sie sind verknüpft. Und auch wenn sie öffentliche APIs verwenden, um unabhängig voneinander zu bleiben, sind sie dennoch gekuppelt.

Ereignisse sind anders. Komponenten senden keine Ereignisse an eine andere Komponente. So funktionieren Ereignisse nicht. Komponenten übertragen Ereignisse eines bestimmten Typs. Wenn es eine Komponente gibt, die auf diesen Typ von Ereignis reagiert, und wenn diese Komponente Ihr Ereignis wahrnimmt, führt sie die entsprechende Aktion aus.

Sie können sich den Unterschied zwischen Attributen und Ereignissen als den Unterschied zwischen drahtgebundenen Schaltkreisen und drahtlosen Schaltkreisen vorstellen. Und wir sprechen hier nicht von drahtlosen Telefonen. Eine Komponente erhält nicht die "Nummer" einer anderen, um sie anzurufen. Das wäre ein Attribut. Nein, Ereignisse sind wie drahtlose Übertragungen. Ihre Komponente kommt ins Radio und sendet eine Nachricht. Gibt es da draußen jemanden mit eingeschaltetem Radio, das auf die richtige Frequenz eingestellt ist? Ihre Komponente kann das nicht wissen. Deshalb sollten Sie Ihre Komponenten so schreiben, dass es in Ordnung ist, wenn niemand die Ereignisse hört, die sie senden. (Was heißt, dass die Dinge ggf. nicht funktionieren, aber nichts abstürzen sollte.)

Senden eines Ereignisses aus einer Komponente

Schluss jetzt mit Theorie. Lassen Sie uns etwas Bestimmtes mit unserer Anwendung anstellen und prüfen, wie Ereignisse im Code funktionieren. Wir beginnen mit der Implementierung des Kontrollkästchens "Reimbursed?". Danach setzen wir um, was wir dabei gelernt haben, und gestalten dann das Formular "Add Expense" zu einer eigenständigen Komponente um, wie eigentlich vorgesehen.

Zuerst wollen wir uns auf den Klickhandler für <lightning:input> für das Feld Reimbursed__c konzentrieren.

<lightning:input type="toggle"
            label="Reimbursed?"
            name="reimbursed"
            class="slds-p-around_small"
            checked="{!v.expense.Reimbursed__c}"
            messageToggleActive="Yes"
            messageToggleInactive="No"
            onchange="{!c.clickReimbursed}"/>

Ehe wir uns näher mit dem Klickhandler beschäftigen, wollen wir einen Blick darauf werfen, was <lightning:input> zu bieten hat.type="toggle" ist tatsächlich ein Kontrollkästchen mit einem Umschalterdesign. class ermöglicht Ihnen das Anwenden benutzerdefinierter CSS-Stile oder Verwenden von SLDS-Dienstprogrammen.messageToggleActive und messageToggleInactive bieten eine benutzerdefinierte Beschriftung für die aktivierte und deaktivierte Position. Diese praktischen Attribute zählen zu den vielen anderen für <lightning:input>. Schließlich bietet uns das Attribut onchange von <lightning:input> eine einfache Möglichkeit zum Verbinden des Umschalters mit einem Aktionshandler, der den Datensatz aktualisiert, wenn der Umschalter nach rechts (aktiviert) oder links (deaktiviert) verschoben wird.

Lassen Sie uns nun nachdenken, was passieren soll, wenn er aktiviert bzw. deaktiviert wird. Ausgehend vom Code, den wir geschrieben haben, um eine neue Aufwendung zu erstellen, erfolgt die Aktualisierung einer Aufwendung wahrscheinlich so.

  1. Abrufen des expense-Elements, das sich geändert hat.
  2. Erstellen einer Serveraktion zum Aktualisieren des zugrunde liegenden Spesendatensatzes.
  3. Packen von expense in die Aktion.
  4. Einrichten eins Rückrufs zum Verarbeiten der Antwort.
  5. Auslösen der Aktion und Senden der Anforderung zum Server.
  6. Wenn die Antwort erfolgt und der Rückruf ausgeführt wird, das Attribut expenses aktualisieren.

Hmm, welches Attribut expenses? Werfen wir noch einmal einen Blick auf das Markup unserer Komponente. Keine expenses, nur eine einzelne expense. Hmmm, stimmt, diese Komponente ist nur für einen einzelnen Eintrag. Es gibt ein expenses-Attribut für die Komponente expensesList, aber das sind nicht einmal die "tatsächlichen" expenses. Die tatsächlichen Spesen befinden sich in einem Komponentenattribut in der Komponente expenses auf oberster Ebene. Hmmm.

Gibt es component.get("v.parent")? Oder muss es component.get("v.parent").get("v.parent") heißen, etwas, das uns einen Verweis auf das übergeordnete Element unseres übergeordneten Elements abrufen ließe, damit wir expenses dort festlegen können?

Halt! Genau. Alle da.

Komponenten greifen nicht in andere Komponenten hinein, um Werte für diese festzulegen. Es ist nicht möglich zu sagen: " Hey, über-übergeordnetes Element, ich aktualisiere nun expenses". Komponenten behalten ihre Hände bei sich. Wenn eine Komponente möchte, dass eine Vorgängerkomponente eine Änderung vornimmt, bittet sie darum. Und zwar nett. Durch Senden eines Ereignisses.

Hier kommt der coole Teil. Das Senden eines Ereignisses sieht beinahe genauso aus wie das direkte Verarbeiten der Aktualisierung. Hier kommt der Code für den Handler clickReimbursed.

({
    clickReimbursed: function(component, event, helper) {
        let expense = component.get("v.expense");
        let updateEvent = component.getEvent("updateExpense");
        updateEvent.setParams({ "expense": expense });
        updateEvent.fire();
    }
})

Oha. Das ist ziemlich einfach! Und er sieht in etwa so aus, wie wir uns das oben vorgestellt haben. Der Code für clickReimbursed bewirkt Folgendes:

  1. Das expense-Element, das sich geändert hat, wird abgerufen.
  2. Ein Ereignis namens updateExpense wird erstellt.
  3. expense wird in das Ereignis gepackt.
  4. Das Ereignis wird ausgelöst.

Der Rückrufteil fehlt, doch ansonsten sieht das vertraut aus. Doch was übernimmt das Aufrufen des Servers und die Verarbeitung der Antwort des Servers sowie das Aktualisieren des Array-Hauptattributs expenses? Und woher wissen wir überhaupt von diesem updateExpense-Ereignis?

updateExpense ist ein benutzerdefiniertes Ereignis, d. h. ein Ereignis, das wir selbst schreiben. Das lässt sich erkennen, wenn wir im Gegensatz zum Abrufen einer Serveraktion component.getEvent() anstelle von component.get() verwenden. Zudem hat das, was wir abrufen, keinen Wertanbieter, sondern bloß einen Namen. Wir definieren dieses Ereignis in Kürze.

Lassen Sie uns zuvor darüber reden, womit das Abrufen des Servers und Verarbeiten der Antwort bewerkstelligt wird. Wir könnten gleich hier in der Komponente expenseItem die Serveranforderung implementieren und die Antwort verarbeiten. Anschließend könnten wir ein Ereignis zum bloßen erneuten Rendern der Dinge senden, die vom Array expenses abhängen. Das wäre eine perfekt gültige Entwurfsentscheidung. Wir könnten die Komponente expenseItem unabhängig belassen, was wünschenswert ist.

Wie wir jedoch sehen werden, ist der Code zum Erstellen einer neuen Aufwendung dem Code zum Aktualisieren einer vorhandenen Aufwendung sehr ähnlich, und zwar so ähnlich, dass wir es bevorzugen, duplizierten Code zu vermeiden. Deshalb besteht unsere Entwurfsentscheidung darin, ein updateExpense-Ereignis zu senden, das von der Hauptkomponente expenses verarbeitet wird. Wenn wir später unser Formular umgestalten, machen wir dasselbe zum Erstellen einer neuen Aufwendung.

Dadurch, dass alle untergeordneten Komponenten die Zuständigkeit für die Verarbeitung von Serveranforderungen und die Verwaltung des Array-Attributs expenses delegieren, stören wir die Kapselung ein wenig. Doch wenn Sie sich diese untergeordneten Komponenten als die internen Implementierungsdetails der Komponente expenses vorstellen, ist das in Ordnung. Die Hauptkomponente expenses ist unabhängig.

Sie haben eine Wahl: Konsolidierung kritischer Logik oder Kapselung. In Aura-Komponenten werden Sie wie bei allen anderen Softwareentwürfen Kompromisse eingehen. Stellen Sie einfach sicher, dass die Details dokumentiert werden.

Definieren eines Ereignisses

Zuallererst definieren wir unser benutzerdefiniertes Ereignis. Wählen Sie in der Entwicklerkonsole File | New | Lightning Event aus und nennen Sie das Ereignis "expensesItemUpdate". Ersetzen Sie den Standardinhalt durch das folgende Markup:

<aura:event type="COMPONENT">
    <aura:attribute name="expense" type="Expense__c"/>
</aura:event>

Es gibt zwei Arten von Ereignissen: Komponenten- und Anwendungsereignisse. Hier wählen wir ein Komponentenereignis, da wir möchten, dass eine Vorgängerkomponente das Ereignis abfängt und verarbeitet. Eine Vorgängerkomponente ist eine Komponente "über" dieser Komponente in der Komponentenhierarchie. Wenn wir ein Ereignis des Typs "allgemeine Übertragung" lieber hätten, das von jeder Komponente empfangen werden könnte, würden wir stattdessen ein Anwendungsereignis verwenden.

Die vollständigen Unterschiede und die ordnungsgemäße Nutzung von Anwendungs- und Komponentenereignissen werden wir hier nicht weiter vertiefen. Dies ist ein komplexeres Thema und wir haben schon genügend komplizierte Details, weshalb dies eine Ablenkung vom Zweck dieses Moduls wäre. Sobald Sie für mehr bereit sind, hilft Ihnen der Abschnitt "Ressourcen" weiter.

Ein anderer bemerkenswerter Aspekt des Ereignisses ist, wie kompakt die Definition ist. Wir haben das Ereignis bei seiner Erstellung expensesItemUpdate genannt. Sein Markup besteht aus einem öffnenden und schließenden <aura:event>-Tag und einem <aura:attribute>-Tag. Die Attribute eines Ereignisses beschreiben die unterstützte Nutzlast. Im Aktionshandler clickReimbursed legen wir die Nutzlast mit einem Aufruf von setParams() fest. Hier in der Ereignisdefinition sehen wir, wie der Parameter "event" definiert ist und dass es keine anderen gültigen Parameter gibt.

Und das ist schon alles, was es zum Definieren von Ereignissen braucht. Ereignissen selbst fügen Sie keine Implementierungs- oder Verhaltensdetails hinzu. Sie sind bloß Pakete. Tatsächlich haben einige Ereignisse überhaupt keine Parameter. Dies liegt daran, dass sie bloß Meldungen sind. "Dies ist geschehen!" Das gesamte Verhalten, was zu tun ist, wenn "dies" passiert, wird in den Komponenten definiert, die das Ereignis senden und empfangen.

Senden eines Ereignisses

Wir haben uns bereits angesehen, wie ein Ereignis im Aktionshandler clickReimbursed tatsächlich ausgelöst wird. Doch damit das funktioniert, müssen wir einen letzten Schritt ausführen, nämlich das Ereignis registrieren. Fügen Sie diese Zeile mit Markup der Komponente expenseItem unmittelbar unter den Definitionen ihrer Attribute hinzu.

    <aura:registerEvent name="updateExpense" type="c:expensesItemUpdate"/>

Dieses Markup besagt, dass unsere Komponente ein Ereignis namens "updateExpense" des Typs "c:expensesItemUpdate" auslöst. Doch war nicht "expensesItemUpdate" der Name des Ereignisses, als wir es definiert haben? Und was ist mit Typen von Komponenten- oder Anwendungsereignissen passiert?

Sie haben recht, wenn Sie das alles ein wenig verwirrend finden. Es ist ggf. hilfreich, sich "Anwendung" und "Komponente" als Ereignistypen im Aura-Komponenten-Framework vorzustellen, wohingegen die Typen, die von den Namen von Ereignissen stammen, die von Ihnen definiert werden, benutzerdefinierte Ereignistypen bzw. Ereignisstrukturtypen sind. Beim Definieren eines Ereignisses definieren Sie also ein Paketformat. Wenn Sie ein Ereignis für das Senden registrieren, deklarieren Sie das von diesem verwendete Format.

Der Prozess zum Definieren und Registrieren eines Ereignisses mag noch immer etwas komisch sein, weshalb wir ein wenig nach vorn blicken wollen. Hier in expenseItem senden wir ein Ereignis namens updateExpense. Später in expenseForm senden wir ein Ereignis namens createExpense. Diese beiden Ereignisse müssen eine Aufwendung enthalten, die auf dem Server gespeichert werden soll. Deshalb wird in beiden der Ereignistyp bzw. das Paketformat c:expensesItemUpdate verwendet, um ihre Ereignisse zu senden.

Auf der Empfangsseite wird unsere Hauptkomponente expenses zum Verarbeiten beider Ereignisse registriert. Wenngleich der Serveraufruf letztlich derselbe ist, sind die Aktualisierungen der Benutzeroberfläche geringfügig unterschiedlich. Woher weiß expenses, ob die Aufwendung im Paket c:expensesItemUpdate erstellt oder aktualisiert werden soll? Anhand des Namens des gesendeten Ereignisses.

Das Verstehen des hier vorliegenden Unterschieds und wie ein Ereignis für mehrere Zwecke genutzt werden kann, ist ein Aha-Erlebnis beim Erlernen von Lightning Components. Sollten Sie diese Aha-Erlebnis noch nicht gehabt haben, wird dies der Fall sein, wenn Sie sich den restlichen Code ansehen.

Ehe wir uns das Verarbeiten von Ereignissen ansehen, lassen Sie uns zusammenfassen, was erforderlich ist, um sie zu senden.

  1. Definieren eines benutzerdefinierten Ereignisses durch Erstellen eines Lightning-Ereignisses, das mit einem Namen und mit Attributen versehen wird.
  2. Registrieren Ihrer Komponente zum Senden dieser Ereignisse durch Wählen eines benutzerdefinierten Ereignistyps und das Versehen dieser besonderen Verwendung dieses Typs mit einem Namen.
  3. Auslösen des Ereignisses in Ihrem Controller- oder Hilfsfunktionscode durch:
    1. Verwenden von component.getEvent() zum Erstellen einer bestimmten Ereignisinstanz.
    2. Senden des Ereignisses mit fire().

Wenn Sie schon den gesamten Code implementiert haben, den wir uns gerade angesehen haben, können Sie ihn testen. Laden Sie Ihre Anwendung neu und schalten Sie das Kontrollkästchen "Reimbursed?" ein paar Mal um. Wenn Sie einen Schritt ausgelassen haben, erhalten Sie einen Fehler und müssen Ihren Code erneut überprüfen. Wenn Sie alles richtig gemacht haben...hey, warten Sie, die Aufwendung ändert ihre Farbe zum Anzeigen des Status von "Reimbursed?", genau wie erwartet!

Dieses Verhalten war vorhanden, ehe wir diese Einheit gestartet haben. Das ist die Folge davon, dass value="{!v.expense.Reimbursed__c}" für die Komponente <lightning:input> festgelegt ist. Wenn Sie den Schalter umschalten, wird die lokale Version von expense aktualisiert. Doch diese Änderung wird nicht an den Server gesendet. Wenn Sie sich den Spesendatensatz in Salesforce ansehen oder die Anwendung neu laden, wird die Änderung nicht angezeigt.

Warum nicht? Wir haben nur die Hälfte der Aufgaben zum Erstellen eines vollständigen Kreislaufs für unser Ereignis erledigt. Wir müssen den Kreislauf schließen, indem wir den Ereignishandler auf der anderen Seite erstellen. Dieser Ereignishandler übernimmt das Senden der Änderung an den Server und dauerhafte Speichern der Aktualisierung.

Verarbeiten eines Ereignisses

Das Aktivieren der Komponente expenseItem für das Senden eines Ereignisses erfordert drei Schritte. Das Aktivieren der Komponente expenses für das Empfangen und Verarbeiten dieser Ereignisse erfordert drei parallele Schritte.

  1. Definieren eines benutzerdefinierten Ereignisses. Das haben wir bereits getan, da expenseItem dasselbe benutzerdefinierte Ereignis sendet, das expenses empfängt.
  2. Registrieren der Komponente zum Verarbeiten des Ereignisses. Hierdurch wird das Ereignis einem Aktionshandler zugeordnet.
  3. Tatsächliches Verarbeiten des Ereignisses in einem Aktionshandler.

Da Schritt 1 schon erfolgt ist, lassen Sie uns direkt Schritt 2 angehen, dem Registrieren von expenses für das Empfangen und Verarbeiten des Ereignisses updateExpense. Wie die Registrierung zum Senden eines Ereignisses besteht die Registrierung zum Verarbeiten eines Ereignisses in einer Zeile mit Markup, die Sie der Komponente expenses unmittelbar hinter dem init-Handler hinzufügen müssen.

     <aura:handler name="updateExpense" event="c:expensesItemUpdate"
        action="{!c.handleUpdateExpense}"/>

Wie der init-Handler verwendet dieser das Tag <aura:handler> und hat das action-Attribut, das den Controller-Aktionshandler für das Ereignis festlegt. Ähnlich der Registrierung des Ereignisses in expenseItem legen Sie hier den Namen und Typ des Ereignisses fest. Beachten Sie allerdings die Verwendung des wesentlich vernünftiger benannten event-Attributs für den Typ.

Anders ausgedrückt, gibt es hier nicht viel, was Sie noch nicht gesehen haben. Was neu und besonders bei der Verarbeitung benutzerdefinierter Ereignisse ist, ist die Kombination der Attribute und das Wissen, wie diese "Empfängerkupplung" in expenses der "Absenderkupplung" in expenseItem entspricht.

Damit ist die Verknüpfung abgeschlossen, damit dies funktioniert. Jetzt müssen Sie nur noch den Aktionshandler schreiben!

Wir beginnen mit dem Aktionshandler handleUpdateExpense. Hier ist der Code, den Sie direkt unter dem Aktionshandler clickCreate einfügen müssen.

    handleUpdateExpense: function(component, event, helper) {
        let updatedExp = event.getParam("expense");
        helper.updateExpense(component, updatedExp);
    }

Hmm. Das ist interessant. Mit Ausnahme der Gültigkeitsprüfung für das Formular und die spezifische Hilfsfunktion, an die wir die Verarbeitung delegieren, scheint dieser Aktionshandler mit handleCreateExpense identisch zu sein.

Lassen Sie uns nun die Hilfsfunktion updateExpense hinzufügen. Fügen Sie wie beim Aktionshandler diesen Code unbedingt direkt unter der Hilfsfunktion createExpense hinzu.

    updateExpense: function(component, expense) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                // do nothing!
            }
        });
        $A.enqueueAction(action);
    },

Zwei Dinge sollten Ihnen auf Anhieb auffallen. Mit Ausnahme der Besonderheiten für den Rückruf ist die Hilfsmethode updateExpense mit der Hilfsmethode createExpense identisch. Das sieht nach einer Gelegenheit aus.

Zweitens wollen wir uns die Besonderheiten für den Rückruf ansehen. Wie bitte? Warum kann es richtig sein, nichts zu tun?

Denken Sie einen Moment darüber nach. Als wir zuvor das Senden des Ereignisses getestet haben (wenn nicht noch früher), haben wir festgestellt, dass sich die Farbe der Komponente expenseItem als Reaktion auf das Umschalten des Kontrollkästchens "Reimbursed?" geändert hat. Erinnern Sie sich noch an die Erläuterung? Die lokale Kopie des Spesendatensatzes wurde bereits aktualisiert! Deshalb müssen wir, zumindest für den Moment, wenn uns der Server informiert, dass er seine Version erfolgreich aktualisiert hat, nichts weiter tun.

Beachten Sie, dass dieser Code nur für den Fall zuständig ist, wenn der Server den Spesendatensatz erfolgreich aktualisiert. Falls ein Fehler auftreten sollte, müssen wir definitiv einige Aufgaben erledigen. Angenommen, die Buchhaltung hat diese Aufwendung als nicht erstattungsfähig gekennzeichnet, wodurch es unmöglich wird, dieses Feld auf true festzulegen. Doch das ist eine Übung für einen anderen Tag.

Umgestalten der Hilfsfunktionen

Lassen Sie uns zu der Gelegenheit zurückkehren, die wir zum Umgestalten von allgemeinem Code erkannt hatten. Bis auf den Rückruf sind die beiden Hilfsfunktionen identisch. Lassen Sie uns deshalb eine neue, allgemeinere Funktion erstellen, die den Rückruf als Parameter verwendet.

    saveExpense: function(component, expense, callback) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        if (callback) {
            action.setCallback(this, callback);
        }
        $A.enqueueAction(action);
    },

Der Parameter callback ist optional. Ist er vorhanden, übergeben wir ihn an action. Ganz einfach. Nun können wir unsere ereignisspezifischen Hilfsfunktionen auf den folgenden Code reduzieren.

    createExpense: function(component, expense) {
        this.saveExpense(component, expense, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
    },
    updateExpense: function(component, expense) {
        this.saveExpense(component, expense);
    },

createExpense ist nur ein wenig kürzer, aber ausschließlich darauf konzentriert, was passieren soll, wenn die Antwort zurückkommt (der Rückruf). Und hey, updateExpense ist ein Einzeiler!

Umgestalten des Formulars "Add Expense"

Diese kleine Umgestaltungsübung war so zufriedenstellend und das Verwenden von Ereignissen so (tut uns leid) elektrisierend, dass wir sie wiederholen möchten, aber umfassender. Tusch!!!

Die nächste Aufgabe besteht im Extrahieren des Formulars "Add Expense" aus der Komponente expenses und deren Verschiebung in eine eigene, neue Komponente. Das Extrahieren des Markups des Formulars erfolgt ganz einfach über Kopieren und Einfügen. Was wird noch damit verschoben? Bevor wir beginnen, Elemente planlos umherzuschieben, lassen Sie darüber nachdenken, was verschoben wird und was bleibt.

Im aktuellen Entwurf verarbeitet der Aktionshandler clickCreate die Validierung der Eingabe, das Senden der Anforderung an den Server sowie das Aktualisieren des lokalen Status und von Benutzeroberflächenelementen. Das Formular benötigt weiterhin einen Aktionshandler und muss wahrscheinlich weiterhin die Gültigkeitsprüfung für das Formular übernehmen. Doch wir haben vor, den Rest zurückzulassen, da wir unsere Serveranforderungslogik konsolidiert in der Komponente expenses belassen möchten.

Wir müssen hier deshalb eine kleine (wirklich nur kleine!) Auftrennung vornehmen. Unser Plan ist also, mit dem Verschieben des Formularmarkups zu beginnen und dann im Anschluss so wenig wie möglich zu verschieben, damit alles ordnungsgemäß funktioniert. Wir gestalten beide Komponenten so um, dass sie über Ereignisse anstatt über Direktzugriff auf das Array-Komponentenattribut expenses kommunizieren.

Los geht's!

Wählen Sie in der Hauptkomponente expenses alles zwischen den beiden <!-- CREATE NEW EXPENSE -->-Kommentaren aus, einschließlich der öffnenden und schließenden Kommentare selbst. Schneiden Sie das Ganze in die Zwischenablage aus. (Ja, ausschneiden. Wir wissen ja, was wir tun.)

Erstellen Sie eine neue Aura-Komponente und nennen Sie sie "expenseForm". Fügen Sie das kopierte Markup des Formulars "Add Expense" zwischen den Tags <aura:component> in die neue Komponente ein.

Navigieren Sie zur Komponente expenses zurück. Fügen Sie die neue Komponente expenseForm dem Markup hinzu. Dieser Abschnitt von expenses sollte so aussehen.

    <!-- NEW EXPENSE FORM -->
    <lightning:layout >
        <lightning:layoutItem padding="around-small" size="6">
            <c:expenseForm/>
        </lightning:layoutItem>
    </lightning:layout>

An dieser Stelle können Sie Ihre Anwendung neu laden, um die Änderungen zu prüfen. Es sollte keine sichtbaren Änderungen geben. Doch wenig überraschend funktioniert die Schaltfläche "Create Expense" nicht mehr.

Lassen Sie uns den Rest des Verschiebens von Dingen rasch durchlaufen.

Verschieben Sie als Nächstes das Attribut newExpense aus der Komponente expenses in das Markup der Komponente expenseForm. Dieses wird für die Formularfelder verwendet, weshalb es sich in der Formularkomponente befinden muss. Das Verschieben erfolgt ohne erforderliche Änderungen, sodass ein einfaches Kopieren und Einfügen genügt.

Erstellen Sie in der Komponente expenseForm die Controller- und Hilfsressourcen.

Verschieben Sie den Aktionshandler clickCreate aus der Komponente expenses in den Controller expenseForm. Die Schaltfläche ist in der Formularkomponente, weshalb sich dort auch der Aktionshandler für die Schaltfläche befinden muss. Ob Sie es glauben oder nicht, auch hier sind keine Änderungen erforderlich. (Sie fangen vielleicht schon an, ein Motiv zu erkennen.)

Nun müssen wir tatsächlich einige Änderungen vornehmen. Doch diese sind Ihnen vertraut, da wir bloß das Senden von Ereignissen hinzufügen, was wir zuvor schon für expenseItem getan haben. Wie Sie sich erinnern, sendet expenseItem auch ein Ereignis mit einer expense-Nutzlast, die von der Komponente expenses verarbeitet wird.

Erstellen Sie in der Hilfsfunktion expenseForm die Funktion createExpense.

    createExpense: function(component, newExpense) {
        let createEvent = component.getEvent("createExpense");
        createEvent.setParams({ "expense": newExpense });
        createEvent.fire();
    },

Diese sieht fast so aus wie der Aktionshandler clickReimbursed in expenseItem.

Wenn eine Komponente ein Ereignis sendet, muss sie das Ereignis registrieren. Fügen Sie Folgendes dem Markup der Komponente expenseForm unmittelbar unter dem Attribut newExpense hinzu.

    <aura:registerEvent name="createExpense" type="c:expensesItemUpdate"/>

An dieser Stelle haben wir alle Aufgaben zum Implementieren der Komponente expenseForm ausgeführt. Sie sollten in der Lage sein, die Anwendung neu zu laden. Das Formular funktioniert nun in dem Sinne, dass es keine Fehler gibt. Wenn Sie ungültige Daten eingeben, sollten die entsprechenden Formularmeldungen angezeigt werden. Wenn Sie Salesforce Lightning Inspector nutzen, können Sie sogar erkennen, dass das Ereignis expensesItemUpdate ausgelöst wurde. Jetzt muss es nur noch verarbeitet werden.

Bevor wir das Ereignis verarbeiten, erkennen Sie bitte an, wie einfach diese Umgestaltung war. Der meiste Code wurde nicht geändert. In den vorangegangenen Schritten haben wir mit insgesamt sechs neuen Zeilen mit Code und Markup gearbeitet. Diese Aufgabe mag Ihnen nicht vertraut sein, doch wenn Sie sie ein paar Mal ausgeführt haben, werden Sie merken, dass nur ein bisschen Code umher geschoben wird.

Lassen Sie uns dies nun abschließen. expenseForm löst das Ereignis createExpense aus, doch wir benötigen auch die Komponente expenses, um es abzufangen. Zuerst registrieren wir den Ereignishandler createExpense und verknüpfen ihn mit dem Aktionshandler handleCreateExpense. Dies ist wiederum nur eine einzelne Markup-Zeile. Fügen Sie diese Zeile unmittelbar über oder unter dem Ereignishandler updateExpense hinzu.

    <aura:handler name="createExpense" event="c:expensesItemUpdate"
        action="{!c.handleCreateExpense}"/>

Erstellen Sie im letzten Schritt den Aktionshandler handleCreateExpense im Controller expenses. Fügen Sie diesen Code unmittelbar über oder unter dem Ereignishandler handleUpdateExpense hinzu.

    handleCreateExpense: function(component, event, helper) {
        let newExpense = event.getParam("expense");
        helper.createExpense(component, newExpense);
    },

So einfach ist das. Die ganze Arbeit wurde an die Hilfsfunktion createExpense delegiert, die weder verschoben noch geändert wurde. Unser Aktionshandler handleCreateExpense hat bloß die Aufgabe, die richtigen Dinge miteinander zu verknüpfen.

Damit ist das Veranschaulichen, wie Komponenten mithilfe von Ereignissen lose gekuppelt werden, abgeschlossen. Erstellen Sie das Ereignis in einer Komponente, lösen Sie es aus, fangen Sie es ab und verarbeiten Sie es in einer anderen. Drahtlose Schaltkreise!

Bonuslektion: Geringfügige visuelle Verbesserungen

Bevor wir uns den Aufgaben zuwenden, hier noch eine bescheidene visuelle Verbesserung.

Wir möchten das Layout unserer Anwendung ein wenig verbessern, indem wir einige Containerkomponenten hinzufügen. Dieser letzte Schritt bietet Ihnen auch die Möglichkeit, die vollständige Komponente expense nach allen unseren Änderungen anzuzeigen. Ersetzen Sie in der Komponente expense das expensesList-Markup durch Folgendes.

<lightning:layout>
    <lightning:layoutItem padding="around-small" size="6">
        <c:expensesList expenses="{!v.expenses}"/>
    </lightning:layoutItem>
    <lightning:layoutItem padding="around-small" size="6">
        Put something cool here
    </lightning:layoutItem>
</lightning:layout>

Die Auswirkungen der Änderungen sind das Hinzufügen von Rändern und Innenabstand, wodurch die Liste "expenses" schmaler wird. Das Layout bietet außerdem an, etwas auf die rechte Seite zu verlagern. In der nächsten Einheit schlagen wir Ihnen verschiedene Übungen vor, die Sie selbst ausführen können.