簡體   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