[ngDefaultControl]
Los controles de terceros requieren un ControlValueAccessorfuncionamiento con formas angulares. Muchos de ellos, como el de Polymer <paper-input>, se comportan como el <input>elemento nativo y, por lo tanto, pueden usar DefaultValueAccessor. Agregar un ngDefaultControlatributo les permitirá usar esa directiva.
<paper-input ngDefaultControl [(ngModel)]="value>
o
<paper-input ngDefaultControl formControlName="name">
Entonces, esta es la razón principal por la que se introdujo este atributo.
Se le llamó ng-default-controlatributo en las versiones alfa de angular2 .
También lo ngDefaultControles uno de los selectores para la directiva 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é significa eso?
Significa que podemos aplicar este atributo a un elemento (como un componente de polímero) que no tiene su propio descriptor de acceso de valor. Entonces, este elemento tomará el comportamiento de DefaultValueAccessory podemos usar este elemento con formas angulares.
De lo contrario, debe proporcionar su propia implementación de ControlValueAccessor
ControlValueAccessor
Estados de documentos angulares
Un ControlValueAccessor actúa como un puente entre la API de formas angulares y un elemento nativo en el DOM.
Escribamos la siguiente plantilla en una aplicación angular2 simple:
<input type="text" [(ngModel)]="userName">
Para comprender cómo inputse comportará nuestro anterior, necesitamos saber qué directivas se aplican a este elemento. Aquí angular da una pista con el error:
Rechazo de promesa no controlado: errores de análisis de plantilla: no se puede enlazar a 'ngModel' porque no es una propiedad conocida de 'input'.
Bien, podemos abrir SO y obtener la respuesta: importar FormsModulea su @NgModule:
@NgModule({
imports: [
...,
FormsModule
]
})
export AppModule {}
Lo importamos y todo funciona según lo previsto. Pero, ¿qué está pasando bajo el capó?
FormsModule exporta para nosotros las siguientes directivas:
@NgModule({
...
exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}

Después de alguna investigación, podemos descubrir que se aplicarán tres directivas a nuestra 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 {
NgControlStatusdirectiva clases sólo manipula como ng-valid, ng-touched, ng-dirtyy podemos omitir aquí.
DefaultValueAccesstorproporciona NG_VALUE_ACCESSORtoken en la matriz de proveedores:
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 {
NgModelLa directiva inyecta el NG_VALUE_ACCESSORtoken del constructor que se declaró en el mismo elemento de host.
export NgModel extends NgControl implements OnChanges, OnDestroy {
constructor(...
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
En nuestro caso NgModelinyectaremos DefaultValueAccessor. Y ahora la directiva NgModel llama a la setUpControlfunción compartida :
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);
});
}
Y aquí está el puente en acción:

NgModelconfigura control (1) y llama al dir.valueAccessor !.registerOnChangemétodo. ControlValueAccessoralmacena la devolución de llamada en la propiedad onChange(2) y activa esta devolución de llamada cuando inputocurre el evento (3) . Y finalmente la updateControlfunción se llama dentro de la devolución de llamada (4)
function updateControl(control: FormControl, dir: NgControl): void {
dir.viewToModelUpdate(control._pendingValue);
if (control._pendingDirty) control.markAsDirty();
control.setValue(control._pendingValue, {emitModelToViewChange: false});
}
donde las llamadas angulares forman API control.setValue.
Esa es una versión corta de cómo funciona.
@Input() ngModely@Output() ngModelChangepara encuadernación bidireccional y pensé que debería ser suficiente un puente. Esto parece hacer lo mismo de una manera completamente diferente. ¿Quizás no debería nombrar mi campongModel?@Input() value; @Output() valueChange: EventEmitter<any> = new EventEmitter();y luego usarlo[(value)]="someProp"ngModely Angular comenzó a arrojarme un error y a preguntar con ControlValueAccessor.