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



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

文章: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);
  }
}

自定义输入组件.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兼容组件"部分):

  • http://restlet.com/blog/2016/02/17/implementing-angular2-forms-beyond-basics-part-2/

最新更新