[ngDefaultControl]
Les contrôles tiers nécessitent un ControlValueAccessorpour fonctionner avec des formes angulaires. Beaucoup d'entre eux, comme Polymer <paper-input>, se comportent comme l' <input>élément natif et peuvent donc utiliser le DefaultValueAccessor. L'ajout d'un ngDefaultControlattribut leur permettra d'utiliser cette directive.
<paper-input ngDefaultControl [(ngModel)]="value>
ou
<paper-input ngDefaultControl formControlName="name">
C'est donc la principale raison pour laquelle cet attrubute a été introduit.
Il a été appelé ng-default-controlattribut dans les versions alpha d'angular2 .
Il en ngDefaultControlva de même pour l'un des sélecteurs de la directive 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 {
Qu'est-ce que ça veut dire?
Cela signifie que nous pouvons appliquer cet attribut à un élément (comme un composant polymère) qui n'a pas son propre accesseur de valeur. Donc, cet élément prendra le comportement deDefaultValueAccessor et nous pouvons utiliser cet élément avec des formes angulaires.
Sinon, vous devez fournir votre propre implémentation de ControlValueAccessor
ControlValueAccessor
États de la documentation angulaire
Un ControlValueAccessor agit comme un pont entre l'API Angular Forms et un élément natif dans le DOM.
Écrivons le modèle suivant dans une application angular2 simple:
<input type="text" [(ngModel)]="userName">
Pour comprendre comment inputse comportera ce qui précède, nous devons savoir quelles directives sont appliquées à cet élément. Ici, angular donne un indice avec l'erreur:
Rejet de promesses non gérées: erreurs d'analyse de modèle: impossible de se lier à «ngModel» car ce n'est pas une propriété connue de «input».
D'accord, nous pouvons ouvrir SO et obtenir la réponse: importez FormsModuledans votre @NgModule:
@NgModule({
imports: [
...,
FormsModule
]
})
export AppModule {}
Nous l'avons importé et tout fonctionne comme prévu. Mais que se passe-t-il sous le capot?
FormsModule exporte pour nous les directives suivantes:
@NgModule({
...
exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}

Après quelques recherches, nous pouvons découvrir que trois directives seront appliquées à notre 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 {
NgControlStatusdirective seulement manipule des classes comme ng-valid, ng-touched, ng-dirtyet nous pouvons l' omettre ici.
DefaultValueAccesstorfournit un NG_VALUE_ACCESSORjeton dans le tableau des fournisseurs:
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 {
NgModeldirective injecte dans le NG_VALUE_ACCESSORjeton de constructeur qui a été déclaré sur le même élément hôte.
export NgModel extends NgControl implements OnChanges, OnDestroy {
constructor(...
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
Dans notre cas NgModelva injecter DefaultValueAccessor. Et maintenant, la directive NgModel appelle la setUpControlfonction partagée :
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);
});
}
Et voici le pont en action:

NgModelconfigure le contrôle (1) et appelle la dir.valueAccessor !.registerOnChangeméthode. ControlValueAccessorstocke le rappel dans la propriété onChange(2) et déclenche ce rappel lorsque l' inputévénement se produit (3) . Et enfin la updateControlfonction est appelée à l'intérieur du callback (4)
function updateControl(control: FormControl, dir: NgControl): void {
dir.viewToModelUpdate(control._pendingValue);
if (control._pendingDirty) control.markAsDirty();
control.setValue(control._pendingValue, {emitModelToViewChange: false});
}
où les appels angulaires forment l'API control.setValue.
C'est une version courte de la façon dont cela fonctionne.
@Input() ngModelet@Output() ngModelChangepour la liaison bidirectionnelle et j'ai pensé que cela devrait suffire à un pont. Cela ressemble à faire la même chose d'une manière complètement différente. Peut-être que je ne devrais pas nommer mon champngModel?