首页 文章

Angular Dynamic表单可观察的属性绑定

提问于
浏览
3

我有一些动态生成的表单和传递值的问题 . 我觉得有人必须解决这个问题,或者我错过了一些明显的东西,但我找不到任何提及它的东西 .

例如,我有三个组成部分,父母,孩子,然后是孩子的孩子 . 对于名称,我将使用,formComponent,questionComponent,textBoxComponent . 两个孩子都在使用changeDetection.OnPush .

因此,表单组件通过输入将一些值传递给questionComponent,而一些组件使用异步管道来订阅商店中它们各自的值 .

QuestionComponent动态创建不同的组件,然后将它们放在页面上(如果它们匹配)(这么多类型的组件,但每个questionComponent只处理一个组件) .

一些代码:

@Input() normalValue
@Input() asyncPipedValue
@ViewChild('questionRef', {read: ViewContainerRef}) public questionRef: any;
private textBoxComponent: ComponentFactory<TextBoxComponent>;

ngOnInit() {
let component = 
this.questionRef.createComponent(this.checkboxComponent);
component.instance.normalValue = this.normalValue;
component.instance. asyncPipedValue = this. asyncPipedValue;
}

这适用于normalValues的所有实例,但不适用于asyncValues . 我可以在questionComponent的ngOnChanges中确认该值正在更新,但该值未传递给textBoxComponent .

我基本上需要的是异步管道,但不适用于模板 . 我尝试了多种方法来传递asyncValues,我尝试检测asyncPipeValue何时更改,并在textBoxComponent上触发changeDetectionRef.markForChanges(),但只有在我将changeDetectionStrategy更改为normal时才会有效,这有点会影响性能我从使用ngrx获得的收益 .

这似乎太过疏忽而没有解决方案,所以我假设这只是我没有想到的东西 . 有什么想法吗?

3 回答

  • 0

    我做了类似的事情,我从我的Ngrx商店的数据填充表格 . 我的表格不是动态的,所以我不能100%确定这是否也适合你 .

    仅使用setter定义输入,然后在窗体/窗体控件上调用patchValue()或setValue() . 您的根组件保持不变,使用异步管道将数据传递到下一个组件 .

    @Input() set asyncPipedValue(data) {
      if (data) {
        this.textBoxComponent.patchValue(data);
      }
    }
    

    patchValue()在AbstractControl类上 . 如果您无法访问问题组件中的那个,则TextBoxComponent可以公开一个类似的方法,可以从QuestionComponent调用,并且实现执行控件的更新 .

    但要注意的一件事是,如果您还在表单/控件上订阅valueChanges,则可能需要设置第二个参数,以便valueChanges事件不会立即触发 .

    this.textBoxComponent.patchValue(data, { emitEvent: false });
    

    要么

    this.textBoxComponent.setValue(...same as above);
    

    然后在TextBoxComponent中

    this.myTextBox.valueChanges
      .debounceTime(a couple of seconds maybe)
      .distinctUntilChanged()
      .map(changes => {
        this.store.dispatch(changes);
      })
      .subscribe();
    

    这种方法运行良好,无需在任何地方保存/更新按钮 .

  • 0

    我相信我已经找到了解决方案(在gitter.com/angular Channels 的帮助下) .

    由于值将进入questionComponent可以更改,并触发它的ngOnChanges触发,只要ngOnChanges中有事件,它就需要解析整个事件,并绑定并更改为动态子组件 .

    ngOnChanges(event) {
    if (this.component) {
      _.forEach(event, (value, key) => {
        if (value && value.currentValue) {
          this.component.instance[key] = value.currentValue;
        }
      });
     }
    }
    

    这都是在QuestionComponent中,如果它们已经改变,它会重置组件实例变量 . 到目前为止,最大的问题是孩子的ngOnChanges没有触发,所以这不是一个完整的解决方案 . 我会继续深入研究它 .

  • 1

    以下是我对这个问题的看法,考虑到有限的代码片段 .

    首先,提供的示例似乎与 ngrx 没有任何关系 . 在这种情况下,预计 ngOnInit 仅运行一次,此时 this.asyncPipedValue 值为 undefined . 因此,如果 this.checkboxComponentthis.checkboxComponentChangeDetection.OnPush ,则该值将不会更新 . 我建议阅读有关更改检测和传递异步输入的one excellent article . 该文章还包含有关变更检测的其他资源 . 此外,似乎相同的输入在组件树中传递两次,从我的角度来看这不是一个好的解决方案 .

    其次,另一种方法是使用 ngrx 然后根本不需要传递任何异步输入 . 特别是,如果两个组件在组件树中没有父子关系,则这种方式很好 . 在这种情况下,一个组件调度将数据放入Store的操作,另一个组件从Store订阅该数据 .

    export class DataDispatcherCmp {
    
        constructor(private store: Store<ApplicationState>) {
        }
    
        onNewData(data: SomeData) {
            this.store.dispatch(new SetNewDataAction(data));
        }
    }
    
    export class DataConsumerCmp implements OnInit {
    
        newData$: Observable<SomeData>;
    
        constructor(private store: Store<ApplicationState>) {
        }
    
        ngOnInit() {
            this.newData$ = this.store.select('someData');
        }
    }
    

    希望这有助于或至少提供一些线索 .

相关问题