[ngDefaultControl]
Kontrol pihak ketiga membutuhkan a ControlValueAccessor
berfungsi dengan bentuk sudut. Banyak dari mereka, seperti Polymer <paper-input>
, berperilaku seperti <input>
elemen asli dan dengan demikian dapat menggunakan DefaultValueAccessor
. Menambahkan ngDefaultControl
atribut akan memungkinkan mereka menggunakan arahan itu.
<paper-input ngDefaultControl [(ngModel)]="value>
atau
<paper-input ngDefaultControl formControlName="name">
Jadi inilah alasan utama mengapa atribut ini diperkenalkan.
Itu disebut ng-default-control
atribut dalam versi alfa angular2 .
Jadi ngDefaultControl
adalah salah satu pemilih untuk direktif DefaultValueAccessor :
@Directive({
selector:
'input:not([type=checkbox])[formControlName],
textarea[formControlName],
input:not([type=checkbox])[formControl],
textarea[formControl],
input:not([type=checkbox])[ngModel],
textarea[ngModel],
[ngDefaultControl]', <------------------------------- this selector
...
})
export class DefaultValueAccessor implements ControlValueAccessor {
Apa artinya?
Artinya kita dapat menerapkan atribut ini ke elemen (seperti komponen polimer) yang tidak memiliki pengakses nilainya sendiri. Jadi elemen ini akan mengambil perilaku DefaultValueAccessor
dan kita dapat menggunakan elemen ini dengan bentuk sudut.
Jika tidak, Anda harus menyediakan implementasi Anda sendiri ControlValueAccessor
ControlValueAccessor
Status dokumen sudut
ControlValueAccessor bertindak sebagai jembatan antara Angular forms API dan elemen asli di DOM.
Mari tulis template berikut dalam aplikasi angular2 sederhana:
<input type="text" [(ngModel)]="userName">
Untuk memahami bagaimana input
perilaku kita di atas, kita perlu mengetahui arahan mana yang diterapkan pada elemen ini. Di sini angular memberikan beberapa petunjuk dengan kesalahan:
Penolakan Janji Tak Tertangani: Kesalahan penguraian template: Tidak dapat mengikat ke 'ngModel' karena bukan properti 'input' yang diketahui.
Oke, kita bisa buka SO dan dapatkan jawabannya: import FormsModule
to your @NgModule
:
@NgModule({
imports: [
...,
FormsModule
]
})
export AppModule {}
Kami mengimpornya dan semua bekerja sebagaimana mestinya. Tapi apa yang terjadi di balik terpal?
FormsModule mengekspor untuk kita arahan berikut:
@NgModule({
...
exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}
Setelah beberapa penyelidikan kita dapat menemukan bahwa tiga arahan akan diterapkan pada kita input
1) NgControlStatus
@Directive({
selector: '[formControlName],[ngModel],[formControl]',
...
})
export class NgControlStatus extends AbstractControlStatus {
...
}
2) NgModel
@Directive({
selector: '[ngModel]:not([formControlName]):not([formControl])',
providers: [formControlBinding],
exportAs: 'ngModel'
})
export class NgModel extends NgControl implements OnChanges,
3) DEFAULT_VALUE_ACCESSOR
@Directive({
selector:
`input:not([type=checkbox])[formControlName],
textarea[formControlName],
input:not([type=checkbox])formControl],
textarea[formControl],
input:not([type=checkbox])[ngModel],
textarea[ngModel],[ngDefaultControl]',
,,,
})
export class DefaultValueAccessor implements ControlValueAccessor {
NgControlStatus
direktif hanya memanipulasi kelas seperti ng-valid
, ng-touched
, ng-dirty
dan kita dapat menghilangkan sini.
DefaultValueAccesstor
menyediakan NG_VALUE_ACCESSOR
token dalam array penyedia:
export const DEFAULT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DefaultValueAccessor),
multi: true
};
...
@Directive({
...
providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {
NgModel
direktif memasukkan NG_VALUE_ACCESSOR
token konstruktor yang dideklarasikan pada elemen host yang sama.
export NgModel extends NgControl implements OnChanges, OnDestroy {
constructor(...
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
Dalam kasus kami NgModel
akan menyuntikkan DefaultValueAccessor
. Dan sekarang direktif NgModel memanggil setUpControl
fungsi bersama :
export function setUpControl(control: FormControl, dir: NgControl): void {
if (!control) _throwError(dir, 'Cannot find control with');
if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');
control.validator = Validators.compose([control.validator !, dir.validator]);
control.asyncValidator = Validators.composeAsync([control.asyncValidator !, dir.asyncValidator]);
dir.valueAccessor !.writeValue(control.value);
setUpViewChangePipeline(control, dir);
setUpModelChangePipeline(control, dir);
...
}
function setUpViewChangePipeline(control: FormControl, dir: NgControl): void
{
dir.valueAccessor !.registerOnChange((newValue: any) => {
control._pendingValue = newValue;
control._pendingDirty = true;
if (control.updateOn === 'change') updateControl(control, dir);
});
}
function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
// control -> view
dir.valueAccessor !.writeValue(newValue);
// control -> ngModel
if (emitModelEvent) dir.viewToModelUpdate(newValue);
});
}
Dan inilah jembatan yang sedang beraksi:
NgModel
mengatur kontrol (1) dan dir.valueAccessor !.registerOnChange
metode panggilan . ControlValueAccessor
menyimpan callback dalam properti onChange
(2) dan mengaktifkan callback ini ketika input
peristiwa terjadi (3) . Dan akhirnya updateControl
fungsi dipanggil di dalam callback (4)
function updateControl(control: FormControl, dir: NgControl): void {
dir.viewToModelUpdate(control._pendingValue);
if (control._pendingDirty) control.markAsDirty();
control.setValue(control._pendingValue, {emitModelToViewChange: false});
}
di mana panggilan sudut membentuk API control.setValue
.
Itu adalah versi singkat dari cara kerjanya.