Hata, Angular'ın bir formControl
a koyduğunuzda ne yapacağını bilmediği anlamına gelir div
. Bunu düzeltmek için iki seçeneğiniz vardır.
formControlName
Açısal tarafından desteklenen bir öğeyi kutunun dışına koyarsınız . Bunlar şunlardır: input
, textarea
ve select
.
ControlValueAccessor
Arayüzü uygularsınız . Böylece Angular'a "kontrolünüzün değerine nasıl erişeceğinizi" (dolayısıyla adı) söylüyorsunuz. Veya basit terimlerle: formControlName
Bir öğeyi koyduğunuzda, bunun doğal olarak kendisiyle ilişkilendirilmiş bir değeri olmayan ne yapmalı .
Şimdi, ControlValueAccessor
arayüzü uygulamak ilk başta biraz göz korkutucu olabilir. Özellikle bununla ilgili çok iyi bir dokümantasyon olmadığından ve kodunuza çok fazla kaynak plakası eklemeniz gerektiğinden. Bunu izlemesi gereken basit adımlarla yıkmaya çalışayım.
Form denetiminizi kendi bileşenine taşıma
Uygulamasını uygulamak için ControlValueAccessor
yeni bir bileşen (veya yönerge) oluşturmanız gerekir. Form denetiminizle ilgili kodu oraya taşıyın. Bu şekilde kolayca tekrar kullanılabilir. Bir bileşenin içinde zaten bir kontrole sahip olmak ilk etapta neden olabilir, neden ControlValueAccessor
arayüzü uygulamanız gerekir , aksi takdirde özel bileşeninizi Açısal formlarla birlikte kullanamazsınız.
Isıtıcı plakayı kodunuza ekleyin
Uygulama ControlValueAccessor
arayüzü burada onunla birlikte Demirbaş var oldukça ayrıntılı geçerli:
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
// a) copy paste this providers property (adjust the component name in the forward ref)
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
// b) Add "implements ControlValueAccessor"
export class CustomInputComponent implements ControlValueAccessor {
// c) copy paste this code
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// d) copy paste this code
writeValue(input: string) {
// TODO
}
Peki münferit parçalar ne yapıyor?
- a) Çalışma zamanında
ControlValueAccessor
arayüzü uyguladığınızı açısal olarak bilmenizi sağlar
- b)
ControlValueAccessor
Arayüzü uyguladığınızdan emin olur
- c) Bu muhtemelen en kafa karıştırıcı kısımdır. Temelde yaptığınız şey, Angular'a sınıf özelliklerinizi / yöntemlerinizi
onChange
ve onTouch
çalışma sırasında kendi uygulamasıyla geçersiz kılma araçlarını verirsiniz, böylece bu işlevleri çağırabilirsiniz. Bu noktayı anlamak önemlidir: onChange ve onTouch'u kendiniz uygulamanız gerekmez (ilk boş uygulama dışında). (C) ile yaptığınız tek şey Angular'ın kendi işlevlerini sınıfınıza eklemesine izin vermektir. Neden? O zaman siz yapabilirsiniz çağırıronChange
ve onTouch
uygun zamanda açısal tarafından sağlanan yöntemler. Bunun nasıl çalıştığını aşağıda göreceğiz.
- d)
writeValue
Yöntemin uygulandığında sonraki bölümde nasıl çalıştığını da göreceğiz . Ben buraya koydum, böylece tüm gerekli özellikleri ControlValueAccessor
uygulanır ve kod hala derler.
WriteValue uygulayın
Ne writeValue
yapar, form denetimi dışarıdan değiştirildiğinde özel bileşeninizin içinde bir şey yapmaktır . Örneğin, özel form denetim bileşeninizi adlandırdıysanız app-custom-input
ve bunu üst bileşende şu şekilde kullanıyorsanız:
<form [formGroup]="form">
<app-custom-input formControlName="myFormControl"></app-custom-input>
</form>
Daha sonra writeValue
ana bileşeni nasılsa değerini her değiştirdiğinde tetiklenen alır myFormControl
. Bu, örneğin formun ( this.form = this.formBuilder.group({myFormControl: ""});
) başlatılması sırasında veya formun sıfırlanması sırasında olabilir this.form.reset();
.
Form denetiminin değeri dışarıdan değişirse, genellikle yapmak isteyeceğiniz şey, form denetim değerini temsil eden yerel bir değişkene yazmaktır. Örneğin, CustomInputComponent
metin tabanlı bir form denetimi etrafında dönerse, şöyle görünebilir:
writeValue(input: string) {
this.input = input;
}
ve html'de CustomInputComponent
:
<input type="text"
[ngModel]="input">
Açısal belgelerde açıklandığı gibi doğrudan giriş öğesine de yazabilirsiniz.
Şimdi dışarıda bir şey değiştiğinde bileşeninizin içinde ne olduğunu ele aldınız. Şimdi diğer yöne bakalım. Bileşeninizin içinde bir şey değiştiğinde dış dünyayı nasıl bilgilendirirsiniz?
Call on onDeğiştir
Bir sonraki adım, ana bileşeni, cihazınızdaki değişiklikler hakkında bilgilendirmektir CustomInputComponent
. Burada (c) 'den gelen onChange
ve onTouch
işlevleri devreye girer. Bu işlevleri çağırarak, bileşeninizdeki değişiklikler hakkında dışarıdan bilgi alabilirsiniz. Değer değişikliklerini dışarıya yaymak için bağımsız değişken olarak onChange öğesini yeni değerle çağırmanız gerekir . Örneğin, kullanıcı input
özel bileşeninizdeki alana bir şey yazarsa, onChange
güncellenmiş değerle çağırırsınız :
<input type="text"
[ngModel]="input"
(ngModelChange)="onChange($event)">
Uygulamayı (c) yukarıdan tekrar kontrol ederseniz, neler olduğunu göreceksiniz: Açısal, kendi onChange
özelliğinin class özelliğine bağlı olması . Bu uygulama, güncellenmiş denetim değeri olan bir bağımsız değişken bekler. Şu anda yaptığınız şey, bu yöntemi çağırmak ve böylece Angular'a değişiklik hakkında bilgi vermek. Açısal şimdi devam edecek ve dışarıdaki form değerini değiştirecektir. Bütün bunların kilit kısmı budur. Angular'a form denetimini ne zaman güncellemesi gerektiğini ve çağırarak hangi değere sahip olduğunu söyledinizonChange
. "Kontrol değerine erişim" için araçlar verdiniz.
Bu arada: İsim onChange
benim tarafımdan seçildi. Burada herhangi bir şey seçebilirsiniz, örneğin propagateChange
. Bununla birlikte, bunu adlandırsanız da, Angular tarafından sağlanan ve registerOnChange
çalışma zamanı sırasında yöntem tarafından sınıfınıza bağlı olan, bir bağımsız değişken alan aynı işlev olacaktır .
Dokunulduğunda
Form denetimlerine "dokunabildiğiniz" için, Angular'a özel form denetiminize ne zaman dokunulduğunu anlamanız için araçlar vermelisiniz. onTouch
Fonksiyonu çağırarak yapabilirsiniz, tahmin ettiniz . Buradaki örneğimiz için, Angular'ın kutudan çıkmış form kontrolleri için bunu nasıl yaptığına uygun kalmak istiyorsanız onTouch
, giriş alanı bulanık olduğunda aramalısınız :
<input type="text"
[(ngModel)]="input"
(ngModelChange)="onChange($event)"
(blur)="onTouch()">
Yine, onTouch
benim tarafımdan seçilen bir isim, ancak gerçek işlevi Angular tarafından sağlanır ve sıfır argüman alır. Bu, Angular'a izin verdiğiniz için, form kontrolüne dokunulduğunu anlamlıdır.
Hepsini bir araya koy
Peki hepsi bir araya geldiğinde bu nasıl görünüyor? Şöyle görünmelidir:
// custom-input.component.ts
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
// Step 1: copy paste this providers property
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
// Step 2: Add "implements ControlValueAccessor"
export class CustomInputComponent implements ControlValueAccessor {
// Step 3: Copy paste this stuff here
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// Step 4: Define what should happen in this component, if something changes outside
input: string;
writeValue(input: string) {
this.input = input;
}
// Step 5: Handle what should happen on the outside, if something changes on the inside
// in this simple case, we've handled all of that in the .html
// a) we've bound to the local variable with ngModel
// b) we emit to the ouside by calling onChange on ngModelChange
}
// custom-input.component.html
<input type="text"
[(ngModel)]="input"
(ngModelChange)="onChange($event)"
(blur)="onTouch()">
// parent.component.html
<app-custom-input [formControl]="inputTwo"></app-custom-input>
// OR
<form [formGroup]="form" >
<app-custom-input formControlName="myFormControl"></app-custom-input>
</form>
Daha fazla örnek
İç İçe Formlar
Denetim Değeri Erişimcileri iç içe form grupları için doğru araç DEĞİLDİR. Yuvalanmış form grupları için @Input() subform
bunun yerine basitçe kullanabilirsiniz . Kontrol Değeri Erişimcileri sarmak içindir controls
, değil groups
! Yuvalanmış form için bir girdinin nasıl kullanılacağına ilişkin bu örneğe bakın: https://stackblitz.com/edit/angular-nested-forms-input-2
Kaynaklar