繁体   English   中英

为什么 ngOnInit 被调用两次?

[英]Why is ngOnInit called twice?

我试图创建新组件ResultComponent ,但是它的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){}
    ....
}

子组件 (result-comp)

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);
    }
}

运行时, console.log显示两次,第一次显示正确的数组,但第二次显示undefined 我一直无法弄清楚:为什么ResultComponent中的ngOnInit会被调用两次?

为什么它被调用两次

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

这个信息来自这个github问题


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

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

与@dylan 答案相关,请检查您的组件 html 结构及其父组件的结构。

好吧,在我的情况下,问题是我引导子组件的方式。 在我的@NgModule装饰器的元数据对象中,我在bootstap属性中传递了“子组件”和“父组件”。 在 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
    })

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

这可能是因为您将AppComponent设置为基本路由

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

注意: AppComponent是在angular默认调用的所以不需要调用

ngOnInit()仅在所有指令都实例化后挂钩一次。 如果您在ngOnInit()有订阅并且它没有取消订阅,那么如果订阅的数据发生变化,它将再次运行。

在下面的 Stackblitz 示例中,我在ngOnInit()有订阅并控制结果。 它控制台两次,因为它加载一次,数据发生变化,然后再次加载。

闪电战

把它放在这里以防有人在这里结束。 如果浏览器的默认按钮类型是“提交”,NgOnInit 也可以被调用两次,比如如果你有下面的,NextComponent 的 NgOnInit 在 Chrome 中将被调用两次:

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

要修复它,请设置类型:

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

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

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

这发生在我身上,因为我在*ngFor有未命名的<router-outlet> 它为循环中的每次迭代加载。

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

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

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

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

就我而言,这是在 Component 同时实现OnChangesOnInit 时发生的 尝试删除这些类之一。 也可以使用ngAfterViewInit方法,在视图初始化后触发,保证调用一次。

我自己也遇到了类似的问题,我正在调用一个组件,然后通过路由器插座引用同一个组件。

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

并且出口路线也指向布局。

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

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

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

<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()

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

当我在 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>

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

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

在 HTML 中:

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

在 CSS 中:

.show{ display: block; }

.hide{ display: none; }

在我的情况下,这是一个 html 错误导致 app.component.html 我有类似的东西

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

而不是

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

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

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

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

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

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

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

我把XComponent放在

引导程序:[AppComponent, XComponent ]

app.module.ts 中

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

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

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

我的解决方案是在组件中使用ngIf来防止未定义的输入,在你的情况下是:

<result-comp *ngIf="answers" [answers]="answers"></result-comp>

在我的情况下,问题是在点击退出按钮后登录页面被调用了两次。 原因是“退出”按钮上的语法错误。 导致问题的代码是:

<a routerLink="/login">
  <i class="fa fa-sign-out" (click)="logout()"></i>
</a>

从图标标签中删除点击事件并放入链接标签解决了这个问题。

<a routerLink="/login" (click)="logout()">
  <i class="fa fa-sign-out"></i>
</a>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM