How can I create my own component for FormControls

2020-03-08 05:49发布

问题:

I would like to create a form and use a new, custom component for its controls. So I created a new component and included it into the parent form. But although the parent form has a formGroup, Angular complains that it does not.

The error:

Error: formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class).

The parent form has:

<form [formGroup]="learningObjectForm" (ngSubmit)="onSubmit()" novalidate>
  <div>
    <button type="submit"
            [disabled]="learningObjectForm.pristine">Save</button>
  </div>

  <ava-form-control [label]="'Resource'"></ava-form-control>

</form>

And in the .ts:

  constructor(private fb: FormBuilder) {
    this.createForm();
  }

  createForm() {
    this.learningObjectForm = this.fb.group({
      text: '',
    });
  }

In the custom component I have

import { Component, Input, OnInit }       from '@angular/core';

@Component({
  selector: 'ava-form-control',
  template: `  <div>
    <label>{{label}} :</label>
    <input formControlName="{{name}}">
  </div>
`
})
export class FormControlComponent implements OnInit {

  @Input() label: string;
  @Input() name: string;

  constructor() {}

  ngOnInit() {
    if (this.name === undefined) {
      // turns 'The Label' into 'theLabel'
      this.name = this.label[0].toLowerCase().concat(this.label.slice(1));
      this.name = this.name.split(' ').join('');
      console.log(this.label, this.name);
    }
  }
}

回答1:

You should also be passing the formGroup instance along with control name to your custom component. And then create a form control under that formGroup in custom component. Your custom component will create the control virtually under the same formGroup that you have provided.

<form [formGroup]="learningObjectForm" (ngSubmit)="onSubmit()" novalidate>
  <div>
    <button type="submit"
            [disabled]="learningObjectForm.pristine">Save</button>
  </div>

  <ava-form-control [label]="'Resource'" [formGroup]="learningObjectForm" [controlName]="'mycontrol'"></ava-form-control>

</form>

custom.component.ts

import { Component, Input, OnInit }       from '@angular/core';

@Component({
  selector: 'ava-form-control',
  template: `  <div>
    <label>{{label}} :</label>
    <input [formControl]="formGroup.controls[controlName]>
  </div>
`
})

export class FormControlComponent implements OnInit {

  @Input() label: string;
  @Input() formGroup: FormGroup;
  @Input() controlName: string;

  constructor() {}

  ngOnInit() {
    let control: FormControl = new FormControl('', Validators.required);
    this.formGroup.addControl(this.controlName, control);
  }
}

With this your parent component can access all the form controls defined within their respective custom components.



回答2:

I played around with the accepted answer for a long time, and never had any luck.

I had much better results implementing the ControlValueAccessor interface as shown here: https://alligator.io/angular/custom-form-control/

It's actually pretty simple, I also rigged up an Example StackBlitz



标签: forms angular