Hata, Angular'ın bir formControla koyduğunuzda ne yapacağını bilmediği anlamına gelir div. Bunu düzeltmek için iki seçeneğiniz vardır.
formControlNameAçısal tarafından desteklenen bir öğeyi kutunun dışına koyarsınız . Bunlar şunlardır: input, textareave select.
ControlValueAccessorArayü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: formControlNameBir öğeyi koyduğunuzda, bunun doğal olarak kendisiyle ilişkilendirilmiş bir değeri olmayan ne yapmalı .
Şimdi, ControlValueAccessorarayü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 ControlValueAccessoryeni 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 ControlValueAccessorarayüzü uygulamanız gerekir , aksi takdirde özel bileşeninizi Açısal formlarla birlikte kullanamazsınız.
Isıtıcı plakayı kodunuza ekleyin
Uygulama ControlValueAccessorarayü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
ControlValueAccessorarayüzü uyguladığınızı açısal olarak bilmenizi sağlar
- b)
ControlValueAccessorArayü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
onChangeve 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 onTouchuygun zamanda açısal tarafından sağlanan yöntemler. Bunun nasıl çalıştığını aşağıda göreceğiz.
- d)
writeValueYö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 ControlValueAccessoruygulanır ve kod hala derler.
WriteValue uygulayın
Ne writeValueyapar, 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-inputve bunu üst bileşende şu şekilde kullanıyorsanız:
<form [formGroup]="form">
<app-custom-input formControlName="myFormControl"></app-custom-input>
</form>
Daha sonra writeValueana 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, CustomInputComponentmetin 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 onChangeve onTouchiş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, onChangegü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 onChangebenim 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. onTouchFonksiyonu ç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, onTouchbenim 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() subformbunun 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