为自定义控件实现值访问器时,未从事件中的模型获取更新值

Not getting updated value from model in events when implementing value accessor for custom controls

我正在关注下面的文章,我正在尝试在与 ngModel 和 ngControl 集成的 angular 2 中实现自定义控件。

文章:http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

但我很难弄清楚如何在发出事件时获取更新后的模型值。活动中好像用的是更新前的模型

这里有一个例子:

https://plnkr.co/edit/ixK6UxhhWZnkFyKfbgky

我做错了什么?

main.ts

import {bootstrap}    from '@angular/platform-browser-dynamic';
import {App} from './app';

bootstrap(App, [])
  .catch(err => console.error(err));

app.ts

import {Component} from '@angular/core'
import {FORM_DIRECTIVES} from "@angular/common";
import {CustomInput} from './custom-input.component'

@Component({
  selector: 'my-app',
  providers: [],
  template: `
  <form (ngSubmit)="onSave()" #demoForm="ngForm">

   <div class="row info-row">
    <span class="col-xs-12">
    <p><span class="boldspan">Form data:</span>{{demoForm.value | json}}</p>
    <p><span class="boldspan">Model data:</span> {{dataModel}}</p>
    </span>
    </div>

    <custom-input ngControl="someValue" ref-input (onKeyDown)="onKeyDown(input)" [(ngModel)]="dataModel">Enter data:</custom-input>

  </form>
  `,
  directives: [CustomInput, FORM_DIRECTIVES]
})
export class App {
  dataModel: string = '';

  onKeyDown(event){
    console.log(event._value);
    console.log(this.dataModel);
  }
}

自定义-input.component.ts

import {Component, Provider, forwardRef, Input, Output, EventEmitter} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR, CORE_DIRECTIVES} from "@angular/common";

const noop = () => {};

const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR = new Provider(
  NG_VALUE_ACCESSOR, {
    useExisting: forwardRef(() => CustomInput),
    multi: true
  });

@Component({
  selector: 'custom-input',
  template: `
      <div class="form-group">
        <label><ng-content></ng-content>
          <input class="form-control" [(ngModel)]="value" (keydown)="onKeyDownEvent($event)" (blur)="onTouched()">
        </label>
      </div>
  `,
  directives: [CORE_DIRECTIVES],
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CustomInput implements ControlValueAccessor{

    @Output() onKeyDown: EventEmitter<any> = new EventEmitter();

    //The internal data model
    private _value: any = '';

    //Placeholders for the callbacks
    private _onTouchedCallback: (_:any) => void = noop;

    private _onChangeCallback: (_:any) => void = noop;

    //get accessor
    get value(): any { return this._value; };

    //set accessor including call the onchange callback
    set value(v: any) {
      if (v !== this._value) {
        this._value = v;
        this._onChangeCallback(v);
      }
    }

    //Set touched on blur
    onTouched(){
      this._onTouchedCallback();
    }

    //From ControlValueAccessor interface
    writeValue(value: any) {
      this._value = value;
    }

    //From ControlValueAccessor interface
    registerOnChange(fn: any) {
      this._onChangeCallback = fn;
    }

    //From ControlValueAccessor interface
    registerOnTouched(fn: any) {
      this._onTouchedCallback = fn;
    }

    onKeyDownEvent(event){
      this.onKeyDown.emit(event);
    }

}

我认为问题在于您混合了自定义输出和已注册的回调。在这种情况下,不需要自定义输出。

您可以利用 CustomInput 组件中的 ngModelChange 事件来调用 _onChangeCallback 回调:

@Component({
  selector: 'custom-input',
  template: `
    <div class="form-group">
      <label><ng-content></ng-content>
        <input class="form-control" [(ngModel)]="value" 
           (ngModelChange)="onModelChange($event)" 
           (keydown)="onKeyDownEvent($event)"
           (blur)="onTouched()">
      </label>
    </div>
`,
(...)
export class CustomInput implements ControlValueAccessor {
  (...)

  onModelChange(value) {
    this.value = value;
    this._onChangeCallback(value);
  }
}

在这种情况下,您的 getter 和 setter 就不再需要了。

这篇文章也可能会让您感兴趣(第 "NgModel-compatible component" 部分):