添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

OK!前幾天都在分享、分析 Dependency Injection 的相關 Tip 與原始碼,今天當然也是要繼續啊!

今天要來分享的 Tip,是最近剛得到 Angular GDE 稱號的 Alex Inkin 在 Twitter 上發布的內容:

This article is inspired by the tweet from Alex Inkin, an Angular GDE, posted on Twitter ( link ). Thank him for allowing me to use his code in this article.

@Directive({
  selector: "[validator]",
  providers: [
      provide: NG_VALIDATORS,
      useExisting: ValidatorDirective,
      multi: true
export class ValidatorDirective implements Validator, OnChanges {
  @Input()
  validator: ValidatorFn = () => null;
  private onChange: Function = () => {};
  validate(control: AbstractControl): ValidationErrors | null {
    return this.validator(control);
  registerOnValidatorChange(onChange: Function) {
    this.onChange = onChange;
  ngOnChanges() {
    this.onChange();
  ngOnDestroy() {
    this.validator = () => null;

↑ Block 1

透過重新提供 NG_VALIDATOR 這個內建的 DI Token,就可以免去在 FormControl 上一直 setValidators 的行為。

爬 code 啦

今天的重點是,這邊的 FormControl 是怎麼取用 Validator 的?

首先要看到的是 html 內的 FormControlName Directive,我所關心的實作部分如下(完整版):

@Directive({selector: '[formControlName]', providers: [controlNameBinding]})
export class FormControlName extends NgControl implements OnChanges, OnDestroy {
  // ... 略
  constructor(
      @Optional() @Host() @SkipSelf() parent: ControlContainer,
      @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
      @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:
          Array<AsyncValidator|AsyncValidatorFn>,
      @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[],
      @Optional() @Inject(NG_MODEL_WITH_FORM_CONTROL_WARNING) private _ngModelWarningConfig: string|
      null) {
    super();
    this._parent = parent;
    this._rawValidators = validators || [];
    this._rawAsyncValidators = asyncValidators || [];
    this.valueAccessor = selectValueAccessor(this, valueAccessors);
  // ... 略

↑ Block 2

當使用 FormControlName 他,它的 constructor 會去尋找有沒有適合的 NG_VALIDATORS 可以用,有的話就將它的實體注入,我們後續會再提到 @Optional 與 @Self 還有其他與 Dependency Injection 相關的 decorator。

但是只看到這邊不足以解釋當透過下拉選單選取另一個 validator 時,是怎麼同時觸發驗證的。

來看一下 Angular 表單驗證器一定要實作的這個介面:Validator

export interface Validator {
   * @description
   * Method that performs synchronous validation against the provided control.
   * @param control The control to validate against.
   * @returns A map of validation errors if validation fails,
   * otherwise null.
  validate(control: AbstractControl): ValidationErrors|null;
   * @description
   * Registers a callback function to call when the validator inputs change.
   * @param fn The callback function
  registerOnValidatorChange?(fn: () => void): void;

每一個表單驗證器都必須實作的只有 validate 這個用來做驗證的方法,registerOnValidatorChange 則是選用的方法,也是這一個 Tip 實現的關鍵。

回到 Block 1,它將傳入的 fn 箭頭函式綁定到內部的變數上,並在 ngOnChange 內呼叫,只要我們變動了傳入的 validator,就會呼叫這個 fn。

至於這個 fn 是何方神聖,可以在這邊找到:

export function setUpControl(control: FormControl, dir: NgControl): void {
  // ... 略
  // re-run validation when validator binding changes, e.g. minlength=3 -> minlength=4
  dir._rawValidators.forEach((validator: Validator|ValidatorFn) => {
    if ((<Validator>validator).registerOnValidatorChange)
      (<Validator>validator).registerOnValidatorChange!(() => control.updateValueAndValidity());
  // ... 略

每次呼叫 fn 的時候,就會呼叫 FormControl 的 updateValueAndValidity 方法,觸發驗證!

以上就是今天所要分享的 Tip!以及它的背後原理!

Thank Alex again! Follow him on Twitter to get more Angular tips :)

倒數 24 天!

以下按照入團順序列出我們團隊夥伴的系列文章!

打通 RxJS 任督二脈 深入 Azure 雲端服務 如何用Laravel寫一個簡單的部落格網站 推動資安從0起步邁向70分,不用花大錢也能有聲有色保平安。 Azure Serverless 平步青雲,漫步雲端