如何使用formControlName和处理嵌套的formGroup?

Posted

技术标签:

【中文标题】如何使用formControlName和处理嵌套的formGroup?【英文标题】:How to use formControlName and deal with nested formGroup? 【发布时间】:2017-11-13 22:17:55 【问题描述】:

正如 Angular documentation 所说,我们可以在表单中使用 formControlName

<h2>Hero Detail</h2>
<h3><i>FormControl in a FormGroup</i></h3>
<form [formGroup]="heroForm" novalidate>
    <div class="form-group">
        <label class="center-block">Name:
            <input class="form-control" formControlName="name">
        </label>
    </div>
</form>

正如他们所说...

在没有父 FormGroup 的情况下,[formControl]="name" 更早地工作,因为该指令可以独立,也就是说,它可以在没有 FormGroup 的情况下工作。对于父 FormGroup,名称输入需要语法 formControlName=name 才能与类中的正确 FormControl 关联。此语法告诉 Angular 查找父 FormGroup,在本例中为 heroForm,然后在该组内查找名为 name 的 FormControl。

无论如何,几个月前我问this 弄清楚formControlName[formControl] 之间的区别。

现在我的问题是:如何将 formControlName 与嵌套的 FormGroups 一起使用?

例如,如果我有以下表单结构:

this.myForm = fb.group(
    'fullname': ['', Validators.required],
    'gender': [],
    'address': fb.group(
        'street': [''],
        'houseNumber': [''],
        'postalCode': ['']
    )
);

使用formControlName 将“street”(或“houseNumber”或“postalCode”)绑定到相关 html 元素的正确方法是什么?

【问题讨论】:

使用 formGroupName ***.com/questions/44431613/… @yurzui 的评论是解决方案,只需在嵌套的 html 模板中使用 formGroupName,然后再将 formControlName 作为子 html 元素访问 【参考方案1】:

您可以使用表单组,它基本上是控件的集合(控件是指在您的 html 表单中给出的字段)在您的打字稿语法中定义并使用 formControlName 指令绑定到您的 HTML 元素,例如

this.myForm = fb.group(
    'fullname': ['', Validators.required],
    'gender': [],
    'address': fb.group(
        'street': [''],
        'houseNumber': [''],
        'postalCode': ['']
    )
);

模板:

<form [formGroup]="myForm" >
   <div class="form-group">
      <label for="fullname">Username</label>
      <input type="text" id="username" formControlName="fullname" class="form-control">            
   </div>
   <div class="radio" *ngFor="let gender of genders">
      <label>
      <input type="radio" formControlName="gender" [value]="gender"> gender  </label>
   </div>
   <div formGroupName="address">
      <div class="form-group">
         <label for="street">Username</label>
         <input type="text" id="username" value="street" formControlName="street" class="form-control">            
      </div>
      <div class="form-group">
         <label for="houseNumber">Username</label>
         <input type="text" id="username" value="street" formControlName="houseNumber" class="form-control">            
      </div>
      <div class="form-group">
         <label for="postalCode">Username</label>
         <input type="text" id="username" value="street" formControlName="postalCode" class="form-control">            
      </div>
   </div>
</form>

一个 formGroup 可以由一个嵌套的 formGroup 组成,层次结构可以继续存在,但在访问值时它相当简单。

【讨论】:

ng-container 如果您不想将 div 物理地放入模板中,例如将元素与 flex 对齐,请记住这一点。也许将它包含在答案中会很好。 很好!但是如何验证呢? @Bielik 在类似的主题上,如果使用 css-grid(这是布置表格的好方法),您可以在 html 中创建层次结构(例如使用 div)但使用 display: contents在每个包装上。这将提升所有控件,就好像 div 元素不存在一样 - 然后网格将接管并将它们放在正确的位置。它与ng-container 的概念完全相同,但在 DOM 中留下了一个节点 - 这对于添加 aria 属性和调试期间很有用。 developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/…【参考方案2】:

这是真的。查看 formGroupName

this.myForm = fb.group(
    'fullname': ['', Validators.required],
    'gender': [],
    'address': fb.group(
        'street': [''],
        'houseNumber': [''],
        'postalCode': ['']
    )
);

<form [formGroup]="myForm" >
   <div class="form-group">
      <label for="fullname">Username</label>
      <input type="text" id="username" formControlName="fullname" class="form-control">            
   </div>
   <div class="radio" *ngFor="let gender of genders">
      <label>
      <input type="radio" formControlName="gender" [value]="gender"> gender  </label>
   </div>
   <div formGroupName="address">
      <div class="form-group">
         <label for="street">Username</label>
         <input type="text" id="username" value="street" formControlName="street" class="form-control">            
      </div>
      <div class="form-group">
         <label for="houseNumber">Username</label>
         <input type="text" id="username" value="street" formControlName="houseNumber" class="form-control">            
      </div>
      <div class="form-group">
         <label for="postalCode">Username</label>
         <input type="text" id="username" value="street" formControlName="postalCode" class="form-control">            
      </div>
   </div>
</form>

【讨论】:

这对我有用 ng6 和 &lt;ng-container formGroupName="quintessence"&gt;【参考方案3】:

tl;博士:

您可以将表单分解为使用嵌套 formgroups 的组件,并且 formcontrolname 可以正常使用。

由于通常嵌套表单组用于指定表单的单独部分,因此我倾向于使用的方法是将其分解为组件并将这些组件传递给嵌套表单组作为输入参数。 因此,在您的情况下,我将有一个 address 组件,该组件将表单组作为参数:

<app-address [formGroup]="myForm.get('address')"></app-address

在那个组件内部,我只需要一个 @Input() formGroup 来在 html 中使用:

<div [formGroup]="formGroup">
....

这样您就可以像往常一样显式引用控件名称,因为它将是该表单组的一部分。

另外,请记住表单是作为参考传递的。您的更改将在父组件的 myForm 元素中进行说明,如果您需要访问不在您的表单组中的表单部分(验证、更改检测等),您始终可以传递整个表单并将表单组定义为明确引用内部组:

<div [formGroup]="formGroup.get('adress')">

(假设您传递的整个表单对象是

编码愉快!

【讨论】:

谢天谢地...我花了很多时间制作具有 FormGroup Input 属性的表单组件。这工作谢谢。 由于新的 TypeScript 版本,这会引发错误 Type 'AbstractControl | null' is not assignable to type 'FormControl'.。你需要强制它工作,就像&lt;div [formGroup]="$any(formGroup.get('adress'))"&gt;【参考方案4】:

我正在努力解决 Angular 10 中的一个问题。 我有一个“父”form group“forma”有几个依赖组:“company”,同时,“company”有两个 “孩子”与其他组 msgAccountssocialMedia。 当我填写表格并提交时,在后端一切都是正确的,我可以看到这些数据是如何存储在数据库中的 正确,但是当我收到这个 json 时,我无法显示数据 在控件中的“company.msgAccounts”和“company.socialMedia”内 (输入)。这是我从服务器得到的:

            
            name:'',
            active: '',
            ........
            company:
              msgAccounts:line: "@linedemo", whatsapp: "0325554244",
              socialMedia: fb: '', tw: '', is: ''
                
            ..........
            
    
 this.forma = this.fb.group( 
          siteName  : [ '', [Validators.required, Validators.minLength(5)]],
          siteEmail  : [ '', [Validators.required, Validators.minLength(5)]],
          // defaultLocation: [ '', [Validators.required, Validators.minLength(10)]],
          active  : [ true, [Validators.required, Validators.minLength(5)]],
          mainLanguage: ['' , [Validators.required, Validators.minLength(2)]],
        
          company: this.fb.group(
            name: [''],
            address: [''],
            phone: [''],
            msgAccounts: this.fb.group(
              line: [],
              whatsapp: []
            ),
            socialMedia: this.fb.group(
              fb: [],
              is: [],
              tw: []
            )
          )
        );
    
    And the html: (Sorry about the indentation, when it was not easy using this editor, and I just pasted a part of the code in order to do it as shorter as possible).
    
            <mat-tab-group>   
                    <mat-tab label="settings">
                        <form autocomplete="off" >
                            <ng-template ng-template matTabContent> 
                                <mat-tab-group  [formGroup]="forma">
                                        <mat-tab label="global"> 
                                           // All this fields have are fine
                                         </mat-tab>
    
                                 <mat-tab formGroupName="company" label="company"> 
                                    <div class="card">
                                        <div class="card-header" id="headingTwo">
                                            <h5 class="mb-0">Details</h5>
                                        </div>
                                        <div id="company-details">
                                                <div class="form-group row">
                                                    <div class="col-sm-3">
                                                        <input 
                                                        type="text" 
                                                        class="form-control" 
                                                        id="name" 
                                                        name="name"
                                                        placeholder="Company name"
                                                        formControlName=name>
                                                    </div>
                        
                                                </div>
    
                             <div class="form-group row" formGroupName="msgAccounts">
                                                    <div class="col-sm-6">
                                             
                                                        <input 
                                                        type="text" 
                                                        class="form-control" 
                                                        id="line" 
                                                        name="line"
                                                        placeholder="line"
                                                        formControlName=line>
                                                    </div>
                                                    <div class="col-sm-6">
                                                    
                                                        <input 
                                                        type="text" 
                                                        class="form-control" 
                                                        id="whatsapp" 
                                                        name="whatsapp"
                                                        placeholder="whatsapp"
                                                        formControlName=whatsapp>
                                                    </div>
                                                </div>
    
                                                    
    
                             <div class="form-group row" formGroupName="socialMedia" >
                         
                                                    <div class="col-sm-6">
                                                        <input 
                                                        type="text" 
                                                        class="form-control" 
                                                        id="is" 
                                                        name="is"
                                                        placeholder="Is"
                                                        formControlName=is>
                                                    </div>
                                                </div>                             
                                            </div>
                                       </div>
                                </mat-tab>                 
               </mat-tab-group>  
                            <div class="form-group row">
                            <div class="col-sm-10">
                               <button type="submit" 
                                   (click)="saveConfig()">Save</button>
                            </div>
            </div>
                        
           </ng-template>
          </form>
       </mat-tab>
    </mat-tab-group>  

【讨论】:

以上是关于如何使用formControlName和处理嵌套的formGroup?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 - 组件内的 formControlName

错误:formControlName 必须与父 formGroup 指令一起使用。您需要添加一个 formGroup 指令 - Angular 反应式表单

使用 formControlName 和 ngModel 的角度 6 警告

带有 ControlValueAccessor 和 formControlName 的 Angular Material Datepicker [重复]

2个formarray内的嵌套复选框 - 角度材料

在Angular2中将管道添加到formControlName