【问题标题】:How to implement Form Projection in Angular?如何在 Angular 中实现表单投影?
【发布时间】:2020-11-25 16:05:02
【问题描述】:

我一直在尝试按照 Kara Erickson 于 2017 年在 Angular Connect 上发表的演讲在 Angular 中实现表单投影,但到目前为止还没有成功。 link to talk

不幸的是,唯一可用的代码出现在幻灯片中并且不完整,因此很难解决。 这是我尝试过的,但它抛出错误:https://stackblitz.com/edit/angular-form-projection-3

表单投影的想法是您有一些包装组件,其模板包含一个<form> 元素,并且您将表单内容投影到其中(谈话中的示例是表单步进器) :

<!-- FormStepper -->
<form>
   <ng-content></ng-content>
</form>

<!-- containing template, e.g. AppComponent -->
<form-stepper>
   <div ngModelGroup="address">
      <input name="street" ngModel/>
      <input name="city" ngModel/>
   </div>
</form-stepper>

诸如此类的幼稚实现会导致错误,因为 ngModelGroup 指令希望在表单内,但由于组件边界而无法找到表单指令(ControlContainer 类)。

根据 Kara 的说法,解决方案是从 FormStepperComponent 的提供者中提供 ControlContainer 类。使用 @ViewChild() 装饰器查询,您从组件的视图中获取表单指令并将其存储在实例属性中,例如form.

  // within FormStepper
  @ViewChild(NgForm) form: NgForm;

然后在 providers 数组中,使用 useFactory 为 ControlContainer 配置一个提供者,这样当它被请求时,它会返回表单实例:

  providers: [
    {
      provide: ControlContainer,
      useFactory: component => {
        return component.form;
      },
      deps: [FormStepperComponent]
    }
  ],

FormStepperComponent 内容子项中的指令应该能够注入提供者数组中提供的服务。这应该允许 ngModelGroup 获得对其所需表单的引用。

问题是工厂函数在视图初始化之前运行,因此此时 component.form 是未定义的。

我还创建了这个演示,它演示了内容投影以及视图初始化、内容初始化和工厂运行的顺序。

https://stackblitz.com/edit/angular-projection-experiment

您可以在控制台中看到工厂提供程序在视图初始化之前运行,因此当指令尝试注入表单时,它只会得到undefined

看来本次演讲中的建议是不可能的?

【问题讨论】:

  • 是的,她提到了这个问题,还在那里添加了 TempleteOutlet。可能是你检查是否解决
  • 我对此进行了实验。看起来它可能是解决方案,但在我得到它之前我不能确定。
  • 我已经更新了问题以考虑到 templateOutlet。它似乎没有任何区别。我不清楚为什么它甚至应该这样做。
  • 看看这里github.com/cloudnc/ngx-sub-form :) 我们在工作中构建并开源了它,我想这就是你要找的东西

标签: javascript angular


【解决方案1】:

您的工厂没有定义 NgForm 视图子视图的原因是 NgForm ViewChild 查询将在 更改检测运行后默认进行查询。您需要更新 NgForm ViewChild 查询以运行 before 更改检测运行。这将确保在您的工厂运行时定义 form 属性。您可以通过将属性 static 添加到查询并将其设置为 true 来做到这一点,即,

@ViewChild(NgForm, { static: true }) form: NgForm;

有关 ViewChild 的文档对此选项进行了更深入的介绍:https://angular.io/api/core/ViewChild

我相信当这个演讲发表时,Angular 的 ViewChild 会在默认情况下在更改检测运行之前进行查询。此后,Angular 已更新为在默认情况下运行更改检测后进行查询。太糟糕了,他们没有为本次演讲提供代码示例并使用最新的 Angular 进行更新。

我复制了您的示例以使其工作:https://stackblitz.com/edit/angular-projection-experiment-uxzdee

【讨论】:

  • 很遗憾,您分叉了错误的示例!那是我为演示形式投影而创建的。问题是内容子项和视图子项的实例化顺序而不是更改检测,所以我不确定您的解释。你可能是对的,但如果没有一个可行的例子就很难说。我尝试将 static:true 属性添加到我的示例中,但仍然出现错误。感谢您花时间回答所有问题。
  • 我刚刚复制了您在此处提供的另一个示例:stackblitz.com/edit/angular-form-projection-3-4ypybx。我添加了静态 true 属性,但在控制台中没有看到任何错误。它似乎工作。您还有其他想要完成的事情吗?
  • 是的,现在看来可以了。可能是 Stackblitz 在我早些时候尝试跑步时出现了问题。我已按承诺授予积分。
猜你喜欢
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多