Angular 2 - form group component

2019-04-22 17:08发布

I'm trying to build a data-driven form, with inputs coming from another component, like this:

<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
    <app-form-group [name]="name"></app-form-group>
    <app-form-group [name]="email"></app-form-group>
    <app-form-group [name]="other"></app-form-group>
</form>

The app-form-group component will look something like this:

<div class="form-group">
    <label class="col-md-2 control-label">{{Name}}</label>
    <div class="col-md-9">
  <input class="form-control" [name]="name" [formControlName]="formCtrlName">
</div>

The problem is that formControlName needs a formGroupdirective, therefore I get this error:

Error : Error in ./FormGroupComponent class FormGroupComponent - inline template:3:58 caused by: 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).

Is there any way to get around this issue?

3条回答
欢心
2楼-- · 2019-04-22 17:44

You should use your FormGroup [formGroup]="signupForm" in app-form-group Component.You can use this Code :

<div class="form-group" [formGroup]="signupForm">
  <label class="col-md-2 control-label">{{Name}}</label>
  <div class="col-md-9">
  <input class="form-control" [name]="name" [formControlName]="formCtrlName">
</div>
查看更多
beautiful°
3楼-- · 2019-04-22 17:47

When you want to implement a custom component for data-binding to an individual value the correct Angular way to do that is to go with the approach where the parent view specifies either [formControl] or [formControlName].

<app-form-group formControlName="name"></app-form-group>
<app-form-group formControlName="email"></app-form-group>
<app-form-group formControlName="other"></app-form-group>

In your child control you need to do the following:

First add the following provider to your @Component declaration, so Angular knows your component can work with [formControlName] / [formControl]

providers: [
  {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CountrySelectorComponent),
    multi: true
  }
]

Your child component's class then needs to implement the ControlValueAccessor interface. This should be a fully working example, if not then let me know and I will change the code, I typed it into the browser directly.

@Component({
  // Ensure [formControl] or [formControlName] is also specified
  selector: '[formControl] app-form-group, [formControlName] app-form-group',
  templateUrl: './country-selector.component.html',
  styleUrls: ['./country-selector.component.scss'],
  providers: [
    {
      // Provide the value accessor
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CountrySelectorComponent),
      multi: true
    }
  ]
})
export class CountrySelectorComponent implements ControlValueAccessor {
  private value: any;
  private hasHadFocus = false;
  private hasNotifiedTouched = false;
  private propagateChange: any = () => {};
  private propogateTouched: any = () => {};

  public changed(event: any) {
    this.value = event.srcElement.value;
    this.propagateChange(this.value);
  }

  /**
   * Called when (focus) is triggered
   */
  public focused() {
    this.hasHadFocus = true;
  }

  /**
   * Called when (blur) is triggered
   */
  public blurred() {
    if (this.hasHadFocus && !this.hasNotifiedTouched) {
      // Only notify once, and only if lost focus after a focus
      this.hasNotifiedTouched = true;
      this.propogateTouched();
    }
  }

  /**
   * Called when a new value is set via code
   * @param obj
   */
  writeValue(obj: any): void {
    this.value = obj;
  }

  /**
   * Register a call back to call when our value changes
   * @param fn
   */
  registerOnChange(fn: any): void {
    this.propagateChange = fn;
  }

  /**
   * Register a call back to call when our value is first touched
   * @param fn
   */
  registerOnTouched(fn: any): void {
    this.propogateTouched = fn;
  }
}

Your app-form-group template would look something like this

<div class="form-group">
    <label class="col-md-2 control-label">{{Name}}</label>
    <div class="col-md-9">
    <input class="form-control" (blur)="blurred()" focus="focussed()" change="changed($event)">
</div>
  1. Add [formGroup]="signupForm" to each of your app-form-group instances in your main view
  2. Implement OnInit in your app-form-group control.
  3. Add private controlContainer: ControlContainer to the constructor of your app-form-group component so Angular will inject it
  4. Add public form: FormGroup; property to your app-form-group component.
  5. In ngOnInit add the following code

    this.form = this.containerControl.control;

In your app-form-group template you would add [formGroup] like so

<div class="form-group" [formGroup]="form">
    <label class="col-md-2 control-label">{{Name}}</label>
    <div class="col-md-9">
  <input class="form-control" [name]="name" [formControlName]="formCtrlName">
</div>

This is the method that requires the least amount of code, and is the one I would recommend when you want to embed composite controls that edit multiple pieces of data (like an Address).

From this blog -> https://mrpmorris.blogspot.co.uk/2017/08/angular-composite-controls-formgroup-formgroupname-reactiveforms.html

查看更多
劳资没心,怎么记你
4楼-- · 2019-04-22 17:52

You can use a @Input property to get the reference into the sub-component:

app-form-group.ts:

@Input('group')
public signupForm: FormGroup;

app.html:

<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
    <app-form-group [name]="name" [group]="signupForm"></app-form-group>
    <app-form-group [name]="name" [group]="signupForm"></app-form-group>
    <app-form-group [name]="name" [group]="signupForm"></app-form-group>
</form>

app-form-group.html:

<div class="form-group" [formGroup]="signupForm">
   <label class="col-md-2 control-label">{{Name}}</label>
   <div class="col-md-9">
   <input class="form-control" [name]="name" [formControlName]="formCtrlName">
</div>

For detailed information have a look at this tutorial: https://scotch.io/tutorials/how-to-build-nested-model-driven-forms-in-angular-2

查看更多
登录 后发表回答