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 ngDefaultControlsin é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
formControlNamesolo 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_ACCESSORy usar un valor existente . También necesitarás unforwardRefaquí. Tenga en cuenta queNG_VALUE_ACCESSORdebe 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
@Componentdecorador:Uso
Su componente está listo para ser utilizado. Con los formularios basados en plantillas , el
ngModelenlace ahora funcionará correctamente.Con los formularios reactivos , ahora puede usarlos correctamente
formControlNamey el control del formulario se comportará como se esperaba.Recursos
fuente
Creo que deberías usar
formControlName="surveyType"en uninputy no en undivfuente
<ng-content>de un componente contenedor y dejar que el componente principal que defineformControlssimplemente ponga el <input> dentro del <wrapper>El error significa que Angular no sabe qué hacer cuando le pones un
formControladiv. Para solucionar esto, tienes dos opciones.formControlNameen un elemento, que es compatible con Angular fuera de la caja. Estos son:input,textareayselect.ControlValueAccessorinterfaz. 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 colocaformControlNameun elemento, que naturalmente no tiene un valor asociado.Ahora, implementando el
ControlValueAccessorinterfaz 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 elControlValueAccessorinterfaz, 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
ControlValueAccessorinterfaz es bastante detallada, aquí está la plantilla que viene con ella:Entonces, ¿qué están haciendo las partes individuales?
ControlValueAccessorinterfazControlValueAccessorinterfazonChangeyonTouchcon 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 alonChangeyonTouchmétodos proporcionados por Angular en el momento apropiado. Veremos cómo funciona esto a continuación.writeValuemétodo en la siguiente sección, cuando lo implementemos. Lo puse aquí, para queControlValueAccessorse implementen todas las propiedades requeridas y su código aún se compile.Implemente writeValue
Lo que
writeValuehace 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-inputy lo usaría en el componente principal de esta manera:luego
writeValuese 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
CustomInputComponentgira 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 funcionesonChangeyonTouchde (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 elinputcampo en su componente personalizado, llameonChangecon 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
onChangepropiedad 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
onChangees elegido por mí. Puede elegir cualquier cosa aquí, por ejemplopropagateChangeo 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 elregisterOnChangemé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
onTouchfunció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 llamaronTouchcuando el campo de entrada esté borroso:Una vez más,
onTouches 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() subformen 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