1

在使用两种不同的方法验证相同的无线电输入元素时,即。模板驱动表单和模型驱动表单,我被这样的场景ngModel困住formControlName了3 个单独的实例。

<!-- template-driven-form.component.html -->
<div class="form-group gender">
  <label for="gender">Select Gender:</label>
  <div class="radio" *ngFor="let gender of genders">
    <input type="radio" name="gender" [value]="gender" ngModel appFormControlValidation validationMsgId="gender" required />
    <label>{{ gender }}</label>
  </div>
</div>

<!-- model-driven-form.component.html -->
<div class="form-group gender">
  <label for="gender">Select Gender:</label>
  <div class="radio" *ngFor="let gender of genders">
    <input type="radio" name="gender" [value]="gender" formControlName="gender" appFormControlValidation validationMsgId="gender" required />
    <label>{{ gender }}</label>
  </div>
</div>
// model-driven-form.component.ts
genders: string[] = ['Male', 'Female', 'Other'];
this.modelForm = new FormGroup({
  gender: new FormControl(null, [Validators.required])
});

// template-driven-form.component.ts
genders: string[] = ['Male', 'Female', 'Other'];

// form-control-directive
(this.control as NgControl).statusChanges.subscribe(
  // returns single instance for 3 radio elements -> template form
  // returns 3 instance for 3 radio elements -> model form
);

从上面的代码片段来看,我对两种表单都使用了相同的 HTML 结构,但实例数量有所不同。这里的问题是,当验证发生时,对于模板驱动的表单,我只收到一次错误消息(这是预期的情况),但对于模型驱动的表单,我收到错误消息显示 3 次!

VALIDATION_SCENARIO

我的问题是:

  1. 为什么为相同的元素类型生成的实例数量与ngModel和不同formControlName
  2. 需要进行哪些更改才能formControlName返回单个实例?

工作Stackbliz版本

4

2 回答 2

0

我建立了一个解决方案,在添加之前检查是否存在应用程序错误,请参阅if (parent.innerHTML.indexOf(componentFactory.selector) < 0)您的 dynamic-component.service

  loadComponentIntoNode(
    vcr: ViewContainerRef,
    dynamicItem: DynamicItem,
    parentNode = null
  ): void {
    if (dynamicItem.component) {
      const parent = parentNode || vcr.element.nativeElement;
      const componentFactory =
        this.componentFactoryResolver.resolveComponentFactory(
          dynamicItem.component
        );
      if (parent.innerHTML.indexOf(componentFactory.selector) < 0) {
        const componentRef = vcr.createComponent(componentFactory);
        const newChild =
          componentRef.injector.get(ErrorComponent).elementRef.nativeElement;
        this.renderer.appendChild(parent, newChild);
        (componentRef.instance as DynamicComponent).data = dynamicItem.data;
      }
    }
  }

问题是,如果您检查 statusChange ,在模板驱动的表单中,作为观察者,您在无线电中拥有这么多元素的数组,以及模型驱动表单中的一个元素的数组,但我找不到关于此的解决方案

于 2021-11-14T15:54:07.847 回答
0

@AndreiGatej 回答 - https://github.com/indepth-dev/community/discussions/53

描述
经过快速排查,实际预计会得到三个实例。具体来说,statusChangesfromthis.statusChangeSubscription = this.control?.statusChanges?.subscribe()被订阅了 3 次。如果您有N单选按钮,那么您最终会N得到该指令的实例。这意味着尽管您有N实例,但它们都将注入相同的 NgControl实例(可以是NgModelFormControlNameFormControlDirective。在反应形式的情况下,该NgControl实例是:

gender: new FormControl(null, [Validators.required]),

使用时显然不会发生这种情况的原因NgModel是,在使用模板驱动表单时,表单控制指令是动态创建的。这是设置NgModel指令的流程:

  1. this.formDirective.addControl(this).
  2. ' NgModels被添加到表单指令树中此标记NgControl的末尾。原因可以在这里找到。
  3. 有趣的是,虽然每个NgModel指令固有地创建一个唯一的FormControl实例,但在指令树中只有一个FormControl实例将被所有NgModel共享相同名称的所有实例共享。FormControl是指示将共享单个实例的逻辑:
if (this.controls[name]) return this.controls[name];
   this.controls[name] = control;
   control.setParent(this);
   control._registerOnCollectionChange(this._onCollectionChange);
   return control;

registerControlresolvedPromise在promise 解决后调用。它也在那里dir.control被分配给任何registerControl返回。

因此,该指令在该承诺(本质上是)解决之前ngOnInit被调用。在当前滴答结束时,所有 NgModel 指令将共享同一个实例。这就是它按预期工作的原因。Promise.resolve()FormControl

于 2021-11-15T15:08:17.797 回答