【问题标题】:Create a reusable FormGroup创建一个可重用的 FormGroup
【发布时间】:2018-01-23 14:31:29
【问题描述】:

我知道将自定义控件创建为组件,但我不知道如何创建自定义

同样我们可以通过implementing ControlValueAccessor 和使用像<my-cmp formControlName="foo"></my-cmp> 这样的自定义组件来做到这一点,我们如何为a group 实现这个效果?

<my-cmp formGroupName="aGroup"></my-cmp>

两个非常常见的用例是 (a) 将长表单分成多个步骤,每个步骤在一个单独的组件中,以及 (b) 封装出现在多个表单中的一组字段,例如地址(国家/地区组,州、城市、地址、建筑物编号)或出生日期(年、月、日)。


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

Parent 使用FormBuilder 构建了以下表单:

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

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

<!-- parent template -->
<form [groupName]="form">
  <input formControlName="username">
  <input formControlName="fullName">
  <input formControlName="password">
  <address-form-group formGroup="address"></address-form-group>
</form>

现在这个AddressFormGroupComponent 知道如何处理其中包含这些特定控件的组。

<!-- child template -->
<input formControlName="country">
<input formControlName="state">
<input formControlName="city">
<input formControlName="street">
<input formControlName="building">

【问题讨论】:

    标签: angular angular-forms


    【解决方案1】:

    rusev's answer 中提到了我缺少的部分,即注入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) {
      }
    }
    

    注意我们需要一个输入的包装器。我们不需要表单,因为它已经在表单中。所以我们使用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) {}
    }
    

    你可以看到live demo on StackBlitz


    这在几个方面比 rusev 的答案有所改进:

    • 没有自定义groupName输入;相反,我们使用 Angular 提供的formGroupName
    • 不需要@SkipSelf 装饰器,因为我们没有注入父控件,而是我们需要的那个
    • 没有尴尬的group.control.get(groupName),它会去父母那里抓住自己。

    【讨论】:

    • 这真的很有帮助!
    • 很好的答案,谢谢,我如何实现验证?,我尝试在子组件上这样做,但我不知道,我该怎么做..?
    • 谢谢,很有帮助。这是在 Angular 文档中吗?我已经找过了,但没有找到任何东西。
    • 你刚刚分离了模板控件呢。我想有条件地呈现一些东西,如果这些属性 foo, bar 是必需的,那么你不能提交表单。
    • Type 'AbstractControl | null' is not assignable to type 'FormGroup'. Type 'null' is not assignable to type 'FormGroup'. 我在尝试将controlContainer.control 绑定到&lt;ng-container&gt; 上的formGorup 时收到此消息。我认为这与严格的类型检查有关,但我该如何解决呢?
    【解决方案2】:

    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: '',
          })
        });
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-08
      • 2019-05-23
      • 2011-12-30
      • 2022-01-18
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      相关资源
      最近更新 更多