其他分享
首页 > 其他分享> > Angular Form 的数据流

Angular Form 的数据流

作者:互联网

Angular Form 总观

先给总结,再谈细节

Form 的两种使用场景与区别

React FormTemplate Form,使用的例子如下,这个是官方用例
下面的是三个React Form的使用场景

// React Form
import { Component } from "@angular/core";
import { FormControl } from "@angular/forms";

@Component({
  selector: "app-reactive-favorite-color",
  template: `
    Favorite Color: <input type="text" [formControl]="favoriteColorControl" />
  `,
})
export class FavoriteColorComponent {
  favoriteColorControl = new FormControl("");
}
// 这个是一个相对复杂一点的ReactForm 的例子
import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";

@Component({
  selector: "example-app",
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <div *ngIf="first.invalid">Name is too short.</div>
      <input formControlName="first" placeholder="First name" />
      <input formControlName="last" placeholder="Last name" />
      <button type="submit">Submit</button>
    </form>
    <button (click)="setValue()">Set preset value</button>
  `,
})
export class SimpleFormGroup {
  form = new FormGroup({
    first: new FormControl("Nancy", Validators.minLength(2)),
    last: new FormControl("Drew"),
  });

  get first(): any {
    return this.form.get("first");
  }

  onSubmit(): void {
    console.log(this.form.value); // {first: 'Nancy', last: 'Drew'}
  }

  setValue() {
    this.form.setValue({ first: "Carson", last: "Drew" });
  }
}
// Template Driven Form
import { Component } from "@angular/core";

@Component({
  selector: "app-template-favorite-color",
  template: `
    Favorite Color: <input type="text" [(ngModel)]="favoriteColor" />
  `,
})
export class FavoriteColorComponent {
  favoriteColor = "";
}
// 复杂一点的Template Driven Form 的例子
import { Component } from "@angular/core";
import { NgForm } from "@angular/forms";
@Component({
  selector: "example-app",
  template: `
    <form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
      <input name="first" ngModel required #first="ngModel" />
      <input name="last" ngModel />
      <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>
  `,
})
export class SimpleFormComp {
  onSubmit(f: NgForm) {
    console.log(f.value); // { first: '', last: '' }
    console.log(f.valid); // false
  }
}

这个例子里面遇到几个关键字[formControl]=xxx,[formGroup]=xxx,formControlName=xxx
NgForm NgModel
那么这些个关键字干了啥事,它们是怎么做到的。

// FormControl 源码片段
@Directive({
  selector: "[formControl]",
  providers: [formControlBinding],
  exportAs: "ngForm",
})
export class FormControlDirective
  extends NgControl
  implements OnChanges, OnDestroy
{
  @Input("formControl") form!: FormControl;
  constructor(
    @Optional()
    @Self()
    @Inject(NG_VALIDATORS)
    validators: (Validator | ValidatorFn)[],
    @Optional()
    @Self()
    @Inject(NG_ASYNC_VALIDATORS)
    asyncValidators: (AsyncValidator | AsyncValidatorFn)[],
    @Optional()
    @Self()
    @Inject(NG_VALUE_ACCESSOR)
    valueAccessors: ControlValueAccessor[],
    @Optional()
    @Inject(NG_MODEL_WITH_FORM_CONTROL_WARNING)
    private _ngModelWarningConfig: string | null
  ) {
    super();
    this._setValidators(validators);
    this._setAsyncValidators(asyncValidators);
    this.valueAccessor = selectValueAccessor(this, valueAccessors);
  }
}
//DEFAULT_VALUE_ACCESSOR 源码
export const DEFAULT_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => DefaultValueAccessor),
  multi: true,
};
@Directive({
  selector:
    "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]",
  // TODO: vsavkin replace the above selector with the one below it once
  // https://github.com/angular/angular/issues/3011 is implemented
  // selector: '[ngModel],[formControl],[formControlName]',
  host: {
    "(input)": "$any(this)._handleInput($event.target.value)",
    "(blur)": "onTouched()",
    "(compositionstart)": "$any(this)._compositionStart()",
    "(compositionend)": "$any(this)._compositionEnd($event.target.value)",
  },
  providers: [DEFAULT_VALUE_ACCESSOR],
})
export class DefaultValueAccessor
  extends BaseControlValueAccessor
  implements ControlValueAccessor
{
  /** Whether the user is creating a composition string (IME events). */
  private _composing = false;

  constructor(
    renderer: Renderer2,
    elementRef: ElementRef,
    @Optional()
    @Inject(COMPOSITION_BUFFER_MODE)
    private _compositionMode: boolean
  ) {
    super(renderer, elementRef);
    if (this._compositionMode == null) {
      this._compositionMode = !_isAndroid();
    }
  }

  /**
   * Sets the "value" property on the input element.
   * @nodoc
   */
  writeValue(value: any): void {
    const normalizedValue = value == null ? "" : value;
    this.setProperty("value", normalizedValue);
  }

  /** @internal */
  _handleInput(value: any): void {
    if (!this._compositionMode || (this._compositionMode && !this._composing)) {
      this.onChange(value);
    }
  }

  /** @internal */
  _compositionStart(): void {
    this._composing = true;
  }

  /** @internal */
  _compositionEnd(value: any): void {
    this._composing = false;
    this._compositionMode && this.onChange(value);
  }
}

下面是来自于NgModel的源码片段。

// 注册方法,当ControlValueAccess 值变化,View 该怎么变化, dir: NgModel,
function setUpViewChangePipeline(control: FormControl, dir: NgControl): void {
  dir.valueAccessor!.registerOnChange((newValue: any) => {
    control._pendingValue = newValue;
    control._pendingChange = true;
    control._pendingDirty = true;

    if (control.updateOn === 'change') updateControl(control, dir);
  });
}

// 这个是FormControl值变化的时候,ControlValueAccessor 的响应。
function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
  const onChange = (newValue: any, emitModelEvent: boolean) => {
    // control -> view
    dir.valueAccessor!.writeValue(newValue);

    // control -> ngModel
    if (emitModelEvent) dir.viewToModelUpdate(newValue);
  };
  control.registerOnChange(onChange);

  // Register a callback function to cleanup onChange handler
  // from a control instance when a directive is destroyed.
  dir._registerOnDestroy(() => {
    control._unregisterOnChange(onChange);
  });
}

function updateControl(control: FormControl, dir: NgControl): void {
  if (control._pendingDirty) control.markAsDirty();
  control.setValue(control._pendingValue, {emitModelToViewChange: false});
  dir.viewToModelUpdate(control._pendingValue);
  control._pendingChange = false;
}

//**************************NgModel************************
// NgModel 内部的一个方法,主要就是发出(onModelChange)事件
 override viewToModelUpdate(newValue: any): void {
    this.viewModel = newValue;
    this.update.emit(newValue);
  }

  ngOnChanges(changes: SimpleChanges) {
    this._checkForErrors();
    if (!this._registered) this._setUpControl();
    if ('isDisabled' in changes) {
      this._updateDisabled(changes);
    }

    if (isPropertyUpdated(changes, this.viewModel)) {
      this._updateValue(this.model);
      this.viewModel = this.model;
    }
  }
  // 这时一个微任务,会在CD之后做。
  private _updateValue(value: any): void {
    resolvedPromise.then(() => {
      this.control.setValue(value, {emitViewToModelChange: false});
    });
  }

//**************************NgModel************************

标签:control,FormControl,Form,DOM,value,._,数据流,Angular
来源: https://www.cnblogs.com/kongshu-612/p/15455650.html