NgModel指令学习笔记

简介: NgModel指令学习笔记

NgModel 指令使用场景比较多,还会和 NgForm 结合使用,所以非常有必要单独写一篇学习笔记。


NgModel 根据领域对象创建一个 FormControl 实例,并把它绑定到一个表单控件元素上。


官方说明:


这个 FormControl 实例将会跟踪值、用户交互和控件的验证状态,以保持视图与模型的同步。 如果用在某个父表单中,该指令还会把自己注册为这个父表单的子控件。

这个指令可以单独使用,也可以用作一个大表单的一部分。你所要做的一切就是用 ngModel 选择器来激活它。

它可以接受一个领域模型作为可选的 Input。如果使用 [] 语法来单向绑定到 ngModel,那么在组件类中修改领域模型将会更新视图中的值。 如果使用 [()] 语法来双向绑定到 ngModel,那么视图中值的变化会随时同步回组件类中的领域模型。


在独立控件模式下使用 ngModel


如果你希望查看与 FormControl 相关的属性(比如校验状态),你也可以使用 ngModel 作为键,把该指令导出到一个局部模板变量中(如:#myVar="ngModel")。 你也可以使用该指令的 control 属性来访问此控件,实际上你要用到的大多数属性(如 validdirty)都会委托给该控件,这样你就可以直接访问这些属性了。 你可以在 AbstractControlDirective 中直接查看这些属性的完整列表。 如下所示:


abstract class AbstractControlDirective {
  abstract control: AbstractControl | null
  value: any
  valid: boolean | null
  invalid: boolean | null
  pending: boolean | null
  disabled: boolean | null
  enabled: boolean | null
  errors: ValidationErrors | null
  pristine: boolean | null
  dirty: boolean | null
  touched: boolean | null
  status: string | null
  untouched: boolean | null
  statusChanges: Observable<any> | null
  valueChanges: Observable<any> | null
  path: string[] | null
  reset(value: any = undefined): void
  hasError(errorCode: string, path?: string | (string | number)[]): boolean
  getError(errorCode: string, path?: string | (string | number)[]): any
}
复制代码


下面是一个在简单的独立控件中使用 ngModel 的例子:


import {Component} from '@angular/core';
@Component({
  selector: 'example-app',
  template: `
    <input [(ngModel)]="name" #ctrl="ngModel" required>
    <p>Value: {{ name }}</p>
    <p>Value: {{ ctrl.value }}</p>
    <p>Valid: {{ ctrl.valid }}</p>
    <button (click)="setValue()">Set value</button>
  `,
})
export class SimpleNgModelComp {
  name: string = '';
  setValue() { this.name = 'Nancy'; }
}
复制代码


页面测试:


1.jpg


在表单中使用 ngModel


当在 <form> 标签中使用 ngModel 时,你还需要提供一个 name 属性,以便该控件可以使用这个名字把自己注册到父表单中。


在父表单的上下文中,通常不用包含单向或双向绑定,因为这个父表单将会为你同步该值。 你可以使用 ngForm 把它导出给一个模板局部变量(如 #f="ngForm"),以访问它的属性。 可以在任何需要提交表单的地方使用它。


如果你只是要为表单设置初始值,对 ngModel 使用单向绑定就够了。在提交时,你可以使用从表单导出的值,而不必使用领域模型的值。


下面的例子展示了如何在表单中使用 ngModel


import {Component} from '@angular/core';
import {NgForm} from '@angular/forms';
@Component({
  selector: 'example-app',
  template: `
    <h2>ngForm中使用 ngModel</h2>
    <div>
      <form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
        <input name="first" ngModel required #first="ngModel">
        <br>
        <input name="last" ngModel>
        <br>
        <button>Submit</button>
      </form>
      <p>First name value: {{ first.value }}</p>
      <p>First name valid: {{ first.valid }}</p>
      <p>Form value: {{ f.value | json }}</p>
      <p>Form valid: {{ f.valid }}</p>
      <div [hidden]="!f.valid">
        <p>{{submitMessage }}</p>
      </div>
    </div>
  `,
})
export class SimpleFormComp {
   submitMessage = '';
  onSubmit(f: NgForm) {
    console.log(f.value);  // { first: '', last: '' }
    console.log(f.valid);  // false
    this.submitMessage = '数据已提交';
  }
}
复制代码


注意:单独 ngModel 的作用是通知 ngForm.value,我要向你那里加入一个 property,其 key 值是组件的 name属性值,其 value 为空字符串。 所以如果没有为 ngModel 赋值的话,则必须存在 name 属性。


页面测试:


1.jpg


在表单组中使用独立 ngModel


使用带有“ngModel"的”“标签时,系统会自动为这个标签创建一个叫做”FormControl"的对象,并且会自动把它添加到”FormGroup"中。而“FormControl"在”FomGroup“中是用""标签上的”name"属性来做标识的。


<form #f="ngForm">
  <input type="text" ngModel name="firstField">
  <span>{{ f.controls['firstField']?.value }}</span>
</form>
复制代码


如果没有使用“name”这个属性,那么将会报错:


Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
复制代码


解决方法除了把“name”属性添加上外,还有第二种选择,就是给""标签设置一个 ngModelOptions。如下:


<form #f="ngForm">
  <input type="text" ngModel [ngModelOptions]="{standalone: true}">
  <span>{{ f.controls['firstField']?.value }}</span>
</form>
复制代码


当设置了这个属性,的 FormControl 对象就不会添加到FormGroup内,也就不能通过


{{ f.controls['firstField']?.value }} 索引到该对象的值了。
复制代码


通过选项设置 ngModel 的 name 属性


在讲解该案例前,需要创建一个自定义表单控件,这里我直接将相关代码列举出来,具体讲解我会在参考文献处标注。


首先需要创建一个组件 formcontrol,修改 formcontrol.component.ts:


import { Component, Input, forwardRef } from '@angular/core';
import {
  ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS,
  AbstractControl, ValidatorFn, ValidationErrors, FormControl
} from '@angular/forms';
@Component({
  selector: 'form-control',
  templateUrl: './formcontrol.component.html',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => FormcontrolComponent),
    multi: true
  }]
})
export class FormcontrolComponent implements ControlValueAccessor {
  @Input() _count: number = 0;
  propagateOnChange: (value: any) => void = (_: any) => { };
  propagateOnTouched: (value: any) => void = (_: any) => { };
  ngOnInit() { }
  get count() {
    return this._count;
  }
  set count(value: number) {
    this._count = value;
    this.propagateOnChange(this._count);
  }
  writeValue(value: any) {
    if (value) {
      this.count = value;
    }
  }
  registerOnChange(fn: any) {
    this.propagateOnChange = fn;
  }
  registerOnTouched(fn: any) {
    this.propagateOnTouched = fn;
  }
  increment() {
    this.count++;
  }
  decrement() {
    this.count--;
  }
}
复制代码


对应的 formcontrol.component.html :


<div>
  <p>当前值: {{ count }}</p>
  <button (click)="increment()"> + </button>&nbsp;&nbsp;
  <button (click)="decrement()"> - </button>
</div>
复制代码


然后在 simple-form.component.html 中使用自定义控件。


<form #form="ngForm">
  <form-control name="counter" ngModel></form-control>
  <button type="submit">Submit</button>
  <br>
  <span>counter value: {{ form.controls['counter']?.value }}</span>
</form>
复制代码


页面测试:


1.jpg


上述代码是自定义表单控件的实现方式,基于该案例验证 ngModel 的另外一种使用场景。


下面的例子展示了设置 name 属性的另一种方式。该 name 属性要和自定义表单组件一起使用,而该自定义组件的 @Input 属性 name 已用作其它用途。


<form #form="ngForm">
  <form-control name="counter" ngModel [ngModelOptions]="{name: 'counter2'}"></form-control>
  <button type="submit">Submit</button>
  <br>
  <span>counter value: {{ form.controls['counter']?.value }}</span>
  <br>
  <span>counter2 value: {{ form.controls['user']?.value }}</span>
</form>
复制代码


页面测试:


1.jpg


总结



关于 NgModel 的使用场景很多,尤其会结合 NgForm 指令使用,所以搞清楚每个场景下的含义尤为重要,对于自己编写 Angular 代码有很大的帮助。以上内容为个人参考官方文档做的学习笔记,如有错误,望不吝赐教。

目录
相关文章
|
存储 Shell 开发者
E906的指令|学习笔记
快速学习 E906的指令
533 0
|
6月前
|
Kubernetes 监控 容器
k9s常用的指令
K9s 是一个用于 Kubernetes 群集管理的命令行工具,它提供了一系列常用的指令,用于查看、管理和监控 Kubernetes 资源。以下是一些常用的 K9s 指令: 1. **查看资源列表:** - `:po`:查看 Pod 列表。 - `:svc`:查看 Service 列表。 - `:deploy`:查看 Deployment 列表。 - `:ns`:查看 Namespace 列表。 2. **在资源列表中的操作:** - 使用上下箭头键浏览资源列表。 - `Enter` 键进入资源的详细信息视图。 - `d`:删除选定的资源。
361 4
|
Kubernetes API 容器
2022-10-13-k8s的操作指令
2022-10-13-k8s的操作指令
97 0
v-if 指令
1.v-if指令的作用是:根据表达式的真假切换元素的显示状态 2.本质是通过操纵dom元素来切换显示状态 3.表达式的值为true,元素存在于dom树中,为false,从dom树中移除 4.频繁的切换使用v-show,反之使用v-if,前者的切换消耗小
|
JavaScript 前端开发
3、指令(v-if与v-for的区别、各种指令的使用)
3、指令(v-if与v-for的区别、各种指令的使用)
148 0
3、指令(v-if与v-for的区别、各种指令的使用)
|
Go Docker 容器
Dockfile指令
笔记:Dockfile指令
198 0
|
前端开发 安全 JavaScript
内置指令
内置指令
135 0
|
JavaScript 开发者 索引
v-for 指令的四种使用方式 | 学习笔记
快速学习 v-for 指令的四种使用方式
129 0
|
存储 移动开发 网络协议
AT 指令做 MTQQ 连接 | 学习笔记
快速学习 AT 指令做 MTQQ 连接
408 0
|
网络协议 NoSQL C语言