Ich habe ein benutzerdefiniertes Element:
<div formControlName="surveyType">
<div *ngFor="let type of surveyTypes"
(click)="onSelectType(type)"
[class.selected]="type === selectedType">
<md-icon>{{ type.icon }}</md-icon>
<span>{{ type.description }}</span>
</div>
</div>
Wenn ich versuche, den formControlName hinzuzufügen, wird eine Fehlermeldung angezeigt:
FEHLER Fehler: Kein Wertzugriff für die Formularsteuerung mit dem Namen: 'queryType'
Ich habe versucht, ngDefaultControl
ohne Erfolg hinzuzufügen . Es scheint, weil es keine Eingabe / Auswahl gibt ... und ich weiß nicht, was ich tun soll.
Ich möchte meinen Klick an dieses formControl binden, damit, wenn jemand auf die gesamte Karte klickt, mein 'Typ' in das formControl verschoben wird. Ist es möglich?
Antworten:
Sie können
formControlName
nur für Direktiven verwenden, die implementierenControlValueAccessor
.Implementieren Sie die Schnittstelle
Um das zu tun, was Sie wollen, müssen Sie eine Komponente erstellen, die implementiert wird
ControlValueAccessor
, was bedeutet , dass die folgenden drei Funktionen implementiert werden :writeValue
(teilt Angular mit, wie der Wert aus dem Modell in die Ansicht geschrieben werden soll)registerOnChange
(registriert eine Handlerfunktion, die aufgerufen wird, wenn sich die Ansicht ändert)registerOnTouched
(Registriert einen Handler, der aufgerufen werden soll, wenn die Komponente ein Berührungsereignis empfängt. Dies ist hilfreich, um festzustellen, ob die Komponente fokussiert wurde.)Registrieren Sie einen Anbieter
Dann müssen Sie Angular mitteilen, dass es sich bei dieser Direktive um eine handelt
ControlValueAccessor
(die Schnittstelle wird sie nicht ausschneiden, da sie beim Kompilieren von TypeScript zu JavaScript aus dem Code entfernt wird). Sie tun dies, indem Sie einen Anbieter registrieren .Der Anbieter sollte einen vorhandenen Wert bereitstellen
NG_VALUE_ACCESSOR
und verwenden . Du brauchst auch einforwardRef
hier. Beachten Sie, dassNG_VALUE_ACCESSOR
dies ein Multi-Provider sein sollte .Wenn Ihre benutzerdefinierte Direktive beispielsweise MyControlComponent heißt, sollten Sie innerhalb des an den
@Component
Dekorateur übergebenen Objekts etwas in der folgenden Richtung hinzufügen :Verwendung
Ihre Komponente ist einsatzbereit. Mit Vorlagen basierenden Formen ,
ngModel
Bindung wird nun korrekt funktionieren.Mit reaktiven Formularen können Sie diese jetzt ordnungsgemäß verwenden
formControlName
und das Formularsteuerelement verhält sich wie erwartet.Ressourcen
quelle
Ich denke du solltest
formControlName="surveyType"
auf eineminput
und nicht auf einem verwendendiv
quelle
<ng-content>
eine Wrapper-Komponente verwenden und die übergeordnete Komponente, die definiert,formControls
einfach den <Eingang> in den <Wrapper> einfügenDer Fehler bedeutet, dass Angular nicht weiß, was zu tun ist, wenn Sie eine
formControl
auf eine setzendiv
. Um dies zu beheben, haben Sie zwei Möglichkeiten.formControlName
auf ein Element, das von Angular unterstützt wird, sofort ein. Das sind:input
,textarea
undselect
.ControlValueAccessor
Schnittstelle. Auf diese Weise teilen Sie Angular mit, "wie Sie auf den Wert Ihres Steuerelements zugreifen können" (daher der Name). Oder in einfachen Worten: Was tun, wenn SieformControlName
ein Element mit einem Wert versehen, dem natürlich kein Wert zugeordnet ist ?Jetzt kann die Implementierung der
ControlValueAccessor
Schnittstelle zunächst etwas entmutigend sein. Vor allem, weil es nicht viel gute Dokumentation dafür gibt und Sie Ihrem Code viel Boilerplate hinzufügen müssen. Lassen Sie mich versuchen, dies in einfach zu befolgenden Schritten aufzuschlüsseln.Verschieben Sie Ihr Formularsteuerelement in eine eigene Komponente
Um das zu implementieren
ControlValueAccessor
, müssen Sie eine neue Komponente (oder Direktive) erstellen. Verschieben Sie den Code für Ihr Formularsteuerelement dorthin. So wird es auch leicht wiederverwendbar sein. Ein Steuerelement bereits in einer Komponente zu haben, könnte in erster Linie der Grund sein, warum Sie dieControlValueAccessor
Schnittstelle implementieren müssen , da Sie Ihre benutzerdefinierte Komponente sonst nicht zusammen mit Angular-Formularen verwenden können.Fügen Sie das Boilerplate Ihrem Code hinzu
Die Implementierung der
ControlValueAccessor
Schnittstelle ist recht ausführlich. Hier ist das mitgelieferte Boilerplate:Was machen die einzelnen Teile?
ControlValueAccessor
Schnittstelle implementiert habenControlValueAccessor
Schnittstelle implementierenonChange
undonTouch
ihre eigene Implementierung zur Laufzeit zu überschreiben , sodass Sie diese Funktionen dann aufrufen können. Daher ist dieser Punkt wichtig zu verstehen: Sie müssen onChange und onTouch nicht selbst implementieren (außer der anfänglichen leeren Implementierung). Das einzige, was Sie mit (c) tun, ist, Angular seine eigenen Funktionen an Ihre Klasse anhängen zu lassen. Warum? Sie können dann die von Angular bereitgestellten Methoden und zum entsprechenden Zeitpunkt aufrufen . Wir werden unten sehen, wie das funktioniert.onChange
onTouch
writeValue
im nächsten Abschnitt sehen, wie die Methode funktioniert, wenn wir sie implementieren. Ich habe es hier eingefügt, damit alle erforderlichen EigenschaftenControlValueAccessor
implementiert werden und Ihr Code weiterhin kompiliert wird.Implementieren Sie writeValue
Was Sie tun
writeValue
müssen, ist, etwas in Ihrer benutzerdefinierten Komponente zu tun, wenn das Formularsteuerelement außen geändert wird . Wenn Sie beispielsweise Ihre benutzerdefinierte Formularsteuerungskomponente benannt habenapp-custom-input
und diese in der übergeordneten Komponente wie folgt verwenden würden:wird dann
writeValue
ausgelöst, wenn die übergeordnete Komponente den Wert von irgendwie ändertmyFormControl
. Dies kann beispielsweise während der Initialisierung des Formulars (this.form = this.formBuilder.group({myFormControl: ""});
) oder beim Zurücksetzen eines Formulars geschehenthis.form.reset();
.Wenn sich der Wert des Formularsteuerelements äußerlich ändert, möchten Sie ihn normalerweise in eine lokale Variable schreiben, die den Wert des Formularsteuerelements darstellt. Wenn Sie
CustomInputComponent
sich beispielsweise um ein textbasiertes Formularsteuerelement drehen, könnte dies folgendermaßen aussehen:und im HTML von
CustomInputComponent
:Sie können es auch direkt in das Eingabeelement schreiben, wie in den Angular-Dokumenten beschrieben.
Jetzt haben Sie behandelt, was innerhalb Ihrer Komponente passiert, wenn sich etwas außerhalb ändert. Schauen wir uns jetzt die andere Richtung an. Wie informieren Sie die Außenwelt, wenn sich innerhalb Ihrer Komponente etwas ändert?
OnChange aufrufen
Der nächste Schritt besteht darin, die übergeordnete Komponente über Änderungen in Ihrem Computer zu informieren
CustomInputComponent
. Hier kommen dieonChange
undonTouch
Funktionen von (c) von oben ins Spiel. Durch Aufrufen dieser Funktionen können Sie die Außenwelt über Änderungen innerhalb Ihrer Komponente informieren. Um Änderungen des Werts nach außen zu übertragen, müssen Sie onChange mit dem neuen Wert als Argument aufrufen . Wenn der Benutzer beispielsweiseinput
in Ihrer benutzerdefinierten Komponente etwas in das Feld eingibt, rufen SieonChange
mit dem aktualisierten Wert auf:Wenn Sie die Implementierung (c) erneut von oben überprüfen, werden Sie sehen, was passiert: Angular hat seine eigene Implementierung an die
onChange
class -Eigenschaft gebunden . Diese Implementierung erwartet ein Argument, nämlich den aktualisierten Steuerwert. Was Sie jetzt tun, ist, dass Sie diese Methode aufrufen und Angular über die Änderung informieren. Angular ändert nun den Formularwert an der Außenseite. Dies ist der Schlüssel zu all dem. Sie haben Angular durch Aufrufen mitgeteilt, wann und mit welchem Wert das Formularsteuerelement aktualisiert werden sollonChange
. Sie haben ihm die Möglichkeit gegeben, "auf den Steuerwert zuzugreifen".Übrigens: Der Name
onChange
wird von mir gewählt. Sie können hier beispielsweise allespropagateChange
oder ähnliches auswählen . Wie auch immer Sie es nennen, es ist dieselbe Funktion, die ein Argument akzeptiert, das von Angular bereitgestellt wird und das zurregisterOnChange
Laufzeit von der Methode an Ihre Klasse gebunden wird .OnTouch anrufen
Da Formularsteuerelemente "berührt" werden können, sollten Sie Angular auch die Möglichkeit geben, zu verstehen, wann Ihr benutzerdefiniertes Formularsteuerelement berührt wird. Sie können es tun, Sie haben es erraten, indem Sie die
onTouch
Funktion aufrufen . Wenn Sie in unserem Beispiel hier die Anforderungen von Angular für die sofort einsatzbereiten Formularsteuerelemente erfüllen möchten, sollten Sie Folgendes aufrufen,onTouch
wenn das Eingabefeld unscharf ist:Nochmal,
onTouch
ist ein Name von mir gewählt, aber was seine eigentliche Funktion ist, wird von Angular bereitgestellt und es werden keine Argumente verwendet. Was Sinn macht, da Sie Angular nur wissen lassen, dass das Formularsteuerelement berührt wurde.Alles zusammenfügen
Wie sieht das aus, wenn alles zusammenkommt? Es sollte so aussehen:
Mehr Beispiele
Verschachtelte Formulare
Beachten Sie, dass Control Value Accessors NICHT das richtige Werkzeug für verschachtelte Formulargruppen sind. Für verschachtelte Formulargruppen können Sie
@Input() subform
stattdessen einfach ein verwenden. Control Value Accessors sollen einwickelncontrols
, nichtgroups
! In diesem Beispiel wird gezeigt, wie eine Eingabe für ein verschachteltes Formular verwendet wird: https://stackblitz.com/edit/angular-nested-forms-input-2Quellen
quelle
Für mich war dies auf das Attribut "multiple" bei der Auswahl der Eingabesteuerung zurückzuführen, da Angular für diesen Steuerungstyp einen anderen ValueAccessor hat.
Und innerhalb der Vorlage verwenden Sie so
Weitere Details finden Sie unter Offizielle Dokumente
quelle