为什么ngOnInit打了两次电话?

时间:2016-08-05 11:10:18

标签: angular ngoninit

我尝试创建新组件,但其 ngOnInit()方法被调用两次,我不知道为什么会这样做?这里我创建了名为 ResultComponent 的组件,它从名为 mcq-component 的父组件中获取 @Input 这是代码:

父组件(MCQComponent)

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'mcq-component',
    template: `
        <div *ngIf = 'isQuestionView'>
            .....
        </div>
        <result-comp *ngIf = '!isQuestionView' [answers] = 'ansArray'><result-comp>
    `,
    styles: [
        `
            ....
        `
    ],
    providers: [AppService],
    directives: [SelectableDirective, ResultComponent]
})
export class MCQComponent implements OnInit{
      private ansArray:Array<any> = [];
    ....
    constructor(private appService: AppService){}
    ....
}

子组件(结果组合)

import { Component, OnInit, Input } from '@angular/core';

@Component({
    selector:'result-comp',
    template: `
        <h2>Result page:</h2>

    `
})
export class ResultComponent implements OnInit{
    @Input('answers') ans:Array<any>;

    ngOnInit(){
        console.log('Ans array: '+this.ans);
    }
}

此处控制台日志显示2次:第一次显示正确的数组,但第二次显示未定义,但我无法弄明白,为什么来自 ResultComponent ngOnInit 被调用两次?

21 个答案:

答案 0 :(得分:36)

为什么要两次调用

  

现在,如果在检测到组件的内容/视图子项的更改期间发生错误,则将两次调用ngOnInit(在DynamicChangeDetector中看到)。   这可能会导致隐藏原始错误的后续错误。

此信息来自此github issue

因此,您的错误似乎可能源于您的代码中的其他地方,与此组件相关。

答案 1 :(得分:16)

由于组件html错误,这发生在我身上。我忘了关闭主机组件中的选择器标签。所以我有<search><search>而不是<search></search> - 请注意语法错误。

与@dylan回答相关,检查你的组件html结构及其父结构。

答案 2 :(得分:14)

我的案例中的问题就是我引导子组件的方式。 在我的 @NgModule 装饰器的元数据对象中,我在 bootstap 属性中传递了'子组件'以及'父组件 STRONG>”。 在bootstap属性中传递子组件是重置我的子组件属性并使 OnInit()触发两次。

@NgModule({
imports: [ BrowserModule,FormsModule ], // to use two-way data binding ‘FormsModule’
declarations: [ parentComponent,Child1,Child2], //all components
//bootstrap: [parentComponent,Child1,Child2] // will lead to errors in binding Inputs in Child components
bootstrap: [parentComponent] //use parent components only
})

答案 3 :(得分:12)

如果您在app.module.ts中使用platformBrowserDynamic().bootstrapModule(AppModule);对其进行评论并尝试。我有同样的问题。我认为这有帮助

答案 4 :(得分:9)

将此放在这里以防万一有人在这里结束。如果您的浏览器的默认按钮类型是&#34;提交&#34;,如果您有以下内容,NgOnInit也可以被调用两次,在Chrome中将调用两次NgOnInit of NextComponent:

<button class="btn btn-primary" (click)="navigateToNextComponent()">

要修复它,请设置类型:

<button class="btn btn-primary" type="button" (click)="navigateToNextComponent()">

答案 5 :(得分:6)

这可能是因为您将AppComponent设置为默认路线

RouterModule.forRoot([
    { path: '', component: AppComponent }  // remove it
])

注意:AppComponent默认调用angular,因此无需调用它

答案 6 :(得分:3)

只要有模板错误,就会发生这种情况。

在我的情况下,我使用了错误的模板参考并纠正了修复我的问题..

答案 7 :(得分:3)

就我而言,构建生成的每个文件 main-es5.js 和 main-es2015.js 的重复项,删除所有 *-es5.js 重复项并且它起作用了。

如果您也是这种情况,请不要删除重复项,只需添加这些属性

<script src="elements-es5.js" nomodule defer>
<script src="elements-es2015.js" type="module">

答案 8 :(得分:1)

如果更改视图,也会发生这种情况。当我订阅数据并更改数据时,这种情况会发生。

答案 9 :(得分:1)

当我在 2021 年偶然发现这个问题时,我刚刚从一个古老的 ng4 迁移到了一个更新的 ng10。 然而,该应用程序通过 index.jsp 集成到了一个单一的 Java 后端中,我没有正确地迁移它。
如果您处于类似位置,请检查您的 <script> 标签。 *-es2015.js 源应该有一个 type="module" 而 *-es5.js 应该有 nomodule defer 集,即:

<script src=".../main-es5.js" nomodule defer></script>
<script src=".../main-es2015.js" type="module"></script>

答案 10 :(得分:1)

如果您有多个这样的路由器插座,就会发生这种情况:

<ng-container *ngIf="condition">
  <div class="something">
    <router-outlet></router-outlet>
  </div>
</ng-container>
<ng-container *ngIf="!condition">
  <div class="something-else">
    <router-outlet></router-outlet>
  </div>
</ng-container>

请注意,如果您这样做,它也可能最终调用构造函数两次。构造函数被调用两次的事实证明组件被创建了两次,这正是当组件位于 *ngIf 中,其条件从 true 变为 false 到 true 时发生的情况

一种有用的调试方法是创建一个名为 rnd 的类变量,如下所示:

rnd = Math.random()

和 console.log 在构造函数和 ngOnInit 中。如果值发生变化,那么您在第二次调用时就知道它是组件的一个新实例,并且它不是被调用两次的同一个实例。虽然这不能解决您的问题,但它可能表明存在问题。

答案 11 :(得分:1)

发生这种情况是因为我在<router-outlet>中未命名*ngFor。它为循环中的每次迭代加载。

在这种情况下,解决方案将是为每个出口使用唯一的名称,或者确保一次在DOM中只有一个(也许带有*ngIf)。

答案 12 :(得分:1)

就我而言,当Component实现 OnChanges OnInit 时,就会发生这种情况。尝试删除其中一个类。您也可以使用 ngAfterViewInit 方法,在视图初始化后触发它,以便保证调用一次。

答案 13 :(得分:0)

对我来说,发生这种情况是因为我在Route中两次注册了相同的组件:

{
        path: 'meal-services',
        children: [
          {
            path: '',
            component: InitTwiceComponent,
            canActivate: [AppVendorOpsGuard],
            data: { roleWhitelist: [UserRoles.Manage] }
          },
          {
            path: ':itemID',
            component: InitTwiceComponent,
            canActivate: [AppVendorOpsGuard],
            data: { roleWhitelist: [UserRoles.Manage] }
          }]
      },
}

答案 14 :(得分:0)

就我而言,我没有取消订阅ngOnInit内的所有订阅。我刚从 ngOnDestroy 内的所有订阅中取消订阅,就解决了我的问题。

答案 15 :(得分:0)

这在我的子组件中发生了。子组件将使用* ngIf显示何时满足特定条件。我的解决方案是用ngClass替换ngIf

在HTML中:

<component [ngClass]="isLoading?'hide':'show'>"

在CSS中:

.show{ display: block; }

.hide{ display: none; }

答案 16 :(得分:0)

就我而言,这是导致此错误的html错误 在app.component.html中,我有类似的东西

<icpm-la-test><icpm-la-test>

代替

<icpm-la-test></icpm-la-test>

有两个开始标签而不是结束标签。而且我在控制台上没有任何错误,并且功能正常,但由于ngOnInit被两次​​调用,订阅发生了多个api调用。

答案 17 :(得分:0)

我自己也遇到类似的问题,我在呼叫一个组件,然后通过路由器出口引用相同的组件。

<layout>
  <router-outlet></router-outlet>
</layout>

出口路线也指向布局。

答案 18 :(得分:0)

这可能表明您错误地启动了组件两次。这可能有以下几个原因:

  1. 您已将 AppRoutingModule 导入 1 个以上的模块。在您的应用中搜索它并确保您只将它包含在 AppModule 中。

  2. 您已将 AppModule 导入另一个模块。这也会根据第 1 点复制您的 AppRoutingModule

  3. 您的标记中有多个 <router-outlet>。这可能会令人困惑,因为如果您有多个嵌套路由,您实际上需要多个 <router-outlet>。但是,您需要检查每个具有子路由的路由是否只有 1 个 <router-outlet>。有时,为了查看实际输出将是什么,复制父组件中的每个嵌套组件会更容易。然后您可以查看是否错误地得到了额外的 <router-outlet>

  4. 您的包装器组件在 AppRoutingModule 和子组件内指定。通常我会有一个包装器组件来处理标准布局,比如页眉、页脚、菜单等。当我在 AppRoutingModule 中设置时,我的所有子路由都将位于其中。有时也可以在另一个组件中使用这个包装器组件,从而复制它,因为它已经在 AppRoutingModule 中指定。这会导致额外的 <router-outlet>

答案 19 :(得分:0)

我将 XComponent 放入

引导程序:[AppComponent, XComponent]

app.module.ts中。

移除 XComponent 后,ngOnInit 仅被触发一次。

答案 20 :(得分:0)

这发生在我身上,因为我在锚标记中有 arribute href="#" 。不要在锚标签中使用。如果您按照如下所示的角度代码使用 routerLink 并尽量避免在 appComponent.ts 中使用 ngInit(0 方法

<a  class="nav-link" [routerLink]="['/login']"> Sign In. </a>