请问:Angular 模版驱动表单,在组件类中通过 @ViewChild 注入的表单模型(NgForm)对象为什么会变化?

发布于 2022-09-05 08:49:22 字数 2620 浏览 14 评论 0

最近在 angular.io 上看 Angular Form 部分的文档,看到 Template-driven Forms 章节中的一段示例代码,不太明白:

import { Component, AfterViewChecked, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
 
import { Hero }      from '../shared/hero';
 
@Component({
  selector: 'hero-form-template2',
  templateUrl: './hero-form-template2.component.html'
})
export class HeroFormTemplate2Component implements AfterViewChecked {
 
  powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
 
  hero = new Hero(18, 'Dr. WhatIsHisWayTooLongName', this.powers[0], 'Dr. What');
 
  submitted = false;
 
  onSubmit() {
    this.submitted = true;
  }
 
  addHero() {
    this.hero = new Hero(42, '', '');
  }
 
  heroForm: NgForm;
  @ViewChild('heroForm') currentForm: NgForm;
 
  ngAfterViewChecked() {
    this.formChanged();
  }
 
  formChanged() {
    if (this.currentForm === this.heroForm) { return; }
    this.heroForm = this.currentForm;
    if (this.heroForm) {
      this.heroForm.valueChanges
        .subscribe(data => this.onValueChanged(data));
    }
  }
 
  onValueChanged(data?: any) {
    if (!this.heroForm) { return; }
    const form = this.heroForm.form;
 
    for (const field in this.formErrors) {
      // clear previous error message (if any)
      this.formErrors[field] = '';
      const control = form.get(field);
 
      if (control && control.dirty && !control.valid) {
        const messages = this.validationMessages[field];
        for (const key in control.errors) {
          this.formErrors[field] += messages[key] + ' ';
        }
      }
    }
  }
 
  formErrors = {
    'name': '',
    'power': ''
  };
 
  validationMessages = {
    'name': {
      'required':      'Name is required.',
      'minlength':     'Name must be at least 4 characters long.',
      'maxlength':     'Name cannot be more than 24 characters long.',
      'forbiddenName': 'Someone named "Bob" cannot be a hero.'
    },
    'power': {
      'required': 'Power is required.'
    }
  };
}

其中这一段:

heroForm: NgForm;
@ViewChild('heroForm') currentForm: NgForm;

ngAfterViewChecked() {
  this.formChanged();
}

formChanged() {
  if (this.currentForm === this.heroForm) { return; }
  this.heroForm = this.currentForm;
  if (this.heroForm) {
    this.heroForm.valueChanges
      .subscribe(data => this.onValueChanged(data));
  }
}

向大家请教一下:

  1. currentForm 这个对象引用为什么会变化?

  2. 为什么要在 ngAfterViewChecked 方法中判断通过 @ViewChild('heroForm') 注入的表单模型对象 currentForm 是否发生了变化?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

踏雪无痕 2022-09-12 08:49:22

首先这是一种很不值得提倡的奇淫技巧,看上面的代码像是为了收集错误到固定的对象当中,没有看到HTML模板,我想只是为了单纯方便而已吧。

其次为什么说是不提倡呢?Angular本身提供模板驱动表单外,还包括模型驱动表单,居然在类中已经有构建一堆错误消息了,那还不如直接用模型驱动表单更为实在。当然这也是Angular推荐的,原因是表单易可测试性也是非常重要的。

最后,回答你的两个问题。

currentForm 表示表单对象,虽然在 ngAfterViewChecked 中每一次子组合的变更都会执行一次,但实则只会执行一次,因为:

if (this.currentForm === this.heroForm) { return; }

我想如果把这一段代码放进 ngOnInit 也是可以的。对于 this.heroForm.valueChanges 而言,只要表单对象存在的情况下订阅 valueChanges 不管放在哪都可以。

以上!

江挽川 2022-09-12 08:49:22

表单模版:

<form #heroForm="ngForm" *ngIf="active" (ngSubmit)="onSubmit()">

验证了一下,之所以使用 ngAfterViewChecked,是为了应对 *ngIf="active",所以需要做以下判断:

if (this.currentForm === this.heroForm) { return; }

感谢 @cipchk 的回答。

想请教一下,开发一般的简单表单,是否首选使用模版驱动的表单方式?感觉会要清晰很多。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文