我们通常使用ngModel在模板上双向绑定数据,但是ngModel只作用于基本标签,我们自定义的组件没办法直接使用。

如果我们要把input标签封装成一个组件,也是可以使用ngModel的,需要这个组件实现ControlValueAccessor接口,并且注入NG_VALUE_ACCESSOR

import { ControlValueAccessor } from '@angular/forms';
import { Component, OnInit, Input, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-login-input',
  templateUrl: './login-input.component.html',
  styleUrls: ['./login-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => LoginInputComponent),
      multi: true
    }
  ]
})
export class LoginInputComponent implements OnInit, ControlValueAccessor {

  constructor() { }
  @Input() title: string;
  @Input() placeholder: string;
  @Input() type: 'text' | 'number' | 'password';

  private innerValue: any = '';

  private onTouchedCallback = () => { };
  private onChangeCallback = (_: any) => { };

  ngOnInit() {
  }

  get value(): any {
    return this.innerValue;
  }

  set value(v: any) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  writeValue(value: any) {

    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }
  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }

}

这样这个组件就能使用ngModel了。

<app-login-input [title]="'用户名'" [type]="'text'" [placeholder]="'请输入用户名'" [(ngModel)]="userName"></app-login-input>