更新 Angular 6 表单值,以防对应的 dom 本机元素值被外部库修改

Updating Angular 6 Form value in case corresponding dom native element value is modified by external library

我正在尝试与 Angular 6 Forms.In 共同浏览一个开源库 togetherJS 更新远程相应元素的 DOM 值 party.I 已一起更新JS相关index.html 中的代码(两行一行在头部,另一行在正文)我观察到使用 ControlValueAccessor 表单类型 DOM 相应元素的值正在更新,但 FormControl 值未在视图中更新。 我的问题是外部库对 DOM 元素所做的更改如何反映到视图中的 angular 6 表单控件的元素值中。

可以从下方获取代码link: https://github.com/srigaurav1986/Angular-Forms.git

如何重现:
1.Download 上面的代码 link.
2.Install 使用 "npm install"
的依赖项 3.Run"ng serve -o"
4.open 在浏览器中“http://localhost:4200/controlvalueaccessor
5.Click 在 "Start TogetherJs"
6.Copy 在另一个浏览器 window 中弹出 link。
7.Update "Name" 字段

我们可以看到 DOM 字段值也在远程端更新,但在按下 "Submit" 按钮后我们可以看到 FormControl 值在远程端保持不变但在另一端发生了变化。

我尝试使用 application.tick、markforcheck() 和 detectchanges() api 手动检测更改,但没有 luck.Is 可以在 DOM 上收听某些事件的方法元素更改并订阅它,并在这种情况下更新 Formcontrol 参数值。

这个问题的答案在于 angular(6) 属性 它适用于影子 DOM 并且只监听 angular 区域内发生的变化,当像 TogetherJS 这样的第三方库更新时 DOM 相应的更改不会影响 angular 组件,因为它们没有订阅实际的 DOM 本机元素。

为了解决这个问题,我们做了以下工作:

  • 在表单 class 构造函数中注册一个回调以捕获 DOM “change” 从共同浏览库触发的事件,即发生在 [=55 之外的事件=] 如下所述的区域:

代码片段

constructor(private formBuilder: FormBuilder,private elementRef: ElementRef,private _renderer: Renderer,private cdr:ChangeDetectorRef,private  app:ApplicationRef,private zone:NgZone) {
            zone.runOutsideAngular(() =>{

            window.document.addEventListener('change', this.change.bind(this));

        })
    }
  • 定义事件处理程序以执行以下操作:
    • 运行 在 Angular 上下文中使用 this.zone.run()
    • 使用 ElementRef 获取组件的模板选择器。
    • 运行 在输入元素上查询选择器并与事件的 outerHTML 进行比较以检查组件中哪个元素已更改。
    • 为匹配元素设置表单控件的值。

PS:这里customerForm是ControlValueAccessor FormGroup类型的实例。在您的情况下,它可以是您的表格。我们可以概括形式(如果是反应式)密钥遍历,如另一个 SO post 中所述

代码片段:

  change(event){
        event.preventDefault();
        console.log("DOM value changed" ,event);
        console.log("component value", this.elementRef.nativeElement);
        this.zone.run(() => { console.log('Do change detection here');
        //this.cdr.detectChanges();
        if(this.elementRef.nativeElement.querySelectorAll('input')[0].outerHTML === event.target.outerHTML)
        {
            console.log('Inside value updation');
            //this.customerForm.controls.name.value=event.target.value;
            this.customerForm.controls['name'].setValue(event.target.value);
        }
    });
        setTimeout(() =>{
            this.cdr.markForCheck();

        })

    }

这将设置组件中更改的元素的相应值(显然是通过遍历循环),并且验证不应失败,因为它发生在当前上下文中。 以上细节的核心思想是如何捕获 angular 区域外发生的变化事件并相应地更新 angular 应用程序。

PS : 我将更新 github 中的完整代码以供其他人阅读。