[英]How to trigger change() in a angular form by a custom control without an input
I do want to create a custom control which does not include any input.我确实想创建一个不包含任何输入的自定义控件。 Whenever the control changes, I do want to save the complete form.每当控件更改时,我都想保存完整的表单。
Our current approach uses the form-changed-event like this:我们当前的方法使用这样的表单更改事件:
<form #demoForm="ngForm" (change)="onChange()">
<custom-input name="someValue" [(ngModel)]="dataModel">
</custom-input>
</form>
As you can see, we use the "change"-event to react to any change in the form.如您所见,我们使用“更改”事件来响应表单中的任何更改。 This works fine as long as we have inputs, checkboxes, ... as controls.只要我们有输入、复选框……作为控件,这就可以正常工作。
But our custom control does only exist out of a simple div we can click on.但是我们的自定义控件只存在于我们可以单击的简单 div 之外。 Whenever I click on the div the value of the control is increased by 1. But the "change"-event of the form is not fired.每当我单击 div 时,控件的值都会增加 1。但是不会触发表单的“更改”事件。 Do I somehow have to link my custom control to the form?我是否必须以某种方式将我的自定义控件链接到表单? Or are there any events which need to be fired?或者是否有任何事件需要触发?
import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
@Component({
selector: 'custom-input',
template: `<div (click)="update()">Click</div>`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}]
})
export class CustomInputComponent implements ControlValueAccessor {
private onTouchedCallback: () => void = () => {};
private onChangeCallback: (_: any) => void = () => {};
update(){
this.value++;
}
get value(): any {
return this.innerValue;
};
set value(v: any) {
console.log("Change to");
if (v !== this.innerValue) {
this.innerValue = v;
this.onChangeCallback(v);
}
}
writeValue(value: any) {
if (value !== this.innerValue) {
this.innerValue = value;
}
}
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
}
I've created a plunker to demonstrate the problem: https://plnkr.co/edit/ushMfJfcmIlfP2U1EW6A我创建了一个 plunker 来演示这个问题: https ://plnkr.co/edit/ushMfJfcmIlfP2U1EW6A
Whenever you click on "Click" the model-value is increased, but there is no output on the console, as the change-event is not fired... (There is a console.log linked to the change-event)每当您单击“单击”时,模型值都会增加,但控制台上没有输出,因为未触发更改事件...(有一个 console.log 链接到更改事件)
Thanks for your replies.感谢您的回复。
Finally I found the following solution to this problem: As Claies mentioned in the comment, my custom component does not fire the change event.最后我找到了这个问题的以下解决方案:正如 Claies 在评论中提到的,我的自定义组件不会触发 change 事件。 Therfore the form does never know about the change.因此,表格永远不会知道更改。 This has nothing todo with angular, but as said is the expected behaviour of a input/form.这与角度无关,但如前所述,这是输入/表单的预期行为。
The easiest solution is to fire the change-event in the customcontrol when a change happens:最简单的解决方案是在发生更改时触发自定义控件中的更改事件:
constructor(private element: ElementRef, private renderer: Renderer) {
}
public triggerChanged(){
let event = new CustomEvent('change', {bubbles: true});
this.renderer.invokeElementMethod(this.element.nativeElement, 'dispatchEvent', [event]);
}
That's it, whenever I called "onControlChange(..)" in my custom component, then I fire this event afterward.就是这样,每当我在自定义组件中调用“onControlChange(..)”时,我都会在之后触发此事件。
Be aware, that you need the Custom-Event-Polyfill to support IE!请注意,您需要 Custom-Event-Polyfill 来支持 IE! https://www.npmjs.com/package/custom-event-polyfill https://www.npmjs.com/package/custom-event-polyfill
You need to emit the click event of div to its parent.您需要将 div 的点击事件发送到其父级。 so that you can handle the event.以便您可以处理该事件。
Parent component:父组件:
import { Component, forwardRef, Output, EventEmitter } from '@angular/core'; // add output and eventEmitter
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
@Component({
selector: 'custom-input',
template: `<div (click)="update($event)">Click</div>`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}]
})
export class CustomInputComponent implements ControlValueAccessor {
private onTouchedCallback: () => void = () => {};
private onChangeCallback: (_: any) => void = () => {};
@Output() clickEvent = new EventEmitter(); // add this
update(event){
this.value++;
this.clickEvent.emit(event); // emit the event on click event
}
get value(): any {
return this.innerValue;
};
}
child component:子组件:
//our root app component
import {Component} from '@angular/core'
@Component({
selector: 'demo-app',
template: `
<p><span class="boldspan">Model data:</span> {{dataModel}}</p>
<form #demoForm="ngForm">
<custom-input name="someValue"
[(ngModel)]="dataModel" (clickEvent) = onChange()> // handling emitted event here
Write in this wrapper control:
</custom-input>
</form>`
})
export class AppComponent {
dataModel: string = '';
public onChange(){
console.log("onChangeCalled");
}
}
Thanks Stefan for pointing me in the right direction.感谢 Stefan 为我指明了正确的方向。
Unfortuantely Renderer (which has invokeElementMethod()
) has recently been deprecated in favor or Renderer2 (which does not have that method)不幸的是,Renderer(具有invokeElementMethod()
)最近已被弃用,取而代之的是 Renderer2(没有该方法)
So the following worked for me所以以下对我有用
this.elementRef.nativeElement.dispatchEvent(new CustomEvent('change', { bubbles: true }));
It seems that change
event is not fired on form when you call ControlValueAccessor
onChange callback (callback passed in registerOnChange
function), but valueChanges
observable (on the whole form) is triggered.当您调用ControlValueAccessor
onChange 回调(回调在registerOnChange
函数中传递)时,似乎不会在表单上触发change
事件,但会触发valueChanges
observable(在整个表单上)。
Instead of:代替:
...
<form (change)="onChange()">
...
you can try to use:你可以尝试使用:
this.form.valueChanges
.subscribe((formValues) => {
...
});
Of course, you must get proper form reference in your component.当然,您必须在组件中获得正确的表单引用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.