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

Posted

技术标签:

【中文标题】为自定义控件实现值访问器时,未从事件中的模型获取更新值【英文标题】:Not getting updated value from model in events when implementing value accessor for custom controls 【发布时间】:2016-09-12 13:35:33 【问题描述】:

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

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

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

这是一个带有示例的 plunker:

https://plnkr.co/edit/ixK6UxhhWZnkFyKfbgky

我做错了什么?

ma​​in.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】:

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

您可以利用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/

【讨论】:

感谢您提供的信息。我不知道 ngModelChange 以及多么棒的文章。我已经尝试过你提出的建议,这里是一个新的plunkr,但奇怪的是,一旦我删除了 get 和 set,它就不再绑定初始值了。你说我把事情搞混了,但是如果不这样做,怎么可能在组件之外获取更新的模型呢?

以上是关于为自定义控件实现值访问器时,未从事件中的模型获取更新值的主要内容,如果未能解决你的问题,请参考以下文章

4.为自定义控件添加事件

c# 为自定义控件添加鼠标双击事件

在为自定义 DataGridViewColumn 设置属性时访问 DataGridView 控件

3.为自定义控件添加属性

2021-11-22 WinFrom面试题 Form中的控件与数据可以实现双向绑定吗?

onClick 事件不适用于 android 中的嵌套列表视图项控件