为自定义控件实现值访问器时,未从事件中的模型获取更新值
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" 部分):
我正在关注下面的文章,我正在尝试在与 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" 部分):