zoukankan      html  css  js  c++  java
  • Angular 学习笔记 (Custom Accessor + Mat FormField + Custom select)

    custom form control 之前就写过了,这里简单写一下.

    创建一个组件实现 ControlValueAccessor 接口

    @Component({
      providers: [
        { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: MyInputComponent },
      ],
    })
    export class MyInputComponent implements ControlValueAccessor {}

    实现 writeValue, model -> view 的时候被调用的,这里实现如何更新 view. 如果时 OnPush 记得要 markForCheck

    writeValue(value: any): void {
      console.log('writeValue');
      this.cdr.markForCheck();
      this.value = value;
    }

    实现 registerOnChange 方法. view -> model 内部通过调用这个方法向外输出值

    private onChangeFn: (value: any) => void;
    registerOnChange(fn: MyInputComponent['onChangeFn']): void {
      this.onChangeFn = fn;
    }

    实现 registerOnTouched 方法, view -> model, 当内部 touched 了通知外部

    private onTouchedFn: () => void;
    registerOnTouched(fn: MyInputComponent['onTouchedFn']): void {
      console.log('registerOnTouched');
      this.onTouchedFn = fn;
    }

    实现 setDisabledState 方法, model -> view, 当外部设定 disabled 后, 内部更新 view 

    setDisabledState(isDisabled: boolean): void {
      console.log('setDisabledState');
      this.cdr.markForCheck();
      this.disabled = isDisabled;
    }

    执行的顺序是 ngOnInit-> DoCheck -> writeValue-> registerOnChange -> registerOnTouched -> setDisabledState -> ngAfterContentInit ...

    实现好了 customer accessor 现在我们来把它放进 mat form filed 里

    refer https://material.angular.io/guide/creating-a-custom-form-field-control

    要做到这一点, 组件必须实现 MatFormFieldControl 里面有 14 个接口要实现的.

    @Directive()
    export abstract class MatFormFieldControl<T> {
      value: T | null;
      readonly stateChanges: Observable<void>;
      readonly id: string;
      readonly placeholder: string;
      readonly ngControl: NgControl | null;
      readonly focused: boolean;
      readonly empty: boolean;
      readonly shouldLabelFloat: boolean;
      readonly required: boolean;
      readonly disabled: boolean;
      readonly errorState: boolean; 
      readonly controlType?: string; 
      readonly autofilled?: boolean;
      abstract setDescribedByIds(ids: string[]): void;
      abstract onContainerClick(event: MouseEvent): void;
    }

    还需要提供 provider, 这里要注意 : 由于我们的组件本身就是 value accessor 所以我们需要动一点手脚, 删除 provide NG_VALUE_ACCESSOR 改用 ngControl.valueAccessor = this 的方式去做.

    @Component({
      providers: [
        { provide: MatFormFieldControl, useExisting: MyInputComponent },
        // { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: MyInputComponent }, 
      ],
    })
    export class MyInputComponent implements MatFormFieldControl<Value> {
      constructor(
        @Optional() @Self() public ngControl: NgControl,
    
      ) {
        if (ngControl != null) {
          ngControl.valueAccessor = this;
        }
      }
    }

    其它部分都很好理解. 

    stateChanges 是用来通知 form field 要 mark for check 的, 当我们内部修改后,要通知它就用这个.

    要记得释放

    ngOnDestroy() {
      this.stateChanges.complete();
      this.focusMonitor.stopMonitoring(this.hostElement.nativeElement);
    }

    其它的都是一些属性. 一般上会使用 getter setter 去维护更新,比如

    public get focused(): boolean {
      return this._focused;
    }
    public set focused(v: boolean) {
      this._focused = v;
      this.stateChanges.next();
    }
    private _focused: boolean;

    比如

    get empty() {
      return this.value === '';
    }
    
    get shouldLabelFloat() {
      return this.focused || !this.empty;
    }

    关于 focus 通常使用 monitor 来监听

    focusMonitor.monitor(hostElement.nativeElement, true).subscribe(origin => {
      this.focused = origin === null ? false : true;
    });
    onContainerClick
    当用户点击内部组件时, 这个也会触发哦,可以通过 event.target 确认用户点击的是内部组件或者真的是外部的 container 做相应的处理
    onContainerClick(event: MouseEvent) {
      // if ((event.target as Element).tagName.toLowerCase() != 'input') {
      //   this.elRef.nativeElement.querySelector('input').focus();
      // }
    }
    errorState 是一个比较烦人的东西
    这个是用在做什么时候需要显示 error 的情况.form field 本身是不处理这个的,是交由 accessor 管理的. 
    material 有一个叫 errorStateMatcher 的 class 我们的组件最好也可以支持这样的设定,这样就比较统一. 
    下面是 input 的做法.
       <input matInput placeholder="Email" [formControl]="emailFormControl"
               [errorStateMatcher]="matcher">
     
     
  • 相关阅读:
    剑指offer--2.替换空格
    剑指offer--1.二维数组中的查找
    poj-1426-Find The Multiple(打表水过)
    hdoj-3791-二叉搜索树(二叉搜索树模板题)
    hdoj-1276-士兵队列训练问题(队列模拟)
    HihoCoder
    CodeForces-831A-Unimodal Array (水题)
    hdoj-1046-Gridland(规律题)
    hdoj-1038-Biker's Trip Odometer(水题)
    hdoj-1037-Keep on Truckin'(水题)
  • 原文地址:https://www.cnblogs.com/keatkeat/p/11941939.html
Copyright © 2011-2022 走看看