Búsqueda de sitios web

javascript - Angular2: Vincular el contexto del formulario a ngTemplateOutlet


Estoy tratando de definir un Componente que contenga un Formulario dinámico (usando ReactiveForms) donde el usuario debería poder agregar/eliminar Controles. Los Controles pueden tomar muchas formas, y tienen que definirse fuera del Componente, por lo que creo que TemplateRef es lo más apropiado para esto.

Estoy teniendo dificultades para encontrar una manera de vincular el Control definido externamente al Formulario interno, mediante el uso de formControlName

Aquí hay un comienzo de la implementación:

// expandable.component.ts

[...]
@Component({
  selector: 'expandable',
  templateUrl: 'app/component/common/expandable/expandable.component.html',
  styleUrls: ['app/component/common/expandable/expandable.component.css']
})
export class ExpandableComponent {
  @ContentChild('childTemplate') childTemplate: TemplateRef<any>;

  @Input() children: Array<any>;
  public form: FormGroup;

  constructor(private _changeDetector: ChangeDetectorRef,
              private _formBuilder: FormBuilder) {
    this.children = [];
  }

  public ngOnInit(): void {
    this.form = this._formBuilder.group({
      children: this._formBuilder.array([])
    });

    const arrayControl = <FormArray>this.form.controls['children'];
    this.children.forEach(child => {
      const group = this.initChildForm();
      arrayControl.push(group);
    });
  }

  private initChildForm(): AbstractControl {
    return this._formBuilder.group({
      key: ['Initial Key', [Validators.required]],
      value: ['Initial Value', [Validators.required]]
    });
  }

  public addChild(): void {
    const control = <FormArray>this.form.controls['children'];
    control.push(this.initChildForm());
    this._changeDetector.detectChanges();
  }
}

-

<!-- expandable.component.html -->

<form [formGroup]="form">
  <div class="form-group">
    <div formArrayName="children">
      <div *ngFor="let child of form.controls.children.controls; let i=index">
        <div [formGroupName]="i">
          <template 
             [ngTemplateOutlet]="childTemplate" 
             [ngOutletContext]="{ $implicit: child }"></template>
        </div>
      </div>
    </div>
  </div>
  <a (click)="addChild()">Add Child</a>
</form>

Intentar definir la plantilla externamente:

  <expandable>
    <template #childTemplate>
      <input class="form-control" 
        formControlName="value" />
      </template>
  </expandable>

Estoy intentando ingenuamente enlazar el formControlName al contexto que se pasa implícitamente desde el exterior, pero sin suerte, ya que obtengo "No se puede encontrar el control con el nombre: 'value'". Idealmente, me gustaría poder hacer el enlace de formControlName directamente en el expandable.component.html, pero tampoco veo ninguna manera de hacerlo.

¿Alguna idea sobre esto?

Respuesta #1

Sí, esto es posible:

Necesitas implementar la interfaz ControlValueAccessor, que ngModel y formControlName inyectan, de la siguiente manera:

@Directive({
  selector: 'control',
  providers: [
    {provide: NG_VALUE_ACCESSOR, multi: true, useExisting: SwitchControlComponent}
  ]
})
export class SwitchControlComponent implements ControlValueAccessor {
  isOn: boolean;
  _onChange: (value: any) => void;

  writeValue(value: any) {
    this.isOn = !!value;
  }

  registerOnChange(fn: (value: any) => void) {
    this._onChange = fn;
  }

  registerOnTouched() {}

  toggle(isOn: boolean) {
    this.isOn = isOn;
    this._onChange(isOn);
  }
}

html :

  <expandable>
    <template #childTemplate>
      <div [formGroup]="form">
         <input control class="form-control" 
           formControlName="value" />
         </template>
      </div>
  </expandable>

Lectura adicional:

Con la nueva API de formularios, no se pueden agregar entradas desde componentes hijos sin agregar etiquetas de formulario adicionales

Artículos relacionados