Angular2中自定义组件实现双向绑定

    在Angular2中的数据流动是单向的,我们常见的双向绑定的例子如下:
<input [(ngModel)]="value"/>

等价于

<input [ngModel]="value" (ngModelChange)="valueChange($event)"/>

    那么我们如何实现自定义自己的组件,该组件也可以接受[(ngModel)]来实现双向绑定呢?首先来看组件的定义代码:

@Component({
  selector: 'app-two-way',
  template: `<input [(ngModel)]="value">`,
  styleUrls: ['./two-way.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => TwoWayComponent),
    multi: true,
  }],
})
export class TwoWayComponent implements ControlValueAccessor {
  private innerValue: any = '';
  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: any) => void = noop;

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

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

  writeValue(value: any): void {
    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

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

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

}

    这样在template中使用该组件的时候就可以类似于input标签一样可以接受ngModel和ngModelChange了。下面来解释其中使用的东西:

  • NG_VALIDATORS:为form control提供ControlValueAccessor,所以我们的组件要实现ControlValueAccessor接口。
  • useExisting:避免出现多个provider
  • forwardRef:在依赖注入中,每个组件都需要有个与其关联的token,可是在provider注册的时候,我们的组件还没有初始化。所以需要使用forwardRef来告知provider构造器这些,使其可以等组件初始化。

接下来就是实现ControlValueAccessor接口,主要的函数是:

  • writeValue:将外部传递进来的值写到模板中
  • registerOnChange:该函数接收一个回调函数,用于在数据发生变化时告知外面的世界。

    在组件定义中,我们定义了onTouchedCallback和onChangeCallback,但是方法都是没有任何实现的,这个定义时必须的。而且这两个方法的真正实现都会在组件初始化时由Angular自己来提供。所以我们不必担心这两个方法没有任何实现的。

猜你喜欢

转载自blog.csdn.net/zhaoruda/article/details/79690329