Wie kann ich eine benutzerdefinierte Komponente erstellen, die genau wie ein natives <input>
Tag funktioniert ? Ich möchte, dass mein benutzerdefiniertes Formularsteuerelement ngControl, ngForm, [(ngModel)] unterstützt.
Soweit ich weiß, muss ich einige Schnittstellen implementieren, damit meine eigene Formularsteuerung genauso funktioniert wie die native.
Außerdem scheint die ngForm-Direktive nur für <input>
Tags zu binden. Ist das richtig? Wie kann ich damit umgehen?
Lassen Sie mich erklären, warum ich das überhaupt brauche. Ich möchte mehrere Eingabeelemente umschließen, damit sie als eine einzige Eingabe zusammenarbeiten können. Gibt es eine andere Möglichkeit, damit umzugehen? Noch einmal: Ich möchte dieses Steuerelement genauso wie das native steuern. Validierung, ngForm, ngModel Zwei-Wege-Bindung und andere.
ps: Ich benutze Typescript.
quelle
Antworten:
In der Tat gibt es zwei Dinge zu implementieren:
ngModel
selbst bereitgestellt wirdControlValueAccessor
, der die Brücke zwischen dieser Komponente undngModel
/ implementiertngControl
Nehmen wir eine Probe. Ich möchte eine Komponente implementieren, die eine Liste von Tags für ein Unternehmen verwaltet. Die Komponente ermöglicht das Hinzufügen und Entfernen von Tags. Ich möchte eine Validierung hinzufügen, um sicherzustellen, dass die Tag-Liste nicht leer ist. Ich werde es in meiner Komponente wie unten beschrieben definieren:
Die
TagsComponent
Komponente definiert die Logik zum Hinzufügen und Entfernen von Elementen in dertags
Liste.Wie Sie sehen können, gibt es in dieser Komponente keine Eingabe, sondern eine
setValue
Eins (der Name ist hier nicht wichtig). Wir verwenden es später, um den Wert von derngModel
zur Komponente bereitzustellen . Diese Komponente definiert ein Ereignis, das benachrichtigt werden soll, wenn der Status der Komponente (die Tags-Liste) aktualisiert wird.Lassen Sie uns nun die Verknüpfung zwischen dieser Komponente und
ngModel
/ implementierenngControl
. Dies entspricht einer Direktive, die dieControlValueAccessor
Schnittstelle implementiert . Für diesen Wert-Accessor muss ein Provider für dasNG_VALUE_ACCESSOR
Token definiert werden (vergessen Sie nicht, ihn zu verwenden,forwardRef
da die Direktive danach definiert ist).Die Direktive fügt dem
tagsChange
Ereignis des Hosts einen Ereignis-Listener hinzu (dh die Komponente, an die die Direktive angehängt ist, dh dieTagsComponent
). DieonChange
Methode wird aufgerufen, wenn das Ereignis eintritt. Diese Methode entspricht der von Angular2 registrierten. Auf diese Weise werden Änderungen erkannt und das zugehörige Formularsteuerelement entsprechend aktualisiert.Das
writeValue
wird aufgerufen, wenn der in gebundene WertngForm
aktualisiert wird. Nachdem Sie die angehängte Komponente (dh TagsComponent) eingefügt haben, können Sie sie aufrufen, um diesen Wert zu übergeben (siehe vorherigesetValue
Methode).Vergessen Sie nicht, die
CUSTOM_VALUE_ACCESSOR
in den Bindungen der Richtlinie anzugeben.Hier ist der vollständige Code des Brauchs
ControlValueAccessor
:Auf diese Weise
tags
wird dasvalid
Attribut descompanyForm.controls.tags
Steuerelementsfalse
automatisch , wenn ich das gesamte Unternehmen entferne .Weitere Informationen finden Sie in diesem Artikel (Abschnitt "NgModel-kompatible Komponente"):
quelle
<textfield>
,<dropdown>
? Ist das "eckig"?Ich verstehe nicht, warum jedes Beispiel, das ich im Internet finde, so kompliziert sein muss. Wenn ich ein neues Konzept erkläre, denke ich, ist es immer am besten, ein möglichst einfaches und funktionierendes Beispiel zu haben. Ich habe es ein wenig destilliert:
HTML für externes Formular mit Komponente, die ngModel implementiert:
In sich geschlossene Komponente (keine separate 'Accessor'-Klasse - vielleicht fehlt mir der Punkt):
Tatsächlich habe ich all diese Dinge gerade zu einer abstrakten Klasse abstrahiert, die ich jetzt mit jeder Komponente erweitere, die ich zur Verwendung von ngModel benötige. Für mich ist dies eine Menge Overhead- und Boilerplate-Code, auf den ich verzichten kann.
Edit: Hier ist es:
Hier ist eine Komponente, die es verwendet: (TS):
HTML:
quelle
@angular/forms
aktualisieren Sie einfach die Importe:import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
CORE_DIRECTIVES
und hinzufügen,@Component
da sie seit Angular2 final standardmäßig bereitgestellt werden. Laut meiner IDE müssen "Konstruktoren für abgeleitete Klassen einen 'Super'-Aufruf enthalten.", Daher musstesuper();
ich den Konstruktor meiner Komponente ergänzen .In diesem Link gibt es ein Beispiel für die RC5-Version: http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel
Wir können dieses benutzerdefinierte Steuerelement dann wie folgt verwenden:
quelle
Thierrys Beispiel ist hilfreich. Hier sind die Importe, die für die Ausführung von TagsValueAccessor erforderlich sind ...
quelle
Ich habe eine Bibliothek geschrieben, die hilft, einige Boilerplates für diesen Fall zu reduzieren :
s-ng-utils
. Einige der anderen Antworten geben ein Beispiel für das Umschließen eines einzelnen Formularsteuerelements. Diess-ng-utils
kann sehr einfach erfolgen mitWrappedFormControlSuperclass
:In Ihrem Beitrag erwähnen Sie, dass Sie mehrere Formularsteuerelemente in eine einzelne Komponente einbinden möchten. Hier ist ein vollständiges Beispiel dafür
FormControlSuperclass
.Anschließend können Sie
<app-location>
mit[(ngModel)]
,[formControl]
, benutzerdefinierte Validatoren - alles , was Sie mit den Kontrollen Winkelstützen aus dem Kasten heraus tun.quelle
Sie können dies auch mit einer @ ViewChild-Direktive lösen. Dies gibt dem Elternteil vollen Zugriff auf alle Mitgliedsvariablen und Funktionen eines injizierten Kindes.
Siehe: Zugriff auf Eingabefelder der injizierten Formularkomponente
quelle
Warum einen neuen Wert-Accessor erstellen, wenn Sie das innere ngModel verwenden können? Wenn Sie eine benutzerdefinierte Komponente erstellen, die eine Eingabe [ngModel] enthält, instanziieren wir bereits einen ControlValueAccessor. Und das ist der Accessor, den wir brauchen.
Vorlage:
Komponente:
Benutzen als:
quelle
innerNgModel
ist inngAfterViewInit
Dies ist recht einfach zu tun
ControlValueAccessor
NG_VALUE_ACCESSOR
.In diesem Artikel können Sie ein einfaches benutzerdefiniertes Feld erstellen. Erstellen Sie eine benutzerdefinierte Eingabefeldkomponente mit Winkel
quelle