Winkel-Zwei-Wege-Bindung, etwas verständnisvoller

Vom Übersetzer
Von einem Übersetzer : Vor zwei Jahren startete ich mein erstes Projekt zu Angular (2+) mit einem großen und erfolgreichen AngularJS-Hintergrund. Der Übergang erforderte eine spürbare Formatierung des Denkens, da zu viel auf A1 und A2 + „ein bisschen anders“ gemacht wird. Der Schmerz des Übergangs hat den Gedankenstram- Blog für mich deutlich reduziert. Vor einem Jahr erhielt ich die Erlaubnis, diesen Artikel "über elementare und für alle leicht verständliche" zu übersetzen. Aber sie sind solche Hände (ihre Artikel sind ein Haufen unfertiger). Überraschenderweise lässt sich der Artikel gut auf Google Übersetzer übersetzen. Einige der Nuancen dieser Übersetzung gingen jedoch verloren, ganz zu schweigen vom Stil des Autors. Der Stil des Autors ist in meiner Version nicht vollständig erhalten geblieben. Aber ich hoffe, ich habe es geschafft, die Stimmung und Gedanken des Artikels zu vermitteln.

Ich verstehe, dass Angular nicht das beliebteste Thema bei Habré ist, aber ich hoffe, dass die Übersetzung jemandem hilft, so wie mir der ursprüngliche Artikel einmal geholfen hat.

Das hat den Wow-Effekt im guten alten AngularJS verursacht, also ist es "Zwei-Wege-Bindung". Diese Magie verliebte sich sofort in AngularJS und brach alle Ideen über langweilige Seitenprogrammierung und (oh, Horror!) Webformulare. Änderungen an Daten werden sofort auf dem Bildschirm angezeigt und umgekehrt. Diejenigen, die zuvor jQuery-Anwendungen entwickelt hatten, empfanden das Verknüpfen als ein Märchen. Und bärtige Monster, die vor jQuery dicke Kunden sägten, begannen verzweifelt, die dumm verlorenen Mannmonate zu zählen.

Darüber hinaus war die Magie der bidirektionalen Bindung nicht nur für spezielle Notationen und ausgewählte Komponenten verfügbar. Wir könnten es leicht in unseren eigenen Anweisungen und Komponenten verwenden (nur durch Einstellen des Konfigurationsparameters).

In Angular2 + haben die Ersteller die integrierte bidirektionale Datenbindung aufgegeben (außer über ngModel). Dies bedeutet jedoch nicht, dass wir in unseren eigenen Anweisungen keine bidirektionale Bindung verwenden können. Es ist nur so, dass das Werbegeschenk vorbei ist und wir jetzt etwas selbst tun müssen. Und vorzugsweise mit einem Verständnis dafür, wie es in Angular funktioniert.

Inhaltsverzeichnis



Zwei-Wege-Bindung auf den Punkt gebracht


In A2 + implementiert nur eine einzige Direktive die bidirektionale Datenbindung: ngModel . Und auf den ersten Blick ist dies die gleiche Magie wie in AngularJS (nur in einer anderen Notation). Aber was ist unter der Haube?

Überraschenderweise ist unter der Haube alles relativ einfach und logisch: Die bidirektionale Bindung wird auf Eigenschaftsbindung und Ereignisbindung reduziert. Zwei einseitige Bindungen statt einer bilateralen? Ok, lass uns zwei.

Und sofort ein Beispiel:

<input [(ngModel)]="username"> <p>Hello {{username}}!</p> 

Ja, ja, dies ist eine wunderschöne und erstaunliche Angular2-Demo aus dem Jahr 2009. Kein Scherz, schön. Beim Ändern des Felds fällt der Benutzername in das Modell und wird sofort in der Begrüßungsnachricht auf dem Formular angezeigt.

Aber wie funktioniert es? Denken Sie daran, dass die bidirektionale Bindung in Angular2 eine Eigenschaftsbindung und eine Ereignisbindung ist. Und ja, sie können gleichzeitig in einer Richtlinie verfügbar sein. Darüber hinaus könnten wir auch ohne ngModel problemlos eine bidirektionale Datenbindung implementieren. Zum Beispiel so:

 <input [value]="username" (input)="username = $event.target.value"> <p>Hello {{username}}!</p> 

Die Ausgabe {{Benutzername}} ist klar, aber was steht dort in der Eingabe ? Lassen Sie uns verstehen:

  • [value] = "Benutzername" - Notation in eckigen Klammern, ordnet den Benutzernamenausdruck der value-Eigenschaft zu
  • (Eingabe) = "Ausdruck" - eine Notation in Klammern, der Ausdruck wird an das Eingabeereignis angehängt (ja, es gibt ein solches Ereignis). In unserem Fall:
    • Benutzername = $ event.target.value - Dieser Ausdruck wird als Antwort auf das Eingabeereignis ausgeführt
    • $ event ist eine synthetische Variable in Angular-Ereignissen, die eine Nutzlast enthält. In diesem Fall enthält sie Informationen über das Geschehen und die Umgebung

Wird es klarer? Wir reparieren es.

Wir binden die Benutzername- Eigenschaft des Angular-Modells an die value- Eigenschaft des Browser-Eingabeelements (Einwegbindung vom Modell zur Ansicht).

Wir binden auch einen Ausdruck an das Eingabeereignis unseres Elements. Womit der Benutzername- Eigenschaft des Modells der Wert von $ event.target.value zugewiesen wird .

Was ist $ event.target.value ? Wie bereits erwähnt, enthält $ event verschiedene nützliche Informationen zum Ereignis. In diesem Fall handelt es sich um ein InputEventObject, bei dem sich die Zieleigenschaft auf das DOM-Element bezieht, das das Ereignis ausgelöst hat (d. H. Unser Eingabeelement).

Alles, was wir im Wesentlichen tun, ist, den Inhalt ( Wert ) des Eingabeelements ( $ event.target ) zu lesen, wenn der Benutzer einen Wert eingibt. Wenn wir diesen Benutzernamenwert zuweisen, werden die Ansichtsdaten an das Modell gesendet.

Das ist alles. Dies ist "Zwei-Wege-Bindung auf den Punkt gebracht". Schönheit?

Aber wann kommt ngModel ins Spiel? Das Szenario der Arbeit mit Eingabeelementen ist sehr verbreitet und gefragt. Und aus irgendeinem Grund möchte ich eine Direktive haben, die die Implementierung verbirgt und vor zusätzlichen Tastenanschlägen schützt.

NgModel verstehen


Wenn Sie sich die Quelle ansehen, können Sie sicherstellen, dass ngModel auch eine Bindung zur Eigenschaft und zum Ereignis hat. So sieht unser ngModel-Beispiel aus, jedoch ohne Verwendung der Kurzsyntax:

 <input [ngModel]="username" (ngModelChange)="username = $event"> <p>Hello {{username}}!</p> 

Fast alles ist gleich. Durch die Bindung der Eigenschaft [ngModel] wird der Wert des Eingabeelements aktualisiert. Eine Ereignisbindung (ngModelChange) benachrichtigt die Welt, dass Änderungen im DOM auftreten.

Und Sie haben festgestellt, dass der Handlerausdruck nur $ event und nicht $ event.target.value verwendet . Stimmt hier etwas nicht? Überhaupt nicht. Wie oben erwähnt, ist $ event eine synthetische Variable, die eine Nutzlast trägt. Die Entscheidung darüber, was als nützlich erachtet wird, trifft Angular. Mit anderen Worten, ngModelChange kümmert sich um das Extrahieren von target.value aus dem internen $ -Ereignis und gibt uns einfach das, was wir wollen, ohne Verpackung und Tamburin. Um technisch korrekt zu sein, sind dies diejenigen von DefaultValueAccessor : Er ist es, der die Daten extrahiert und an das Basis-DOM-Objekt überträgt, obwohl ... Sie können einfach nicht darüber nachdenken).

Da das zweimalige Schreiben von Benutzername und ngModel immer noch redundant ist, ermöglicht Angular die Verwendung der abgekürzten Syntax [()] , die auch als „Banane in einer Box“ bezeichnet wird. Dies ähnelt dem vorherigen Beispiel und bringt uns zum Beispiel vom Anfang des Abschnitts zurück, jedoch mit einem Verständnis der ngModel- Implementierung. Bereitstellung der gleichen bidirektionalen Bindung.

 <input [(ngModel)]="username"> <p>Hello {{username}}!</p> 


Erstellen Sie Ihre eigenen bidirektionalen Datenbindungen


Jetzt wissen wir genug, um unsere eigenen bidirektionalen Datenbindungen zu erstellen. Alles, was Sie tun müssen, ist einfach die gleichen Regeln wie ngModel zu befolgen , nämlich:

  • Geben Sie eine Eigenschaftsbindung ein (zum Beispiel: [foo] )
  • Binden Sie an ein Ereignis mit demselben Namen und Suffix Ändern (zum Beispiel: (fooChange) )
  • Stellen Sie sicher, dass die Ereignisbindung das Abrufen der Eigenschaft übernimmt (falls erforderlich).

Beachten Sie, dass das Erstellen einer bidirektionalen Datenbindung erheblich mehr Arbeit erfordert als AngularJS? Dies könnte für uns sehr frustrierend sein ... Wenn wir versuchen würden, wo immer möglich unsere eigene Zwei-Wege-Bindung zu verwenden. Im wirklichen Leben sollten Sie immer überlegen, ob wir eine bidirektionale Bindung benötigen und ob es bei Bedarf einfacher ist, ngModel zu nutzen. Letzteres findet beispielsweise beim Erstellen benutzerdefinierter Formularsteuerelemente statt .

Angenommen, wir erstellen eine benutzerdefinierte Zählerkomponente (und möchten kein benutzerdefiniertes Formularsteuerelement verwenden).

 @Component({ selector: 'custom-counter', template: ` <button (click)="decrement()">-</button> <span>{{counter}}</span> <button (click)="increment()">+</button> ` }) export class CustomCounterComponent { counterValue = 0; get counter() { return this.counterValue; } set counter(value) { this.counterValue = value; } decrement() { this.counter--; } increment() { this.counter++; } } 

Wir haben die Eigenschaft der Zählerkomponente , den aktuellen Wert des Zählers anzuzeigen. Um es in beide Richtungen zu binden, müssen Sie es zunächst in einen Eingabeparameter umwandeln. Hierfür ist der Dekorator @Input () sehr nützlich:

 @Component() export class CustomCounterComponent { counterValue = 0; @Input() get counter() { return this.counterValue; } ... } 

Auf diese Weise können Sie die Komponenteneigenschaft bereits wie folgt an den Verbraucher binden:

 <custom-counter [counter]="someValue"></custom-counter> 

Jetzt müssen wir das Ereignis @Output () mit demselben Namen ( Zähler ) und dem Suffix Change setzen (es stellt sich heraus, counterChange). Wir möchten dieses Ereignis jedes Mal auslösen, wenn sich der Zähler ändert. Warum die Eigenschaft @Output () hinzufügen ? Und wir beenden in ein paar Schritten den Zählersetzer, in dem wir die Änderung des Wertes abfangen und das Ereignis mit dem aktuellen Zählerwert auswerfen:

 @Component() export class CustomCounterComponent { ... @Output() counterChange = new EventEmitter(); set counter(val) { this.counterValue = val; this.counterChange.emit(this.counterValue); } ... } 

Das ist es! Jetzt können wir den Ausdruck mithilfe der bidirektionalen Datenbindungssyntax an diese Eigenschaft binden:

 <custom-counter [(counter)]="someValue"></custom-counter> <p>counterValue = {{someValue}}</p> 

Schauen Sie sich die Demo an und probieren Sie es aus!

Denken Sie auch hier daran, dass eine Komponente wie ein benutzerdefinierter Zähler am besten mit einem benutzerdefinierten Formularsteuerelement implementiert werden kann, und nutzen Sie ngModel , um die bidirektionale Datenbindung zu implementieren, wie in diesem Artikel beschrieben .

Fazit


Angular verfügt nicht mehr über eine integrierte bidirektionale Datenbindung. Stattdessen enthält das Feld APIs, mit denen Sie die vollständige Bindung als Bindungseigenschaften und -ereignisse implementieren können.

ngModel ist eine in FormsModule integrierte bidirektionale Bindungsanweisung (denken Sie daran, sie dem Importabschnitt der @ NgModule- Deklaration hinzuzufügen : ca. per). Die Verknüpfung über ngModel sollte bevorzugt werden, wenn Komponenten erstellt werden, die als benutzerdefinierte Formularsteuerelemente dienen. Ansonsten hängt alles von Ihrer Vorstellungskraft ab.

PS vom Übersetzer: Die verbindliche Implementierung in A2 + ist moderner geworden. Jetzt werden fast "freie" Setter verwendet, um Änderungen durch "Feng Shui" zu überwachen (obwohl klar ist, dass die Mechanismen für die Schmutzprüfung zumindest für hochrangige Benutzerkomponenten bestehen bleiben). Dies ermöglichte es, 100.500 Beobachter aufzugeben (Verfahren zur Überwachung von Änderungen in „ihren“ Daten). Was in A1 gerne eine böswillige Belastung des Browsers verursachte und ungewöhnlich direkte Hände bei der Planung umfangreicher interaktiver Seiten erforderte.

Mit richtig gestalteten Komponenten reagiert A2 sofort besser. Lassen Sie auf Kosten der Arbeit der Programmierer. Jetzt können Sie eine Legion von Komponenten auf der Seite platzieren und müssen sich keine Gedanken mehr über Prozessorressourcen machen.

Die Kehrseite der Medaille waren die anfänglichen Kosten des "Einstiegsprozesses" in A2 +, die die Popularität des Frameworks beeinflussten. A1 hatte aber auch hohe Einstiegskosten, nur wurde es in die Major League verbannt. Aufgrund mangelnden Verständnisses für die Organisation großer Anwendungen sind viele Prototypen auf A1 „gestartet“, dann „zusammengebrochen“ und entsprachen React and Vue.

Ich hoffe, dass ich mit diesem Artikel dazu beitragen werde, die Schwelle für den ersten Zugang zu A2 +, der weiterhin gefragt ist (was ich aus erster Hand weiß), leicht zu senken.

Source: https://habr.com/ru/post/de453696/


All Articles