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

南笙酒味 提交于 2019-12-24 01:45:44

问题


I was following the article below and I'm trying to implement a custom control in angular 2 integrated with ngModel and ngControl.

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

But I'm having a hard to time to figure out how to get the updated model value when emitting events. It seems to use the model before the update in the event.

Here is a plunker with an example:

https://plnkr.co/edit/ixK6UxhhWZnkFyKfbgky

What am I doing wrong?

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);
  }
}

custom-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);
    }

}

回答1:


I think that the problem is that you mix a custom output and registered callbacks. In this case, it's not necessary to have a custom output.

You could leverage the ngModelChange event within the CustomInput component to call the _onChangeCallback callback:

@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);
  }
}

In this case, your getter and setter aren't necessary anymore.

This article could also interest you (section "NgModel-compatible component"):

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


来源:https://stackoverflow.com/questions/37259397/not-getting-updated-value-from-model-in-events-when-implementing-value-accessor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!