欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

angular 前端 formGroup和formControlName问题

angular前端 formGroup和formControlName问题


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/k_e_vi_n/article/details/89445020

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).

      Example:

      
    <div [formGroup]="myGroup">
      <input formControlName="firstName">
    </div>

    In your class:

    this.myGroup = new FormGroup({
       firstName: new FormControl()
    });

情况一:没有FormGroup,使用FormControlName

情况二:有FormGroup,没有FormControlName而是使用[(ngModel)]

总而言之,FormGroup和FormControlName必须同时存在



来自  https://blog.csdn.net/k_e_vi_n/article/details/89445020


Angular 响应式表单之表单分组

1

1、案例需求

表单提交,表单全部校验成功才能提交,当表单校验错误,表单边框变红,同时有错误提示信息,有重置功能

2、名词解释

在分析代码之前,首先明确 FormControl、formControl、formControlName、FormGroup、formGroup、formGroupName、FormArray、formArray、formArrayName 都是什么意思以及它们的用法。

2.1、FormControl

  • FormControl:跟踪独立表单控件的值和验证状态。它和 FormGroup 和 FormArray 是 Angular 表单的三大基本构造块之一。它扩展了 AbstractControl 类,并实现了关于访问值、验证状态、用户交互和事件的大部分基本功能。

当使用响应式表单时,FormControl 类是最基本的构造块。要注册单个的表单控件,在组件中导入 FormControl 类,并创建一个 FormControl 的新实例,把它保存在类的某个属性中。

export class AppComponent implements OnInit {
    const control = new FormControl('', Validators.required);
    console.log(control.value);      // ''
    console.log(control.status);     // 'INVALID'
}

在组件类中创建了控件之后,还要把它和模板中的一个表单控件关联起来,为表单控件添加 formControl 绑定。

<label>
  Name:
  <input type="text" [formControl]="name">
</label>
  • formControl:是一个输入指令,接受 FormControl 的实例,在模版中使用。

  • formControlName: 也是输入指令,但是它接受的是一个字符串,同 formGroup 指令配合使用。

<div>
  <input type="text" [formControl]="myForm.controls.firstName"/>
  <input type="text" [formControl]="myForm.controls.lastName"/>
  <input type="text" [formControl]="myForm.controls.email"/>
  <input type="text" [formControl]="myForm.controls.title"/>
</div>

//等同于

<div [formGroup]="myForm">
  <input type="text" formControlName="firstName"/>
  <input type="text" formControlName="lastName"/>
  <input type="text" formControlName="email"/>
  <input type="text" formControlName="title"/>
</div>

2.2、FormGroup

  • FormGroup:跟踪一组 FormControl 实例的值和有效性状态

FormGroup 把每个子 FormControl 的值聚合进一个对象,它的 key 是每个控件的名字。它通过归集其子控件的状态值来计算出自己的状态。如果组中的任何一个控件是无效的,那么整个组就是无效的。

2.3、FormArray

  • FormArray:跟踪一个控件数组的值和有效性状态

FormArray 聚合了数组中每个表单控件的值。它会根据其所有子控件的状态总结出自己的状态。如果 FromArray 中的任何一个控件是无效的,那么整个数组也会变成无效的。

  • FormControl、FormGroup、FormArray 类 用法一致

  • formControl、formGroup、formArray 输入指令 值为对应类的实例 用法一致

  • formControlName、formGroupName、formArrayName 输入指令 值为字符串 用法一直

3、代码分析

fromGroup 可以然我们对表单内容进行分组,方便我们在语义上区分不同类型的输入,本例中,地址细分为“省”、“市”、“区”。

this.formGroup = this.fb.group({
      name: ['', nameValidator()],
      age: ['', ageValidator()],
      sex: ['', sexValidator()],
      address: this.fb.group({
        province: ['', requiredValidator('请输入省')],
        city: ['', requiredValidator('请输入市')],
        district: ['', requiredValidator('请输入区')]
      })
    });

address 此时不是 fromControl 而是 formGroup。

<div class="form-group"
    formGroupName="address">
    <label>地址:</label>
    <div>
      <label>省:</label>
      <input type="text"
        formControlName="province">
      <p>{{errorMessage('province')}}</p>
    </div>
    <div>
      <label>市:</label>
      <input type="text"
        formControlName="city">
      <p>{{errorMessage('city')}}</p>
    </div>
    <div>
      <label>区:</label>
      <input type="text"
        formControlName="district">
      <p>{{errorMessage('district')}}</p>
    </div>
  </div>

在获取 省市区的 formControl 时,可以通过这样获取

// 太复杂了
this.formGroup.controls['address'].controls['province'];

// 同样复杂
this.formGroup.get('address').controls['province'];

// 还好
this.formGroup.get(['address', 'province']);

第三种方式虽然简单,但是不够完美,get方法不能一步到位,必须同时传入 formGroupName 和 formControlName。因此在查看单个表单是否有错误信息时,必须先判断 formControlName 是子组件还是孙子组件。

errorMessage(formControlName: string): string {

    let control: AbstractControl;

    if (this.formGroup.contains(formControlName)) {
      control = this.formGroup.get(formControlName);
    } else {
      control = this.formGroup.get(['address', formControlName]);
    }
    return ((control.touched || control.dirty) && control.invalid) ? control.errors.message : '';
  }

contains方法:检查组内是否有一个具有指定名字的已启用的控件,存在返回 true,不存在返回 false。

来自 https://segmentfault.com/a/1190000018249859



[angular2 笔记] No.4 FormControl 和 FormGroup

SEVEN_PIG
0.577字数 2,965阅读 5,490

FormControl和FormGroup是Angular中两个最基础的表单对象。

1. FormControl

FormControl代表单一的输入字段,它是Angular表单中的最小单元。

FormControl封装了这些字段的值和状态,比如是否有效、是否脏(被修改过)或是否有错
误等。

比如,下列代码演示了如何在TypeScript中使用FormControl:

// create a new FormControl with the value "Nate" 
let nameControl = new FormControl("Nate"); 

let name = nameControl.value; // -> Nate 

// now we can query this control for certain values: 
nameControl.errors // -> StringMap<string, any> of errors 
nameControl.dirty  // -> false 
nameControl.valid  // -> true 
// etc. 

为了构建表单,我们会创建几组FormControl对象,然后为它们附加元数据和逻辑。

在Angular中,我们经常将一个类(本例中为FormControl)以属性形式(本例中为formControl)附加在DOM上。比如下面这个表单:

<!-- part of some bigger form --> 
<input type="text" [formControl]="name" /> 

这会在此form的上下文中创建一个新的FormControl对象。

2. FormGroup

大多数表单都拥有不止一个字段,因此我们需要某种方式来管理多个FormControl。假设我们要检查表单的有效性。如果要遍历这个FormControl数组并检查每一个FormControl是否有效,必然相当繁琐;而FormGroup则可以为一组FormControl提供总包接口(wrapper interface),来解决这种问题。

下面是FormGroup的创建方式:

let personInfo = new FormGroup({ 
    firstName: new FormControl("Nate"), 
    lastName: new FormControl("Murray"), 
    zip: new FormControl("90210") 
}) 

FormGroup 和 FormControl 都继承自同一个祖先AbstractControl。这意味检查 personInfo 的状态或值就像检查单个FormControl那么容易:

//   firstName: "Nate", 
//   lastName: "Murray", 
//   zip: "90210" 
// } 

// now we can query this control group for certain values, which have sensible 
// values depending on the children FormControl's values: 
personInfo.errors // -> StringMap<string, any> of errors 
personInfo.dirty  // -> false 
personInfo.valid  // -> true 
// etc. 

注意,当我们试图从FormGroup中获取value时,会收到一个“键值对”结构的对象。它能让我们从表单中一次性获取全部的值而无需逐一遍历FormControl,使用起来相当顺手。

3. 创建表单

我们要构建一个表单,效果如图:


带SKU的表单演示:简易版

3.1 加载 FormsModule

为了使用这个新的表单库,先要确保我们的NgModule中导入了这个表单库。

Angular中有两种使用表单的方式,我们在本章中都会展开讨论:使用FormsModule以及使用ReactiveFormsModule。既然都要用到,那么这个模块就同时导入它们。因此需要在引用启动程序app.ts中这样写:

import { 
  FormsModule, 
  ReactiveFormsModule 
} from '@angular/forms'; 

// farther down... 

({ 
  declarations: [ 
    FormsDemoApp, 
    DemoFormSku, 
    // ... our declarations here 
  ], 
  imports: [ 
    BrowserModule, 
    FormsModule,         // <-- add this 
    ReactiveFormsModule  // <-- and this 
  ], 
  bootstrap: [ FormsDemoApp ] 
}) 
class FormsDemoAppModule {}

这确保了我们能在视图中使用Angular表单指令。

FormsModule为我们提供了一些模板驱动的指令,例如:

  • ngModel

  • NgForm
    ReactiveFormsModule则提供了下列指令:

  • formControl

  • ngFormGroup

3.2 简易SKU 表单:template

  template: ` 
  <div class="ui raised segment"> 
    <h2 class="ui header">Demo Form: Sku</h2> 
    <form #f="ngForm" 
     (ngSubmit)="onSubmit(f.value)" 
     class="ui form"> 

      <div class="field"> 
       <label for="skuInput">SKU</label> 
       <input type="text" 
         id="skuInput" 
         placeholder="SKU" 
         name="sku" ngModel> 
      </div> 

      <button type="submit" class="ui button">Submit</button> 
    </form> 
  </div> 
  ` 
3.2.1. form和NgForm

我们导入了FormsModule,因此可以在视图中使用NgForm了。记住,当这些指令在视图中可用时,它就会被附加到任何能匹配其selector的节点上。

NgForm做了一件便利但隐晦的工作:它的选择器包含form 标签(而不用显式添加ngForm属性)。这意味着当我们导入FormsModule时候,NgForm就会被自动附加到视图中所有的<form>标签上。这确实非常有用,但由于它发生在幕后,也许会让很多人感到困惑。

NgForm给我们提供了两个重要的功能:

(1) 一个名叫ngForm的FormGroup对象;
(2) 一个输出事件(ngSubmit)。

我们在视图的<form>标签中同时用到了它们两个。

 <form #f="ngForm" 
   (ngSubmit)="onSubmit(f.value)" 

首先,我们使用了#f="ngForm"。#v=thing 语法的意思是,我们希望在当前视图中创建一个局部变量。

这里我们为视图中的ngForm创建了一个别名,并绑定到变量#f。这个ngForm来自哪里呢?它是由NgForm指令导出的。

ngForm是什么类型的对象呢?它是FormGroup类型的。这意味着我们可以在视图中把变量 f 当作FormGroup使用,而这也正是我们在输出事件(ngSubmit)中的使用方法。

我们在表单中绑定ngSubmit事件的语法是:(ngSubmit)="onSubmit(f.value)"。

  • (ngSubmit):来自NgForm指令。

  • onSubmit():将会在我们的组件类中进行定义(稍后)。

  • f.value:f就是我们前面提到的FormGroup,而.value会以键值对的形式返回FormGroup中所有控件的值。

总结起来,这行代码的意思是:“当我提交表单时,将会以该表单的值作为参数,调用组件实例上的onSubmit方法。”

3.2.2. input和NgModel
<input type="text" 
 id="skuInput" 
 placeholder="SKU" 
 name="sku" ngModel> 

NgModel指令指定的selector是ngModel。这意味着我们可以通过添加这个属性把它附加到
input标签上:ngModel="whatever"。在这里我们指定了一个不带属性值的ngModel。

有两种不同的方法能在模板中指定ngModel,这里是第一种。当使用不带属性值的ngModel 时,我们是要指定:
(1) 单向数据绑定;
(2) 希望在表单中创建一个名为sku的FormControl(这个sku来自input标签上的name属性)。

NgModel会创建一个新的FormControl对象,把它自动添加到父FormGroup上(这里也就是form表单对象),并把这个FormControl对象绑定到一个DOM上。也就是说,它会在视图中的input标签和FormControl对象之间建立关联。这种关联是通过name属性建立的,在本例中是"sku"。

注意:

  1. NgModel,指的是类和供代码中引用的对象。

  2. ngModel,来自指令的选择器 selector,并且只会被用在DOM/模板中。

NgModel和FormControl并不是同一个,NgModel是用在视图中的指令, 而FormControl则用来表示表单中的数据和验证规则。

3.3. 简易 SKU 表单:组件定义类

import { Component } from '@angular/core'; 

@Component({ 
  selector: 'demo-form-sku', 

  template: ` 
  <div class="ui raised segment"> 
    <h2 class="ui header">Demo Form: Sku</h2> 
    <form #f="ngForm" 
       (ngSubmit)="onSubmit(f.value)" 
       class="ui form"> 

      <div class="field"> 
       <label for="skuInput">SKU</label> 
       <input type="text" 
         id="skuInput" 
         placeholder="SKU" 
         name="sku" ngModel> 
      </div> 

      <button type="submit" class="ui button">Submit</button> 
    </form> 
  </div> 
  ` 
})

export class DemoFormSku { 
  onSubmit(form: any): void { 
    console.log('you submitted value:', form); 
  } 
} 

4. 使用 FormBuilder

FormBuilder是一个名副其实的表单构建助手。你应该还记得,表单是由FormControl和FormGroup构成的,而FormBuilder则可以帮助我们创建它们(你可以把它看作一个“工厂”对象)。

让我们在先前的例子中添加一个FormBuilder,看看:

  • 如何在组件定义类中使用FormGroup;

  • 如何在视图表单中使用自定义的FormGroup。

我们将使用formGroup和formControl指令来构建这个组件,这意味着我们需要导入相应的类。

import { 
  FormBuilder, 
  FormGroup 
} from '@angular/forms'; 

4.1. 使用 FormBuilder

通过在组件类上声明带参数的constructor,我们注入了一个FormBuilder。

export class DemoFormSkuBuilder { 
  myForm: FormGroup; 

  constructor(fb: FormBuilder) { 
    this.myForm = fb.group({ 
      'sku': ['ABC123'] 
    }); 
  } 

  onSubmit(value: string): void { 
    console.log('you submitted value: ', value); 
  } 
} 

Angular将会注入一个从FormBuilder类创建的对象实例,并把它赋值给fb变量(来自构造函数)。

我们将会使用FormBuilder中的两个主要函数:

  • control,用于创建一个新的FormControl;

  • group,用于创建一个新的FormGroup。

myForm是FormGroup类型。我们通过调用fb.group()来创建FormGroup。.group方法的参数是代表组内各个FormControl的键值对。

在这里,我们设置了一个名为sku的控件,其值为["ABC123"]——意思是控件的默认值为"ABC123"。

我们需要将它绑定到表单元素上。

4.2 在视图中使用myForm

我们希望修改<form>标签,让它使用myForm变量。回忆一下,在上一节中我们提到过,当导入FormsModule时,ngForm就会自动起作用。还提到过ngForm会自动创建它自己的FormGroup。但在这里我们不希望使用外部的FormGroup,而是使用FormBuilder创建的这个myForm实例变量。那该怎么做呢?

Angular提供了另一个指令,能让我们使用现有的FormGroup。它叫作formGroup,可以这样使用。

<h2 class="ui header">Demo Form: Sku with Builder</h2>
<form [formGroup]="myForm" 

这里我们告诉Angular,想用myForm作为这个表单的FormGroup。

注意:
当使用FormsModule时,NgForm会自动应用于<form>元素上。但其实有一个例外:NgForm不会应用到带formGroup属性的<form>节点上。

你也许不明白原因,这是因为NgForm的selector是:

form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]

这意味着你还可以使用 ngNoForm 属性产生一个不带NgForm的<form>表单。

将FormControl绑定到input标签上。记住,ngModel会创建一个新的FormControl对象,并附加到父FormGroup中。但在这个例子中,我们已经用FormBuilder创建了自己的FormControl。
要将现有的FormControl绑定到input上,可以用 formControl

<label for="skuInput">SKU</label> 
<input type="text" 
   id="skuInput" 
   placeholder="SKU" 
   [formControl]="myForm.controls['sku']">

我 们 将 input标 签 上 的 formControl指 令 指 向 了 myForm.controls上 现 有 的FormControl控件sku。

完整代码

import { Component } from '@angular/core'; 
import { 
  FormBuilder, 
  FormGroup 
} from '@angular/forms'; 
@Component({ 
  selector: 'demo-form-sku-builder', 
  template: ` 
  <div class="ui raised segment"> 
    <h2 class="ui header">Demo Form: Sku with Builder</h2> 
    <form [formGroup]="myForm" 
         (ngSubmit)="onSubmit(myForm.value)" 
         class="ui form"> 

      <div class="field"> 
       <label for="skuInput">SKU</label> 
       <input type="text" 
          id="skuInput" 
          placeholder="SKU" 
         [formControl]="myForm.controls['sku']"> 
      </div> 

    <button type="submit" class="ui button">Submit</button> 
    </form> 
  </div> 
  ` 
}) 
export class DemoFormSkuBuilder { 
  myForm: FormGroup; 
  constructor(fb: FormBuilder) { 
    this.myForm = fb.group({ 
      'sku': ['ABC123'] 
    }); 
  } 

  onSubmit(value: string): void { 
    console.log('you submitted value: ', value); 
  } 
} 
需要记住以下两点。
  1. 如果想隐式创建新的FormGroup和FormControl,使用:

  • ngForm

  • ngModel

  1. 如果要绑定一个现有的FormGroup和FormControl,使用:

  • formGroup

  • formControl

5. 添加验证

想使用验证器,我们得做两件事:
(1) 为FormControl对象指定一个验证器;
(2) 在视图中检查验证器的状态,并据此采取行动。

要 为 FormControl对 象 分 配 一 个 验 证 器 , 我 们 可 以 直 接 把 它 作 为 第 二 个 参 数 传 给FormControl的构造函数。

let control = new FormControl('sku', Validators.required);

像这个例子中一样通过如下语法使用FormBuilder:

  constructor(fb: FormBuilder) { 
    this.myForm = fb.group({ 
      'sku': ['', Validators.required] 
    }); 
    this.sku = this.myForm.controls['sku']; 
  } 

6. 监听变化

到目前为止,我们只在提交表单时才调用onSubmit方法来获取表单的值。但我们也要经常监听控件的变化。

FormGroup和FormControl都带有EventEmitter(事件发射器),我们可以通过它来观察变化。

想监听控件的变化,我们要:
(1) 通过调用control.valueChanges访问到这个EventEmitter;
(2) 然后使用.subscribe方法添加一个监听器。

下面是一个例子。

  constructor(fb: FormBuilder) { 
    this.myForm = fb.group({ 
      'sku': ['', Validators.required] 
    }); 

    this.sku = this.myForm.controls['sku']; 

    this.sku.valueChanges.subscribe( 
      (value: string) => { 
         console.log('sku changed to:', value); 
      } 
    ); 

    this.myForm.valueChanges.subscribe( 
      (form: any) => { 
         console.log('form changed to:', form); 
      } 
    ); 

  } 

在这里我们监听了两个事件:sku字段的变化和整个表单的变化。

我们传递了一个带有next键的对象(也可以传递其他键,但目前还不用关心它们)。next就是我们希望当值发生变化时被调用的函数。

如果在输入框中输入kj,就会在控制台中看到:

sku changed to:  k 
form changed to:  Object {sku: "k"} 
sku changed to:  kj 
form changed to:  Object {sku: "kj"} 

如你所见,每一次按键都会触发控件的变化,我们的可观察对象也会被触发。监听单个FormControl时,我们会得到一个值(例如kj);而监听整个表单时,我们会得到一个包含键值对的对象(例如{sku: "kj"})。

7. ngModel

ngModel是一个特殊的指令,它将模型绑定到表单中。ngModel的特殊之处在于它实现了 双向绑定 。
相对于单向绑定来说,双向绑定更加复杂和难以推断。Angular通常的数据流向是单向的:自顶向下。但对于表单来说,双向绑定有时会更容易。

下面对表单稍作修改:我们希望能输入产品名称productName。这次要用ngModel来保持组件实例和视图的同步。

我们的组件定义类如下所示。

export class DemoFormNgModel { 
  myForm: FormGroup; 
  productName: string; 

  constructor(fb: FormBuilder) { 
    this.myForm = fb.group({ 
'productName': ['', Validators.required] 
    }); 
  } 

  onSubmit(value: string): void { 
    console.log('you submitted value: ', value); 
  } 
} 

注意,我们只是简单地将productName: string存成了实例变量。
紧接着,我们在input标签上使用ngModel。

<label for="productNameInput">Product Name</label> 
<input type="text" 
       id="productNameInput" 
       placeholder="Product Name" 
       [formControl]="myForm.get('productName')" 
       [(ngModel)]="productName"> 

注意,这里ngModel的语法很有意思:我们在ngModel属性上同时使用了()和[]。我们既使用了表示输入属性(@Input)的方括号[],又使用了表示输出属性(@Output)的圆括号(),这就是双向绑定的标志

另 外 还 需 要 注 意 的 是 : 我 们 仍 然 用 formControl指 定 此 input应 该 绑 定 到 表 单 上 的FormControl。这是因为ngModel只负责将input绑定到对象实例上,但FormControl的功能是与此独立的。由于我们还需要对这个值加以验证并把它作为表单的一部分提交上去,仍要保留formControl指令。

来自  https://www.jianshu.com/p/ecdef08ac77b

普通分类: