2018 年 3 月

第 33 卷,第 3 期

此文章由机器翻译

孜孜不倦的程序员 - 如何成为 MEAN:验证 Angular

通过Ted Neward |年 3 月 2018

Ted Neward我们又见面了,MEAN 用户们。

在以前的列中,我可以开始查看窗体控件和输入的角的支持。双向绑定花费顶部计费,但如何验证输入的任何意义上-若要确保发言人具有第一个和最后一个名称,例如,而不只是空字符串-遗留。在本月的列中,我们需要解决,因为在不验证数据输入基本上仅仅让用户注入到你的系统,新的垃圾回收保留为你进行排序。

而且,如我们大多数人所知,可能会不正确。Like、 商标字实际上,Really Bad™ 程度。

SpeakerUI 回顾

在最后一列中,我 ditched 支持 SpeakerUI,也不能将环绕在扬声器模型实例知道 SpeakerEdit 组件-基于传递到其中的内容-它处于只读的状态用于查看扬声器传出的讲解或可编辑状态。它使用两个 < div > 部分 (其中之一隐藏根据组件处于的状态) 以保持 UI 不同 (请参阅图 1)。只读部分需要任何验证,当然,因为没有用户输入;它是可编辑部分的问题我们此处。

图 1 SpeakerUI 组件

<form #speakerForm="ngForm">
  <div [hidden]="readonly">
    FirstName: <input name="firstName" type="text"
      [(ngModel)]="model.firstName"><br>
    LastName:  <input name="lastName" type="text"
      [(ngModel)]="model.lastName"><br>
      Subjects: {{model.subjects}}<br>
    <button (click)="save()"
      [disabled]="!speakerForm.form.dirty">Save</button>
    <button (click)="cancel()"
      [disabled]="!cancellable(speakerForm)">Cancel</button>
    <br><span>{{diagnostic}}</span>
  </div>
  ...
</form>

第一个需要注意的事项是,最后一个月的列,这,我移动逻辑用于确定是否编辑模式可以取消 (如何禁用取消按钮的属性绑定的注意) 组件本身上的方法。这可能有点太过严厉,在此特定情况,但它确实可以说明角的一个重要方面,则不需要执行所有直接在该模板本身内的用户界面逻辑操作。应取消逻辑变得很复杂,具有在模板中,可能是个好主意,但我们需要将窗体对象 (speakerForm 对象定义上个月) 在组件代码中可用。

这需要使用新的模块已不存在组件中的一个:NgForm。

NgForm

NgForm 是专门用于处理窗体角中定义的类。它包含在单独的模块的其余部分的角度的核心,因此它需要检索独立导入:

import { NgForm } from '@angular/forms';

当在运行时使用窗体,角构造的对象的集合,表示各种控件和窗体本身,并使用它来执行验证和其他处理。此对象集合通常隐藏为方便起见,在幕后,但始终可供使用的角度开发人员。

一旦传递到可取消的方法时,你可以使用窗体对象检查通过多个属性的形式的状态。NgForm 脏,定义无效、 原始、 接触、 不变和有效的属性,以表示全套处理该窗体的不同的用户交互状态。出于演示目的,我将向窗体的可编辑部分中添加更多的诊断行:

<br>Pristine: {{speakerForm.form.pristine}}
Dirty: {{speakerForm.form.dirty}}
Touched: {{speakerForm.form.touched}}
Untouched: {{speakerForm.form.untouched}}
Invalid: {{speakerForm.form.invalid}}
Valid: {{speakerForm.form.valid}}

这些只需将显示的每个状态,当用户交互时处理该窗体,并帮助说明什么每表示。例如,"着"意味着-真正 — 用户尚未接触的窗体中任何方式。只需单击 (或处理,移动设备上) 编辑字段,以便显示光标没有足以呈现为正在"接触。"表单 但是,如果没有键入发生,即使该窗体"指针",它是仍然"原始"。 其他在此就不一一列举了。

有效性,如可能在预料中,建议用户已违反了某种类型的开发人员已规定的数据输入约束。角度看起来以生成从标准 HTML5 有效性约束,因此,例如,如果你决定发言人必须具有第一个和最后一个的名称,只需使用"必需"的特性的编辑字段:

FirstName: <input name="firstName" type="text"
  [(ngModel)]="model.firstName" required><br>
LastName:  <input name="lastName" type="text"
  [(ngModel)]="model.lastName" required><br>

有鉴于此,如果或用户编辑现有扬声器并清除任一 firstName lastName 完全编辑字段,无效的状态翻转为 true,为 false 的有效状态,因为角识别存在所需的标志。话虽如此,尽管角不执行任何其他操作-现成的角不提供任何内置 UI,以指示的格式无效。它是最多向开发人员可以向该字段需要关注某种方式中的用户发出信号。这可以在有许多种情况下,所有依赖于开发人员使用的 UI 支持。例如,它时很常见使用 Bootstrap CSS 框架来标记为通过着色它 (或它的某些部分) 需要关注的表单域红色。或者,不少见具有隐藏的文本范围下面或之后将以某种方式违反了约束和平分跨度的隐藏特性应用到该窗体的状态时显示的字段。

但引发一细微点-你想要知道哪个控件在窗体是无效的以便您可以将链接到该控件的直接反馈。幸运的是,NgForm 控件属性,它是 NgControl 对象的数组,并且初始屏幕 (如 firstName 和 lastName) 窗体中定义的每个控件将具有 NgControl 实例来表示它。因此,你可以引用直接在隐藏的属性的模板表达式内这些控件对象:

FirstName: <input name="firstName" type="text"
  [(ngModel)]="model.firstName" required>
<span [hidden]="speakerForm.controls.firstName.valid">
  Speakers must have a first name</span><br>
LastName:  <input name="lastName" type="text"
  [(ngModel)]="model.lastName" required>
<span [hidden]="speakerForm.controls.firstName.valid">
  Speakers must have a first name</span><br>

公正性迫使我地接受此代码具有细微问题 — 当运行时,它将产生在运行时的一些错误。这是因为在组件的最早阶段 NgForm 尚未构造的 NgControl 对象的集合,因此表达式、 speakerForm.controls.firstName,将是未定义。

若要避免此问题的简单方法是定义控件,本地模板变量而不是经过窗体的控件数组,并使用 * ngIf 指令进行测试,以查看是否该窗体是接触或已更新,并且如果是这样,是否有效:

FirstName: <input name="firstName" type="text"
  [(ngModel)]="model.firstName" #firstName="ngModel"
  required>
<div *ngIf="firstName.invalid &&
            (firstName.dirty || firstName.touched)">
  <div *ngIf="firstName.errors.required">
    A first name is required.
  </div>
</div>

实质上,这消除了需要完成 speakerForm,但有助于你了解 speakerForm 对象可以访问的我们在运行时,尽管显示某些警告。

自定义验证

在 HTML5 标准不在其中定义验证这些情况下你希望或不需要您可以编写自定义验证程序可将调用以测试相关的字段的角度允许。例如,多年前,我们假设我在名为 Josh 扬声器愉快的体验。我不喜欢 Josh。永远不会进行了。我不会关心让专家是我们的数据库的一部分,因此我希望不允许特定输入的自定义窗体验证程序。(显然,这是一个非常 pedantic 示例,但此处的概念保留的几乎任何类型的验证程序,它无法构思。)

如其他验证,角想尝试与"点击中"的 HTML 语法尽可能多地,这意味着,甚至是自定义验证程序不会显示如 HTML5 验证程序,因此 forbiddenName 验证程序应出现类似于任何其他 HTML 验证规则:

FirstName: <input name="firstName" type="text"
  [(ngModel)]="model.firstName" #firstName="ngModel"
  required forbiddenName="josh">

在模板中,你只需添加所需 * ngIf 指令以测试该窗体包含禁止的名称指定,而如果是这样,显示特定的错误消息:

<div *ngIf="firstName.invalid &&
            (firstName.dirty || firstName.touched)">
  <div *ngIf="firstName.errors.required">
    A first name is required.
  </div>
  <div *ngIf="firstName.errors.forbiddenName">
    NO JOSH!
  </div>
</div>

好了。应该保留 (查看你 Josh) 任何不需要发言人出。

自定义验证程序指令

为了实现此目的,角需要我们为角速度指令,这是一种挂接到 HTML 查看语法元素,如的 forbiddenName 指令中的输入的字段,或需要或甚至一些角度代码的方法中编写验证程序 * ngIf。指令处于功能非常强大,非常超过我这里拥有完全浏览此聊天室。但我可以至少阐释如何验证程序工作,因此,让我们首先创建一个新的指令,使用"ng 生成指令 ForbiddenValidator"命令行中,并将其出搭建基架禁止-validator.directive.ts:

import { Directive } from '@angular/core';
@Directive({
  selector: '[appForbiddenValidator]'
})
export class ForbiddenValidatorDirective {
  constructor() { }
}

@Directive 中的选择器是你想要在 HTML 模板中使用的语法和坦白地说,appForbiddenValidator 实际上不会获取赛车的核心。它应该是稍微清除中其使用情况,如 forbiddenName 某项内容。此外,该指令需要点击到现有集合的验证程序-无需深入过多的详细信息,@Directive 的提供程序参数包含必要的样本要提供给较大 ForbiddenValidatorDirective验证程序的集合:

@Directive({
  selector: '[forbiddenName]',
  providers: [{provide: NG_VALIDATORS,
               useExisting: ForbiddenValidatorDirective,
               multi: true}]
})

接下来,该指令需要实现验证程序接口,从而提供一种方法,请验证该-可能猜到-时验证需要执行的操作调用。但是,验证函数的结果不是结果的某种类型的通过/失败的验证,但要使用来执行验证自身的函数:

export class ForbiddenValidatorDirective implements Validator {
  @Input() forbiddenName: string;
  validate(control: AbstractControl): {[key: string]: any} {
    if (this.forbiddenName) {
      const nameRe = new RegExp(this.forbiddenName, 'i');
      const forbidden = nameRe.test(control.value);
      return forbidden ? {'forbiddenName': {value: control.value}} : null;
    } else {
      return null;
    }
  }
}

从根本上说,角将指导完成的验证程序的列表,如果它们全都返回 null,则所有内容是 kosher 和所有输入都被都视为有效。如果其中任何返回除此之外的任何内容,它具有被视为验证错误组的一部分,并且添加到模板中引用的错误集合 * ngIf 语句前面。

如果验证程序具有 forbiddenName 值,该值是什么从传入的指令的使用情况,则没有要完成的验证-组件的输入可用于构造 RegExp 实例 (使用不区分大小写匹配"i"),并随后 RegExp 测试方法用于检查控件的值是否与 name-which-shall-not-be-accepted 匹配。如果已存在,然后使用密钥 forbiddenName (这是什么模板已使用更早版本来确定是否显示错误消息,请记住),并在控件的输入的值构建一组。否则,返回 null,指令手和窗体的验证程序的其余部分会触发。如果所有这些将返回 null,则所有内容都合法。

总结

角的验证支持,则你可以看出,非常广泛的全功能。它将生成执行文件位于每个 HTML5 浏览器中,但提供一定程度的是可扩展的并且在需要时功能强大的运行时控件支持的标准 HTML 验证支持。大多数应用程序将查找内置的验证程序足以应对大多数情况下,但能够创建自定义验证程序意味着角可以根据需要为复杂处理用户输入时。(这很好,因为用户不断地查找到新的方法来尝试输入到系统的垃圾回收。我责怪 Josh 该。) 仍有一些隐藏"@angular/forms"模块中之前在这里,我们已完成这样敬请关注的多个意外事件。

祝您工作愉快!


Ted Neward是基于西雅图的 polytechnology 顾问、 发言人和导师,当前正在作为开发人员关系在总监Smartsheet.com。他已写大量的文章,创作和合著十几项丛书,并于世界各地说出。可通过 ted@tedneward.com 与他联系,也可阅读他的博客 blogs.tedneward.com

感谢以下 Microsoft 技术专家:Garvice Eakins


在 MSDN 杂志论坛讨论这篇文章