Mam element niestandardowy:
<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>
Kiedy próbuję dodać formControlName, pojawia się komunikat o błędzie:
BŁĄD Błąd: brak metody dostępu wartości dla kontrolki formularza o nazwie: „SurveyType”
Próbowałem dodać ngDefaultControl
bez powodzenia. Wydaje się, że to dlatego, że nie ma wejścia / wyboru ... i nie wiem, co robić.
Chciałbym powiązać moje kliknięcie z tym formControl, aby gdy ktoś kliknął na całą kartę, wypchnąłby mój „typ” do formControl. Czy to możliwe?
Odpowiedzi:
Możesz używać
formControlName
tylko w dyrektywach, które implementująControlValueAccessor
.Zaimplementuj interfejs
Aby więc robić to, co chcesz, musisz stworzyć komponent, który implementuje
ControlValueAccessor
, co oznacza implementację następujących trzech funkcji :writeValue
(informuje Angulara, jak zapisać wartość z modelu do widoku)registerOnChange
(rejestruje funkcję obsługi, która jest wywoływana, gdy zmienia się widok)registerOnTouched
(rejestruje procedurę obsługi, która ma zostać wywołana, gdy komponent otrzyma zdarzenie dotykowe, przydatne do określenia, czy komponent został skupiony).Zarejestruj dostawcę
Następnie musisz powiedzieć Angularowi, że ta dyrektywa to
ControlValueAccessor
(interfejs nie będzie jej wycinał, ponieważ jest usuwany z kodu, gdy TypeScript jest kompilowany do JavaScript). Robisz to rejestrując dostawcę .Dostawca powinien zapewnić
NG_VALUE_ACCESSOR
i wykorzystać istniejącą wartość . Będziesz także potrzebować plikuforwardRef
tutaj. Pamiętaj, żeNG_VALUE_ACCESSOR
powinien to być wielu dostawców .Na przykład, jeśli twoja niestandardowa dyrektywa nosi nazwę MyControlComponent, powinieneś dodać coś w następujących wierszach wewnątrz obiektu przekazanego do
@Component
dekoratora:Stosowanie
Twój komponent jest gotowy do użycia. Z form opartych na szablonach ,
ngModel
wiążąca będzie teraz działać poprawnie.Dzięki formularzom reaktywnym możesz teraz prawidłowo używać,
formControlName
a formant formularza będzie działał zgodnie z oczekiwaniami.Zasoby
źródło
Myślę, że powinieneś używać
formControlName="surveyType"
nainput
a nie nadiv
źródło
<ng-content>
komponentu opakowania i pozwolić komponentowi nadrzędnemu, który definiuje,formControls
po prostu umieścić <input> wewnątrz <wrapper>Błąd oznacza, że Angular nie wie, co zrobić, gdy postawisz
formControl
nadiv
. Aby to naprawić, masz dwie możliwości.formControlName
element na elemencie, który jest obsługiwany przez Angular po wyjęciu z pudełka. Są to:input
,textarea
iselect
.ControlValueAccessor
interfejs. W ten sposób mówisz Angularowi "jak uzyskać dostęp do wartości twojej kontrolki" (stąd nazwa). Lub w prostych słowach: co zrobić, gdy umieścisz elementformControlName
na elemencie, który nie ma naturalnie związanej z nim wartości.Teraz wdrożenie
ControlValueAccessor
interfejsu może być na początku nieco zniechęcające. Zwłaszcza, że nie ma tam dobrej dokumentacji na ten temat i musisz dodać dużo schematu do swojego kodu. Pozwólcie, że spróbuję to wyjaśnić w kilku prostych krokach.Przenieś kontrolkę formularza do jej własnego składnika
Aby zaimplementować
ControlValueAccessor
, musisz stworzyć nowy komponent (lub dyrektywę). Przenieś tam kod związany z formantem formularza. W ten sposób będzie również łatwo wielokrotnego użytku. Posiadanie kontrolki już wewnątrz komponentu może być przede wszystkim powodem, dla którego musisz zaimplementowaćControlValueAccessor
interfejs, ponieważ w przeciwnym razie nie będziesz mógł używać swojego komponentu niestandardowego razem z formularzami Angular.Dodaj szablon do swojego kodu
Implementacja
ControlValueAccessor
interfejsu jest dość rozwlekła, oto standardowy szablon, który z nim związany:Więc co robią poszczególne części?
ControlValueAccessor
interfejs został zaimplementowanyControlValueAccessor
interfejsonChange
ionTouch
jego własnej implementacji w czasie wykonywania, dzięki czemu możesz wywoływać te funkcje. Więc ten punkt jest ważny, aby zrozumieć: nie musisz samodzielnie implementować onChange i onTouch (poza początkową pustą implementacją). Jedyne, co robisz z (c), to pozwolić Angularowi dołączyć jego własne funkcje do twojej klasy. Czemu? Możesz więc zadzwonić pod numeronChange
ionTouch
metod przewidzianych przez kątowej w odpowiednim czasie. Zobaczymy, jak to działa poniżej.writeValue
następnej sekcji zobaczymy, jak działa ta metoda, kiedy ją zaimplementujemy. Umieściłem to tutaj, więc wszystkie wymagane właściwościControlValueAccessor
są zaimplementowane, a kod nadal się kompiluje.Zaimplementuj writeValue
Co
writeValue
robi, to zrobić coś wewnątrz komponentu niestandardowego, gdy kontrolka formularza zostanie zmieniona na zewnątrz . Na przykład, jeśli nazwałeś swój komponent kontroli formularza niestandardowegoapp-custom-input
i chciałbyś go używać w komponencie nadrzędnym w następujący sposób:następnie
writeValue
jest wyzwalany za każdym razem, gdy komponent nadrzędny w jakiś sposób zmienia wartośćmyFormControl
. Może to być na przykład podczas inicjowania formularza (this.form = this.formBuilder.group({myFormControl: ""});
) lub resetowania formularzathis.form.reset();
.Zwykle będziesz chciał zrobić, jeśli wartość kontrolki formularza zmieni się na zewnątrz, to zapisanie jej w zmiennej lokalnej, która reprezentuje wartość kontrolki formularza. Na przykład, jeśli
CustomInputComponent
obraca się wokół kontrolki formularza opartego na tekście, może to wyglądać następująco:oraz w html
CustomInputComponent
:Możesz również napisać go bezpośrednio do elementu wejściowego, jak opisano w dokumentacji Angular.
Teraz poradziłeś sobie z tym, co dzieje się wewnątrz komponentu, gdy coś zmienia się na zewnątrz. Spójrzmy teraz w innym kierunku. W jaki sposób informujesz świat zewnętrzny, gdy coś zmienia się w twoim elemencie?
Calling onChange
Następnym krokiem jest poinformowanie komponentu nadrzędnego o zmianach w twoim
CustomInputComponent
. W tym miejscu do gry wchodzą funkcjeonChange
ionTouch
z (c) z góry. Wywołując te funkcje, możesz informować zewnątrz o zmianach w twoim komponencie. Aby propagować zmiany wartości na zewnątrz, musisz wywołać onChange z nową wartością jako argumentem . Na przykład, jeśli użytkownik wpisze coś winput
polu komponentu niestandardowego, wywołaszonChange
zaktualizowaną wartość:Jeśli ponownie sprawdzisz implementację (c) z góry, zobaczysz, co się dzieje: Angular powiązał swoją własną implementację z
onChange
właściwością klasy. Ta implementacja oczekuje jednego argumentu, który jest zaktualizowaną wartością kontrolną. To, co teraz robisz, to wywołanie tej metody, a tym samym poinformowanie Angulara o zmianie. Angular przejdzie teraz dalej i zmieni wartość formularza na zewnątrz. To jest kluczowa część tego wszystkiego. Poinformowałeś Angular, kiedy powinien zaktualizować kontrolkę formularza i jaką wartość, wywołująconChange
. Dałeś mu sposób „dostępu do wartości kontrolnej”.Przy okazji: nazwa
onChange
jest wybrana przeze mnie. Możesz tu wybrać cokolwiek, na przykładpropagateChange
lub coś podobnego. Jednak jakkolwiek ją nazwiesz, będzie to ta sama funkcja, która pobiera jeden argument, który jest dostarczany przez Angular i który jest powiązany z twoją klasą przezregisterOnChange
metodę w czasie wykonywania.Wywołanie onTouch
Ponieważ kontrolki formularza można „dotykać”, należy również dać Angularowi środki do zrozumienia, kiedy dotknięta zostanie niestandardowa kontrolka formularza. Możesz to zrobić, zgadłeś, wywołując
onTouch
funkcję. Więc w naszym przykładzie tutaj, jeśli chcesz zachować zgodność z tym, jak Angular robi to dla gotowych kontrolek formularza, powinieneś zadzwonić,onTouch
gdy pole wejściowe jest rozmyte:Ponownie,
onTouch
to nazwa wybrana przeze mnie, ale to, jaka jest jego rzeczywista funkcja, jest dostarczana przez Angular i nie przyjmuje żadnych argumentów. Co ma sens, ponieważ po prostu informujesz Angulara, że kontrolka formularza została dotknięta.Kładąc wszystko razem
Jak to wygląda, gdy wszystko idzie razem? To powinno wyglądać tak:
Więcej przykładów
Zagnieżdżone formularze
Należy zauważyć, że Accessors wartości kontrolnych NIE są odpowiednim narzędziem dla zagnieżdżonych grup formularzy. W przypadku zagnieżdżonych grup formularzy możesz po prostu użyć
@Input() subform
zamiast tego. Accessors wartości kontrolnych mają na celu zawijaniecontrols
, a niegroups
! Zobacz ten przykład, jak używać danych wejściowych dla zagnieżdżonego formularza: https://stackblitz.com/edit/angular-nested-forms-input-2Źródła
źródło
Dla mnie było to spowodowane atrybutem „multiple” w kontrolce wyboru wejścia, ponieważ Angular ma inny ValueAccessor dla tego typu kontroli.
A wewnątrz szablonu użyj w ten sposób
Więcej szczegółów w Official Docs
źródło