使用 Angular 元素调用组件构造函数两次

Posted

技术标签:

【中文标题】使用 Angular 元素调用组件构造函数两次【英文标题】:Component constructor is getting called twice with Angular elements 【发布时间】:2019-07-05 01:45:06 【问题描述】:

我正在开发一个递归查询构建器表单,它类似于 this,在 angular 7 中,具有反应式表单。也就是说,用户可以通过点击Add rule继续添加并行规则,并且可以通过点击Add group添加组。 我创建了两个组件,QueryDesignerComponentQueryComponentQueryDesignerComponent 保存外部容器,ANDOR 条件和 QueryComponent 保存行输入,即 LHSoperatorRHS

    当用户单击Add rule 时,我只是通过在QueryDesignerComponent 内再推一个带有QueryComponent 的条目来扩展规则。我用*ngFor 重复这个。 当用户单击Add group 时,我在QueryComponent 中调用QueryDesignerComponent,这使其递归。我用*ngFor 重复这个。

我已经完成了实现及其在 Angular 应用程序中的正常工作,该应用程序具有 Angular 环境。

现在,我正在尝试将此流程移植到angular elements,以使其可重复使用,而不管环境如何。

这就是我将第一行 [QueryComponent] 放在 QueryDesignerComponent 中的方式,

<div class="qd--criteria">
    <div class="row qd--body qd--clear-margin-lr">
        <div class="col-md-12 qd--condition-container">
            <query [data]="data" [operators]="operators" [(queryForm)]="queryForm"></query>
        </div>
    </div>
</div>

这样我就可以管理QueryComponent 中的并行查询和组,

<!--Top level container for query view | everything related should go here: start-->
<div class="qd--query-container" [formGroup]="queryForm" *ngIf="queryForm">
    <div class="row" formArrayName="queries">
        <!--Repeat the dynamically added/removed queries: start-->
        <div class="col-md-12 qd--query-inputs-container" *ngFor="let query of currentQueries.controls; let queryIndex = index">
            <div class="row qd--query-inputs" [formGroupName]="queryIndex">
                <!--Actual query inputs: start-->
                <div class="col-md-10 qd--condition-holder">
                    <div class="row no-gutter">
                        <!--Left hand side input: start-->

                        <!--Left hand side input: end-->

                        <!--Operator: start-->

                        <!--Operator: end-->

                        <!--Right hand side input: start-->

                        <!--Right hand side input: end-->
                    </div>
                </div>
                <!--Actual query inputs: start-->

                <!--Group options: start-->

                <!--Group options: end-->

                <!--Group query: start-->
                <div *ngIf="query !== undefined" class="ai--query-groups">
                    <div *ngFor="let group of getGroups(query).controls; let groupIndex=index" class="ai--query-group">
                        <query-designer
                                [data]="data"
                                [operators]="operators"
                                [queryForm]="group"
                                (removeQueryGroup)="removeQueryGroupHandler($event)"
                                [queryIndex]="queryIndex"
                                [groupIndex]="groupIndex"></query-designer>
                    </div>
                </div>
                <!--Group query: end-->

            </div>
        </div>
        <!--Repeat the dynamically added/removed queries: end-->
    </div>
</div>
<!--Top level container for query view: start-->


<!--Repeat the dynamically added/removed queries: start-->
<div class="col-md-12 qd--query-inputs-container" *ngFor="let query of currentQueries.controls; let queryIndex = index">
    <div class="row qd--query-inputs" [formGroupName]="queryIndex">
        <!--Actual query inputs: start-->
        <div class="col-md-10 qd--condition-holder">
            <div class="row no-gutter">
                <!--Left hand side input: start-->

                <!--Left hand side input: end-->

                <!--Operator: start-->

                <!--Operator: end-->

                <!--Right hand side input: start-->

                <!--Right hand side input: end-->
            </div>
        </div>
        <!--Actual query inputs: start-->

        <!--Group options: start-->

        <!--Group options: end-->

        <!--Group query: start-->
        <div *ngIf="query !== undefined" class="ai--query-groups">
            <div *ngFor="let group of getGroups(query).controls; let groupIndex=index" class="ai--query-group">
                <query-designer
                        [data]="data"
                        [operators]="operators"
                        [queryForm]="group"
                        (removeQueryGroup)="removeQueryGroupHandler($event)"
                        [queryIndex]="queryIndex"
                        [groupIndex]="groupIndex"></query-designer>
            </div>
        </div>
        <!--Group query: end-->

    </div>
</div>
<!--Repeat the dynamically added/removed queries: end-->

这就是我创建自定义角度元素的方式,

@NgModule(
  imports: [
    CommonModule,
    BrowserModule,
    NgSelectModule,
    FormsModule,
    ReactiveFormsModule,
    CoreModule
  ],
  declarations: [
    AppComponent,
    QueryComponent,
    QueryDesignerComponent
  ],
  entryComponents: [QueryDesignerComponent],
  providers: []
)
export class AppModule 
  constructor(private injector: Injector) 
    const strategyFactory = new ElementZoneStrategyFactory(QueryDesignerComponent, injector);
    const customElement = createCustomElement(QueryDesignerComponent,  injector, strategyFactory );
    customElements.define('query-designer', customElement);
  

  ngDoBootstrap()  

在第一次渲染中,它工作正常,我可以添加n 并行行数。但是,当我单击Add group 时,QueryDesignerComponent 的构造函数被调用了两次!这使得QueryDesignerComponent 的第一个实例接收undefined,第二个实例接收正确的值。

我关注了why ngOnInit called twice?,相关的github issue 和ngOnInit and Constructor are called twice 但是,我没有找到运气!!

有谁知道我怎样才能摆脱这个问题?任何帮助/指导将不胜感激!

【问题讨论】:

请提供更多代码以使此问题可重现。 您是否尝试使用延迟加载来加载此模块? angular.io/guide/lazy-loading-ngmodulesgithub.com/angular/angular/issues/12869 @web.dev,添加了更多代码。请看一下 @JoelGarciaNuño,让我检查一下。在跳转实际链接之前,这是一个非常快速的问题,但它对角度元素有帮助吗? 这有助于加载一次组件,可能不会再次调用构造函数。使用延迟加载,在你的路由器路径上使用#angular.io/guide/lazy-loading-ngmodules#routes-at-the-app-level,但是休息一下,可能是一个简单的问题,当你按下按钮超过两次时,调用了多少次构造函数? 【参考方案1】:

我也遇到了同样的问题,Angular Elements 也是。

问题是我在 Angular7 组件中调用了 Angular7 组件,并且在 app.module.ts 上我引导了与 Angular7 组件名称相同名称的子组件。

当父级调用 my-child-component 时,它同时调用了 Angular7 和自定义元素。

因此,解决方法是在这种情况下使用不同的名称。

【讨论】:

【参考方案2】:

另一种可能性可能是 app.module.ts 中的以下代码

platformBrowserDynamic().bootstrapModule(AppModule).catch(err =>  console.error(err) );

【讨论】:

您能否进一步解释这是如何导致问题的。 这实际上对我有用!第一次加载页面时,我看到我的组件被加载了两次。删除该行解决了问题,但我仍然不确定为什么。【参考方案3】:

我试过这个并为 Angular 元素工作。 从 @NgModule 中移除 Bootstrap 并在 ngDoBootstrap 中添加自定义元素定义而不是构造函数。

请参考以下代码。

 @NgModule(
  declarations: [
    MyComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  schemas: [
    CUSTOM_ELEMENTS_SCHEMA
  ],
  providers: [MyService]
)
export class AppModule implements DoBootstrap 
  constructor(private injector: Injector) 
  
  ngDoBootstrap(appRef: ApplicationRef) 
    const el = createCustomElement(MyComponent,  injector: this.injector );
    customElements.define('my-widget', el);
  

【讨论】:

以上是关于使用 Angular 元素调用组件构造函数两次的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 2 中调用其构造函数之前将数据发送或绑定到子组件?

Angular 2 - 构造函数调用后的生命周期事件

Angular 6订阅构造函数上的事件多次调用

为啥不应该在Angular中组件的构造函数中进行数据初始化?

从 Angular2 路由中的子组件调用 router.parent.navigate 方法时未触发组件构造函数和路由器生命周期挂钩

构造函数和渲染方法在反应组件中运行两次