简体   繁体   English

如何在没有输入的情况下通过自定义控件以角度形式触发 change()

[英]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.以便您可以处理该事件。

Plunker Link插件链接

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.

相关问题 如何以角度形式将元素更改为输入或下拉? - How to change element to input or dropdown in angular form? 如何从角度的自定义窗体控件指令获取a的父窗体? - How to get the parent form of a from a custom form control directive in angular? Angular 2 - 自定义表单控件 - 禁用 - Angular 2 - Custom Form Control - Disable 更改表单控件 Angular 中的数据 - Change data in form control Angular 如何控制表单中的输入字符串,在Angular js中包含子字符串? - how I control the input string in a form , contain a substring in Angular js? 如何在应用程序中为 Angular Forms 制作带有表单控制输入的通用组件 - How to make generic component with form control input for Angular Forms in app 如何在不提交表单的情况下获取输入控件的价值? - How to get value of input control outside of a form without submitting it? 在以角4反应形式进行控件初始化后,如何添加自定义验证? - How to add custom validation after control initialization in angular 4 reactive form? 如何在Angular 5 6 7中使用本机元素HTMLElement创建自定义表单控件 - How to create custom form control using native element HTMLElement in angular 5 6 7 angular 在不使用 setErrors 的情况下将错误设置为表单控件并使其在表单组自定义验证器中无效 - angular set error to form control and make it invalid in form group custom validator without the use of setErrors
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM