创建一个可重用的FormGroup

Laz*_*vić 15 angular angular-forms

我知道创建自定义的控件作为组件,但我无法弄清楚如何创建自定义.

同样,我们可以做到这一点实现ControlValueAccessor,并使用自定义组件一样<my-cmp formControlName="foo"></my-cmp>,我们如何才能达到这种效果

<my-cmp formGroupName="aGroup"></my-cmp>
Run Code Online (Sandbox Code Playgroud)

两个非常常见的用例是:(a)将长形式分为几个步骤,每个步骤分别在一个单独的组件中;(b)封装一组出现在多种形式的字段,例如地址(国家,州,城市组) ,地址,建筑物编号)或出生日期(年,月,日).


用法示例(不是实际工作代码)

Parent具有以下形式FormBuilder:

// parent model
form = this.fb.group({
  username: '',
  fullName: '',
  password: '',
  address: this.fb.group({
    country: '',
    state: '',
    city: '',
    street: '',
    building: '',
  })
})
Run Code Online (Sandbox Code Playgroud)

父模板(为简洁起见,不可访问和非语义):

<!-- parent template -->
<form [groupName]="form">
  <input formControlName="username">
  <input formControlName="fullName">
  <input formControlName="password">
  <address-form-group formGroup="address"></address-form-group>
</form>
Run Code Online (Sandbox Code Playgroud)

现在AddressFormGroupComponent知道如何处理在其中具有这些特定控件的组.

<!-- child template -->
<input formControlName="country">
<input formControlName="state">
<input formControlName="city">
<input formControlName="street">
<input formControlName="building">
Run Code Online (Sandbox Code Playgroud)

Laz*_*vić 34

rusev的回答中提到了我失踪的那篇文章,那是注入的ControlContainer.

事实证明,如果放置formGroupName在组件上,并且该组件注入ControlContainer,则会获得对包含该组件的容器的引用.从这里很容易.

我们创建一个子表单组件.

@Component({
  selector: 'sub-form',
  template: `
    <ng-container [formGroup]="controlContainer.control">
      <input type=text formControlName=foo>
      <input type=text formControlName=bar>
    </ng-container>
  `,
})
export class SubFormComponent {
  constructor(public controlContainer: ControlContainer) {
  }
}
Run Code Online (Sandbox Code Playgroud)

注意我们如何需要输入的包装器.我们不需要表单,因为它已经在表单中.所以我们使用了ng-container.这将远离最终DOM,因此没有不必要的元素.

现在我们可以使用这个组件.

@Component({
  selector: 'my-app',
  template: `
    <form [formGroup]=form>
      <sub-form formGroupName=group></sub-form>
      <input type=text formControlName=baz>
    </form>
  `,
})
export class AppComponent  {
  form = this.fb.group({
    group: this.fb.group({
      foo: 'foo',
      bar: 'bar',
    }),
    baz: 'baz',
  })

  constructor(private fb: FormBuilder) {}
}
Run Code Online (Sandbox Code Playgroud)

您可以在StackBlitz上看到现场演示.


这是对rusev在几个方面的答案的改进:

  • 没有自定义groupName输入; 相反,我们使用formGroupNameAngular提供的
  • 不需要@SkipSelf装饰器,因为我们没有注入父控件,而是我们需要的控件
  • group.control.get(groupName)为了抓住自己,父母没有尴尬的事情.

  • 很好的答案,谢谢,我如何实现验证?,我尝试在子组件上执行此操作,但我不知道,该怎么办..? (2认同)

小智 5

Angular 表单没有与表单控件名称一样的组名称概念。但是,您可以通过将子模板包装在表单组中来轻松解决此问题。

这是一个类似于您发布的标记的示例 - https://plnkr.co/edit/2AZ3Cq9oWYzXeubij91I?p=preview

 @Component({
  selector: 'address-form-group',
  template: `
    <!-- child template -->
    <ng-container [formGroup]="group.control.get(groupName)">
      <input formControlName="country">
      <input formControlName="state">
      <input formControlName="city">
      <input formControlName="street">
      <input formControlName="building">
    </ng-container>
  `
})
export class AddressFormGroupComponent  { 
  @Input() public groupName: string;

  constructor(@SkipSelf() public group: ControlContainer) { }
}

@Component({
  selector: 'my-app',
  template: `
    <!-- parent template -->
    <div [formGroup]="form">
      <input formControlName="username">
      <input formControlName="fullName">
      <input formControlName="password">
      <address-form-group groupName="address"></address-form-group>
    </div>
    {{form?.value | json}}
  `
})
export class AppComponent { 
  public form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      username: '',
      fullName: '',
      password: '',
      address: this.fb.group({
        country: '',
        state: '',
        city: '',
        street: '',
        building: '',
      })
    });
  }
}
Run Code Online (Sandbox Code Playgroud)