[ngDefaultControl]
การควบคุมของบุคคลที่สามต้องการControlValueAccessor
ฟังก์ชันในรูปแบบเชิงมุม หลายคนเช่นพอลิเมอ<paper-input>
ร์ทำงานเหมือน<input>
องค์ประกอบดั้งเดิมดังนั้นจึงสามารถใช้ไฟล์DefaultValueAccessor
. การเพิ่มngDefaultControl
แอตทริบิวต์จะช่วยให้สามารถใช้คำสั่งนั้นได้
<paper-input ngDefaultControl [(ngModel)]="value>
หรือ
<paper-input ngDefaultControl formControlName="name">
นี่จึงเป็นเหตุผลหลักว่าทำไมจึงมีการนำคุณสมบัตินี้มาใช้
มันถูกเรียกว่าng-default-control
แอตทริบิวต์ในอัลฟารุ่น angular2
ดังนั้นจึงngDefaultControl
เป็นหนึ่งในตัวเลือกสำหรับคำสั่ง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 {
หมายความว่าอย่างไร?
หมายความว่าเราสามารถใช้แอตทริบิวต์นี้กับองค์ประกอบ (เช่นส่วนประกอบโพลีเมอร์) ที่ไม่มีตัวเข้าถึงค่าของตัวเอง ดังนั้นองค์ประกอบนี้จะรับพฤติกรรมDefaultValueAccessor
และเราสามารถใช้องค์ประกอบนี้กับรูปแบบเชิงมุมได้
มิฉะนั้นคุณต้องจัดเตรียมการใช้งานของคุณเอง ControlValueAccessor
ControlValueAccessor
เอกสารเชิงมุมระบุ
ControlValueAccessor ทำหน้าที่เป็นสะพานเชื่อมระหว่าง API รูปแบบเชิงมุมกับองค์ประกอบดั้งเดิมใน DOM
มาเขียนเทมเพลตต่อไปนี้ในแอปพลิเคชั่น angular2 อย่างง่าย:
<input type="text" [(ngModel)]="userName">
เพื่อให้เข้าใจถึงinput
พฤติกรรมข้างต้นของเราเราจำเป็นต้องรู้ว่าคำสั่งใดที่ใช้กับองค์ประกอบนี้ ที่นี่เชิงมุมให้คำใบ้กับข้อผิดพลาด:
การปฏิเสธสัญญาที่ไม่สามารถจัดการได้: ข้อผิดพลาดในการแยกวิเคราะห์เทมเพลต: ไม่สามารถผูกกับ 'ngModel' ได้เนื่องจากไม่ใช่คุณสมบัติที่เป็นที่รู้จักของ 'input'
โอเคเราสามารถเปิด SO และรับคำตอบ: นำเข้าFormsModule
สู่@NgModule
:
@NgModule({
imports: [
...,
FormsModule
]
})
export AppModule {}
เรานำเข้าและทำงานได้ตามที่ตั้งใจไว้ แต่เกิดอะไรขึ้นภายใต้ประทุน?
FormsModuleส่งออกคำสั่งต่อไปนี้สำหรับเรา:
@NgModule({
...
exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}
หลังจากการตรวจสอบเราพบว่าคำสั่งสามข้อจะถูกนำไปใช้กับ 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
สั่งการเรียนเพียงปรุงชอบng-valid
, ng-touched
, ng-dirty
และเราสามารถละเว้นได้ที่นี่
DefaultValueAccesstor
ให้NG_VALUE_ACCESSOR
โทเค็นในอาร์เรย์ผู้ให้บริการ:
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
การฉีดคำสั่งในNG_VALUE_ACCESSOR
โทเค็นตัวสร้างที่ประกาศบนองค์ประกอบโฮสต์เดียวกัน
export NgModel extends NgControl implements OnChanges, OnDestroy {
constructor(...
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
ในกรณีของเราจะฉีดNgModel
DefaultValueAccessor
และตอนนี้ NgModel directive เรียกใช้setUpControl
ฟังก์ชันที่ใช้ร่วมกัน:
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) => {
dir.valueAccessor !.writeValue(newValue);
if (emitModelEvent) dir.viewToModelUpdate(newValue);
});
}
และนี่คือสะพานในการดำเนินการ:
NgModel
ตั้งค่าการควบคุม(1)และdir.valueAccessor !.registerOnChange
วิธีการโทร ControlValueAccessor
ร้านค้าโทรกลับในonChange
(2)ทรัพย์สินและความรุนแรงนี้ติดต่อกลับเมื่อinput
เหตุการณ์เกิดขึ้น(3) และสุดท้ายupdateControl
ฟังก์ชั่นถูกเรียกภายใน callback (4)
function updateControl(control: FormControl, dir: NgControl): void {
dir.viewToModelUpdate(control._pendingValue);
if (control._pendingDirty) control.markAsDirty();
control.setValue(control._pendingValue, {emitModelToViewChange: false});
}
ที่โทรเชิงมุมรูปแบบ control.setValue
API
นี่เป็นเวอร์ชันสั้น ๆ ของวิธีการทำงาน