Tengo un elemento personalizado:
<div formControlName="surveyType">
<div *ngFor="let type of surveyTypes"
(click)="onSelectType(type)"
[class.selected]="type === selectedType">
<md-icon>{{ type.icon }}</md-icon>
<span>{{ type.description }}</span>
</div>
</div>
Cuando intento agregar el formControlName, recibo un mensaje de error:
Error de ERROR: sin valor de acceso para el control de formulario con el nombre: 'surveyType'
Traté de agregar ngDefaultControl
sin éxito. Parece que es porque no hay entrada / selección ... y no sé qué hacer.
Me gustaría vincular mi clic a este formularioControl para que cuando alguien haga clic en toda la tarjeta que empuje mi 'tipo' en el formularioControl. ¿Es posible?
Respuestas:
Puede usar
formControlName
solo en directivas que implementanControlValueAccessor
.Implementar la interfaz
Entonces, para hacer lo que quiere, debe crear un componente que implemente
ControlValueAccessor
, lo que significa implementar las siguientes tres funciones :writeValue
(le dice a Angular cómo escribir el valor del modelo a la vista)registerOnChange
(registra una función de controlador que se llama cuando cambia la vista)registerOnTouched
(registra un controlador que se llamará cuando el componente recibe un evento táctil, útil para saber si el componente se ha enfocado).Registrar un proveedor
Luego, debe decirle a Angular que esta directiva es una
ControlValueAccessor
(la interfaz no la cortará, ya que se elimina del código cuando TypeScript se compila en JavaScript). Para ello, debe registrar un proveedor .El proveedor debe proporcionar
NG_VALUE_ACCESSOR
y usar un valor existente . También necesitarás unforwardRef
aquí. Tenga en cuenta queNG_VALUE_ACCESSOR
debe ser un proveedor múltiple .Por ejemplo, si su directiva personalizada se llama MyControlComponent, debe agregar algo en las siguientes líneas dentro del objeto pasado al
@Component
decorador:Uso
Su componente está listo para ser utilizado. Con los formularios basados en plantillas , el
ngModel
enlace ahora funcionará correctamente.Con los formularios reactivos , ahora puede usarlos correctamente
formControlName
y el control del formulario se comportará como se esperaba.Recursos
fuente
Creo que deberías usar
formControlName="surveyType"
en uninput
y no en undiv
fuente
<ng-content>
de un componente contenedor y dejar que el componente principal que defineformControls
simplemente ponga el <input> dentro del <wrapper>El error significa que Angular no sabe qué hacer cuando le pones un
formControl
adiv
. Para solucionar esto, tienes dos opciones.formControlName
en un elemento, que es compatible con Angular fuera de la caja. Estos son:input
,textarea
yselect
.ControlValueAccessor
interfaz. Al hacerlo, le está diciendo a Angular "cómo acceder al valor de su control" (de ahí el nombre). O en términos simples: qué hacer, cuando colocaformControlName
un elemento, que naturalmente no tiene un valor asociado.Ahora, implementando el
ControlValueAccessor
interfaz puede ser un poco desalentador al principio. Especialmente porque no hay mucha buena documentación de esto y necesita agregar una gran cantidad de repeticiones a su código. Así que déjame intentar desglosar esto en algunos pasos fáciles de seguir.Mueva su control de formulario a su propio componente
Para implementar el
ControlValueAccessor
, debe crear un nuevo componente (o directiva). Mueva el código relacionado con su control de formulario allí. De esta manera, también será fácilmente reutilizable. En primer lugar, tener un control dentro de un componente podría ser la razón, por qué necesita implementar elControlValueAccessor
interfaz, porque de lo contrario no podrá usar su componente personalizado junto con formas angulares.Agregue el repetitivo a su código
La implementación de la
ControlValueAccessor
interfaz es bastante detallada, aquí está la plantilla que viene con ella:Entonces, ¿qué están haciendo las partes individuales?
ControlValueAccessor
interfazControlValueAccessor
interfazonChange
yonTouch
con su propia implementación durante el tiempo de ejecución, de modo que luego pueda llamar a esas funciones. Por lo tanto, es importante entender este punto: no necesita implementar onChange y onTouch usted mismo (aparte de la implementación vacía inicial). Lo único que estás haciendo con (c) es dejar que Angular adjunte sus propias funciones a tu clase. ¿Por qué? Entonces puedes llamar alonChange
yonTouch
métodos proporcionados por Angular en el momento apropiado. Veremos cómo funciona esto a continuación.writeValue
método en la siguiente sección, cuando lo implementemos. Lo puse aquí, para queControlValueAccessor
se implementen todas las propiedades requeridas y su código aún se compile.Implemente writeValue
Lo que
writeValue
hace es hacer algo dentro de su componente personalizado, cuando el control de formulario se cambia por fuera . Entonces, por ejemplo, si ha nombrado su componente de control de formulario personalizadoapp-custom-input
y lo usaría en el componente principal de esta manera:luego
writeValue
se activa cada vez que el componente padre cambia de alguna manera el valor demyFormControl
. Esto podría ser, por ejemplo, durante la inicialización del formulario (this.form = this.formBuilder.group({myFormControl: ""});
) o en un reinicio del formulariothis.form.reset();
.Lo que normalmente querrá hacer si el valor del control del formulario cambia en el exterior, es escribirlo en una variable local que represente el valor del control del formulario. Por ejemplo, si
CustomInputComponent
gira alrededor de un control de formulario basado en texto, podría verse así:y en el html de
CustomInputComponent
:También puede escribirlo directamente en el elemento de entrada como se describe en los documentos angulares.
Ahora ha manejado lo que sucede dentro de su componente cuando algo cambia afuera. Ahora veamos la otra dirección. ¿Cómo se informa al mundo exterior cuando algo cambia dentro de su componente?
Llamando aCambiar
El siguiente paso es informar al componente padre sobre los cambios dentro de su
CustomInputComponent
. Aquí es donde entran en juego las funcionesonChange
yonTouch
de (c) desde arriba. Al llamar a esas funciones, puede informar al exterior sobre los cambios dentro de su componente. Para propagar los cambios del valor al exterior, debe llamar a onChange con el nuevo valor como argumento . Por ejemplo, si el usuario escribe algo en elinput
campo en su componente personalizado, llameonChange
con el valor actualizado:Si vuelve a comprobar la implementación (c) desde arriba, verá lo que sucede: Angular limita su propia implementación a la
onChange
propiedad de clase. Esa implementación espera un argumento, que es el valor de control actualizado. Lo que está haciendo ahora es llamar a ese método y, por lo tanto, informar a Angular sobre el cambio. Angular ahora continuará y cambiará el valor del formulario en el exterior. Esta es la parte clave de todo esto. Le dijo a Angular cuándo debería actualizar el control de formulario y con qué valor llamandoonChange
. Le ha dado los medios para "acceder al valor de control".Por cierto: el nombre
onChange
es elegido por mí. Puede elegir cualquier cosa aquí, por ejemplopropagateChange
o similar. Sin embargo, sin importar cómo lo nombre, será la misma función que toma un argumento, que es proporcionada por Angular y que está vinculada a su clase por elregisterOnChange
método durante el tiempo de ejecución.Llamando a Touch
Dado que los controles de formulario se pueden "tocar", también debe darle a Angular los medios para comprender cuándo se toca su control de formulario personalizado. Puede hacerlo, lo adivinó, llamando a la
onTouch
función. Entonces, para nuestro ejemplo aquí, si desea seguir cumpliendo con cómo Angular lo está haciendo para los controles de formulario listos para usar, debe llamaronTouch
cuando el campo de entrada esté borroso:Una vez más,
onTouch
es un nombre elegido por mí, pero Angular proporciona su función real y no requiere argumentos. Lo que tiene sentido, ya que solo le está haciendo saber a Angular, que se ha tocado el control de formulario.Poniendolo todo junto
Entonces, ¿cómo se ve eso cuando se trata todo junto? Debe tener un aspecto como este:
Más ejemplos
Formas anidadas
Tenga en cuenta que los Access Value Accessors NO son la herramienta adecuada para los grupos de formularios anidados. Para grupos de formularios anidados, simplemente puede usar un
@Input() subform
en su lugar. ¡Los Access Value Controlors están diseñados para envolvercontrols
, nogroups
! Vea en este ejemplo cómo usar una entrada para un formulario anidado: https://stackblitz.com/edit/angular-nested-forms-input-2Fuentes
fuente
Para mí, se debió al atributo "múltiple" en el control de entrada seleccionado, ya que Angular tiene un ValueAccessor diferente para este tipo de control.
Y dentro de la plantilla use así
Más detalles ref Documentos oficiales
fuente