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">
 
 

最新文章

  1. stm8s103串口
  2. OC中面向对象2
  3. filter的详细配置
  4. elk实战分析nginx日志文档
  5. JavaWeb总结--Servlet 工作原理解析
  6. SQLServer中在视图上使用索引(转载)
  7. HDU 5311 Hidden String (暴力)
  8. background image position问题
  9. Jackson ObjectMapper类
  10. html5中的meta标签
  11. Timer 的学习
  12. 剑指offer 4.树 重建二叉树
  13. 从零开始学安全(十四)●Windows Server 2012 R2 本地搭建FTP服务器
  14. Python编程:从入门到实践(选记)
  15. oracle中 trunc 处理日期的用法
  16. HTML和CSS标签常用命名规则
  17. [转]TDD之Dummy Stub Fake Mock
  18. 虚函数与bind 实现设计模式的练习
  19. 查看apk包名和Activity名
  20. Hibernate之一对一关联映射

热门文章

  1. Java_jdbc 基础笔记之三 数据库连接 (Statement)
  2. zabbix ALL = NOPASSWD: ALL
  3. 算法的时间复杂度O
  4. spring2.5 jdk1.8
  5. synchronized 同步对象概念
  6. [译]使用Pandas读取大型Excel文件
  7. matlab基本函数 randn,rand,orth
  8. 【minikube &amp; helm】
  9. react 生命周期图解
  10. shell每隔一秒钟就记录下netstat状态