【问题标题】:Why is ngOnInit called twice?为什么 ngOnInit 被调用两次?
【发布时间】:2021-11-25 08:23:39
【问题描述】:

我试图创建新组件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 会被调用两次?

【问题讨论】:

  • 代码不可运行时请不要使用截图功能。
  • 对不起,我使用了 sn-p 功能,但实际上我无法使用 {} 功能插入代码。
  • {} 从来没有任何问题。出了什么问题?
  • 代码无法在代码区域内正确设置。 :(,这就是问题所在。

标签: angular ngoninit


【解决方案1】:

为什么会被调用两次

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

此信息来自此github issue


所以看来你的错误可能源于你的代码的其他地方,与这个组件有关。

【讨论】:

  • 感谢您的回复,这里其实我只是控制器 isQuestionView 标志,就是这样。
  • @Shree,你能解释一下吗?我也面临同样的问题...
  • @RavindraThorat 嗨,这种可能性很小,例如 1. 错误的结束标签(如 ) 2. 两次添加相同的组件
  • 感谢您的快速响应,但在我的情况下没有发生任何事情。我在这里创建了一个线程stackoverflow.com/questions/54632686/…。不知道为什么它不能正常工作
  • 在我的情况下,我遇到了一个与我的 JSON 对象之间的错误映射有关的静默错误。修复这个错误阻止了对 ngOnInit() 的双重调用。谢谢:)
【解决方案2】:

由于组件 html 有问题,这发生在我身上。我忘记关闭主机组件中的选择器标签。所以我有这个&lt;search&gt;&lt;search&gt;,而不是&lt;search&gt;&lt;/search&gt;——注意语法错误。

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

【讨论】:

  • 你先生是个英雄。我已经盯着这个看了一个小时了。这是我的问题。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    我的问题是我引导子组件的方式。 在我的 @NgModule 装饰器的元数据对象中,我在 bootstap 属性中传递了“子组件”以及“父组件强>'。 在引导属性中传递子组件是重置我的子组件properties并使 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
        })
    

    【讨论】:

      【解决方案5】:

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

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

      注意:AppComponentangular中默认调用,所以不需要调用它

      【讨论】:

      • 我就是这种情况。
      【解决方案6】:

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

      在下面的 Stackblitz 示例中,我在 ngOnInit() 内订阅并控制结果。它控制台两次,因为它加载一次并且数据更改并再次加载。

      Stackblitz

      【讨论】:

      • 不,不是,它只记录了一次。输出是AngularAngular8,然后是pp 只被登录一次。
      【解决方案7】:

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

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

      要修复它,请设置类型:

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

      【讨论】:

      • 谢谢,这正是我的问题。不过,我想这仅在您的按钮是 &lt;form&gt; 的一部分时才有意义。
      【解决方案8】:

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

      在我的情况下,我使用了错误的模板引用并更正了我的问题..

      【讨论】:

      • 您能详细说明“模板错误”吗?举个例子吧。
      【解决方案9】:

      这发生在我身上,因为我在 *ngFor 中未命名 &lt;router-outlet&gt;s。它为循环中的每次迭代加载。

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

      【讨论】:

      • 不错的一个 Cyril :) 我在 *ngIf 中有多个 - 注释掉一个解决了问题 - 现在我只需要稍微调整代码即可修复这 :D 你是我今天的英雄
      【解决方案10】:

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

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

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

      【讨论】:

        【解决方案11】:

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

        <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 中。如果值发生变化,那么您在第二次调用时就知道它是组件的一个新实例,并且它不是同一个实例被调用两次。虽然这不能解决您的问题,但它可能表明存在问题。

        【讨论】:

          【解决方案12】:

          在我的例子中,当 Component 同时实现 OnChangesOnInit 时,就会发生这种情况。尝试删除这些类之一。也可以使用ngAfterViewInit方法,在视图初始化后触发,保证调用一次。

          【讨论】:

            【解决方案13】:

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

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

            而且出口路线也指向了Layout。

            【讨论】:

              【解决方案14】:

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

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

              【讨论】:

                【解决方案15】:

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

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

                【讨论】:

                  【解决方案16】:

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

                  【讨论】:

                    【解决方案17】:

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

                    在 HTML 中:

                    &lt;component [ngClass]="isLoading?'hide':'show'&gt;"

                    在 CSS 中:

                    .show{ display: block; }
                    
                    .hide{ display: none; }
                    

                    【讨论】:

                      【解决方案18】:

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

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

                      而不是

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

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

                      【讨论】:

                        【解决方案19】:

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

                        1. 您已将AppRoutingModule 导入多个模块。在您的应用中搜索它,并确保您仅将其包含在 AppModule 中。

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

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

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

                        【讨论】:

                          【解决方案20】:

                          我把 XComponent 放在了

                          引导程序:[AppComponent, XComponent]

                          app.module.ts 中。

                          在移除 XComponent 之后,ngOnInit 只被触发了一次。

                          【讨论】:

                            【解决方案21】:

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

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

                            【讨论】:

                            • 你能详细说明一下吗? > 尽量避免在 appComponent.ts 中有 ngInit(0 方法
                            【解决方案22】:

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

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

                            【讨论】:

                              猜你喜欢
                              • 2019-04-04
                              • 2023-04-10
                              • 2021-07-13
                              • 2015-12-24
                              • 2012-12-10
                              • 2014-01-11
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多