hao同学的技术博客

  • 首页
  • Java
    • Java
    • JVM教程
    • Java面试
    • Java并发入门
    • Java并发进阶
  • 项目
    • 从零打造项目
  • Python
    • Python
    • Python爬虫
    • 算法
  • Java框架
    • Spring
    • SpringBoot
  • 前端
    • Angular
  • 其他
    • Linux
    • SQL
  • 随笔
分享技术,记录人生
一个痴迷于技术的厨艺爱好者
  1. 首页
  2. Angular
  3. 正文

Angular之NgModel指令学习

2022年5月28日 434点热度 0人点赞 0条评论

Angular之NgModel指令学习插图

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

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

官方说明:

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

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

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

在独立控件模式下使用 ngModel

如果你希望查看与 FormControl 相关的属性(比如校验状态),你也可以使用 ngModel 作为键,把该指令导出到一个局部模板变量中(如:#myVar="ngModel")。 你也可以使用该指令的 control 属性来访问此控件,实际上你要用到的大多数属性(如 valid 和 dirty)都会委托给该控件,这样你就可以直接访问这些属性了。 你可以在 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'; }
}

页面测试:

ngModel demo

在表单中使用 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 属性。

页面测试:

ngModel测试

在表单组中使用独立 ngModel

使用带有“ngModel"的 < input> 标签时,系统会自动为这个标签创建一个叫做”FormControl"的对象,并且会自动把它添加到”FormGroup"中。而“FormControl"在”FomGroup“中是用"< input>"标签上的”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”属性添加上外,还有第二种选择,就是给"< input>"标签设置一个 ngModelOptions。如下:

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

当设置了这个属性,< input>的 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>

页面测试:

Angular之NgModel指令学习插图3

上述代码是自定义表单控件的实现方式,基于该案例验证 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>

页面测试:

Angular之NgModel指令学习插图4

总结

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

参考文献

Angular学习笔记 ——input 标签上的【name属性】和【ngModelOptions属性】

官方文档

自定义表单控件 ControlValueAccessor接口

Angular ControlValueAccessor - 自定义表单控件介绍与实战

angular自定义表单控件(转)

Angular 自定义表单控件 -- CheckboxGroupComponent

本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可
标签: Angular 前端
最后更新:2022年5月28日

hresh

这是一个专注于IT技术学习交流的个人技术博客网站,包括Java学习、Python爬虫、Web开发实践等领域,深耕Java领域,内容涵盖Java基础、Java并发编程、Java虚拟机、Java面试等核心知识点。

点赞
< 上一篇
下一篇 >

文章评论

取消回复

hresh

这是一个专注于IT技术学习交流的个人技术博客网站,包括Java学习、Python爬虫、Web开发实践等领域,深耕Java领域,内容涵盖Java基础、Java并发编程、Java虚拟机、Java面试等核心知识点。

文章目录
  • 在独立控件模式下使用 ngModel
  • 在表单中使用 ngModel
  • 在表单组中使用独立 ngModel
  • 通过选项设置 ngModel 的 name 属性
  • 总结
  • 参考文献
最新 热点 随机
最新 热点 随机
后端必知:遵循Google Java规范并引入checkstyle检查 Spring Security结合Redis实现缓存功能 Spring Security结合JWT实现认证与授权 Spring Security自定义认证逻辑实现图片验证码登录 Spring Security进阶学习 Spring Security入门学习
Java面试准备之Spring框架系列三 Spring IoC之AbstractBeanFactory 数据爬取 js 分析:对加密参数进行 js分析 从 SpringBoot 1.x 升级到 2.x 的时候所踩的坑 Python 回溯法 子集树模块系列——八皇后问题 后端必知:遵循Google Java规范并引入checkstyle检查

COPYRIGHT © 2022 hao同学的技术博客. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

鄂ICP备2022007381号

鄂公网安备 42010302002449号