Почему ngOnInit вызывается дважды?

Я пытаюсь создать новый компонент ResultComponent, но его метод ngOnInit() вызывается дважды, и я не знаю, почему это происходит. В коде ResultComponent наследует @Input от родительского компонента mcq-component.

Вот код:

Родительский компонент (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. Я не смог понять: почему ngOnInit в ResultComponent вызывается дважды?


person Shree    schedule 05.08.2016    source источник
comment
Пожалуйста, не используйте функцию вырезания, если код не работает.   -  person Günter Zöchbauer    schedule 05.08.2016
comment
Извините, я использовал функцию фрагмента, но на самом деле мне не удалось вставить код с помощью функции {}.   -  person Shree    schedule 05.08.2016
comment
Никогда не было проблем с {}. В чем была проблема?   -  person Günter Zöchbauer    schedule 05.08.2016
comment
код не удалось правильно установить в области кода. :(, это была проблема.   -  person Shree    schedule 05.08.2016


Ответы (21)


Почему это называется дважды

Прямо сейчас, если во время обнаружения изменений содержимого/представления дочерних элементов компонента возникает ошибка, ngOnInit будет вызываться дважды (видно в DynamicChangeDetector). Это может привести к последующим ошибкам, скрывающим исходную ошибку.

Эта информация взята из этой ошибки github.


Таким образом, кажется, что ваша ошибка может иметь происхождение где-то еще в вашем коде, связанном с этим компонентом.

person Dylan Meeus    schedule 05.08.2016
comment
спасибо за ваш ответ, здесь на самом деле я просто контролирую флаг isQuestionView, вот и все. - person Shree; 05.08.2016
comment
@Shree, не могли бы вы объяснить это? Я тоже сталкиваюсь с той же проблемой... - person Ravindra Thorat; 18.02.2019
comment
@RavindraThorat Привет, для этого мало вероятностей, например 1. неправильный закрывающий тег (например, ‹my-comp›‹my-comp›) 2. добавление одного и того же компонента дважды - person Shree; 18.02.2019
comment
спасибо за быстрый ответ, но в моем случае ничего не происходит. Здесь я создал поток с угловыми элементами"> stackoverflow.com/questions/54632686/. я не уверен, почему он не работает правильно - person Ravindra Thorat; 18.02.2019
comment
В моем случае у меня была тихая ошибка, связанная с плохим сопоставлением между моими объектами JSON. Исправление этой ошибки предотвратило этот двойной вызов ngOnInit(). Спасибо :) - person Louis; 07.05.2019
comment
ngOninit вызывается дважды, только когда я перезагружаю страницу. Можете ли вы помочь мне? - person rahul tiwari; 07.04.2021

Это происходило со мной из-за неисправного компонента html. Я забыл закрыть тег селектора в компоненте хоста. Так что у меня было это <search><search> вместо <search></search> - обратите внимание на синтаксическую ошибку.

Что касается ответа @dylan, проверьте html-структуру вашего компонента и его родителя.

person parsethis    schedule 15.10.2016
comment
Вы, сэр, герой. Я смотрю на это уже час. ЭТО была моя проблема. - person NewZeroRiot; 20.12.2016

если вы использовали platformBrowserDynamic().bootstrapModule(AppModule); в app.module.ts, прокомментируйте это и попробуйте. У меня такая же проблема. я думаю это поможет

person Scott Machlovski    schedule 03.01.2018

Что ж, проблема в моем случае заключалась в том, как я загружал дочерние компоненты. В моем объекте метаданных декоратора @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
    })
person Amardeep Kohli    schedule 15.12.2016

Это может произойти из-за того, что вы установили AppComponent в качестве маршрута по умолчанию.

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

Примечание. AppComponent по умолчанию вызывается в angular, поэтому его не нужно вызывать.

person WasiF    schedule 25.04.2018
comment
Так было со мной. - person DongBin Kim; 06.03.2019

Поместите это сюда на случай, если кто-то окажется здесь. NgOnInit также можно вызвать дважды, если тип кнопки вашего браузера по умолчанию — «отправить», скажем, если у вас есть следующее, NgOnInit из NextComponent будет вызываться дважды в Chrome:

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

Чтобы исправить это, установите тип:

<button class="btn btn-primary" type="button" (click)="navigateToNextComponent()">
person user3689408    schedule 27.01.2017
comment
спасибо, это была именно проблема в моем случае. Я думаю, это актуально только в том случае, если ваши кнопки являются частью <form>. - person omoser; 15.02.2017

ngOnInit() перехватывает только один раз после создания экземпляров всех директив. Если у вас есть подписка внутриngOnInit() и она не отписана, то она будет запущена снова, если данные подписки изменятся.

В следующем примере Stackblitz у меня есть подписка внутри ngOnInit(), и я получаю результат консоли. Он консоль дважды, потому что он загружается один раз и данные меняются, а затем загружается снова.

Stackblitz

person Maihan Nijat    schedule 20.02.2019
comment
Нет, он зарегистрирован только один раз. Выход был Angular, Angular8, затем p. p зарегистрировался только один раз. - person codenamezero; 23.06.2020

Это происходит всякий раз, когда есть какие-либо ошибки шаблона.

В моем случае я использовал неправильную ссылку на шаблон и исправил это, исправив мою проблему.

person GingerBeer    schedule 08.03.2017
comment
Можно подробнее об ошибках шаблона? Пожалуй, приведите пример. - person Jacques; 01.04.2019

Это случилось со мной, потому что у меня были безымянные <router-outlet> внутри *ngFor. Он загружается для каждой итерации в цикле.

Решением в этом случае было бы иметь уникальное имя для каждого выхода или убедиться, что в DOM одновременно присутствует только один (возможно, с *ngIf).

person Kevin Beal    schedule 28.08.2018
comment
хорошо, Кирилл :) У меня есть несколько ‹router-outlet›‹/router-outlet› внутри *ngIf - комментирование одного решило проблему - теперь мне просто нужно немного изменить код, чтобы исправить это: D ты мой герой для день - person danday74; 30.04.2021

В моем случае сборка сгенерировала дубликаты каждого файла main-es5.js и main-es2015.js, удалив все дубликаты *-es5.js, и все заработало.

Если это и ваш случай, не удаляйте дубликаты, просто добавьте эти атрибуты.

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

В моем случае это происходит, когда компонент реализует как OnChanges, так и OnInit. Попробуйте удалить один из этих классов. Вы также можете использовать метод ngAfterViewInit, он запускается после инициализации представления, поэтому он гарантированно вызывается один раз.

person Cem Mutlu    schedule 23.01.2018

У меня была похожая проблема, я вызывал компонент, а затем ссылался на тот же компонент через маршрутизатор-выход.

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

Выходной маршрут также указывал на Макет.

person CodeMylife    schedule 05.11.2018

Для меня это произошло потому, что я дважды зарегистрировал один и тот же компонент в Route:

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

Когда я наткнулся на эту проблему в 2021 году, я совсем недавно перешел с древнего ng4 на более новый ng10. Однако приложение было интегрировано в монолитную серверную часть Java через index.jsp, который я не перенес должным образом.
Если вы находитесь в аналогичном положении, проверьте свои теги <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>
person hvsp    schedule 14.06.2021

Это может произойти, если у вас есть несколько розеток маршрутизатора, например:

<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. Если значение изменяется, то при втором вызове вы знаете, что это новый экземпляр компонента, а не один и тот же экземпляр, вызываемый дважды. Хотя это не решит вашу проблему, это может указывать на проблему.

person danday74    schedule 30.04.2021

В моем случае я не отписался от всех подписок, которые у меня были внутри ngOnInit. Я просто отписался от всех подписок в ngOnDestroy, и это решило мою проблему.

person Sudharsan Prabu    schedule 24.06.2019

Это случилось со мной в дочернем компоненте. Дочерний компонент должен показывать, когда выполняется определенное условие, используя *ngIf. Мое решение состоит в том, чтобы заменить ngIf на ngClass.

в HTML:

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

в CSS:

.show{ display: block; }

.hide{ display: none; }
person PocoM    schedule 19.05.2020

В моем случае это была ошибка html, которая вызвала это в app.component.html, у меня было что-то вроде

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

вместо

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

вместо закрывающего тега было два открывающих тега. и у меня не было ошибок на консоли, и функциональность работала нормально, за исключением того, что у меня было несколько вызовов API с подписками из-за двойного вызова ngOnInit.

person Lakshmi    schedule 05.06.2020

Это может означать, что вы запускаете компонент дважды по ошибке. Это может быть по нескольким причинам:

  1. Вы импортировали AppRoutingModule более чем в 1 модуль. Найдите его в своем приложении и убедитесь, что он включен только в файл AppModule.

  2. Вы импортировали AppModule в другой модуль. Это также дублирует ваш AppRoutingModule согласно пункту 1.

  3. У вас есть более одного <router-outlet> в вашей разметке. Это может сбить с толку, потому что если у вас есть несколько вложенных маршрутов, вам на самом деле понадобится более одного <router-outlet>. Однако вам нужно будет убедиться, что каждый маршрут, имеющий дочерние маршруты, имеет только 1 <router-outlet>. Иногда проще скопировать каждый вложенный компонент внутрь родителя, чтобы увидеть, каким будет фактический результат. Затем вы можете увидеть, не оказалось ли по ошибке лишнего <router-outlet>.

  4. Ваш компонент-оболочка указан в AppRoutingModule и внутри дочернего компонента. Часто у меня будет компонент-оболочка, который обрабатывает стандартные элементы макета, такие как верхний и нижний колонтитулы, меню и т. Д. Все мои дочерние маршруты затем будут находиться внутри этого, когда они указаны в моем файле AppRoutingModule. Иногда можно использовать этот компонент-оболочку и внутри другого компонента, тем самым продублировав его, поскольку он уже указан в файле AppRoutingModule. Это приведет к дополнительному <router-outlet>.

person DoubleA    schedule 16.02.2021

Я поместил XComponent в

начальная загрузка: [AppComponent, XComponent]

в app.module.ts.

После удаления XComponent ngOnInit срабатывал только один раз.

person cdevries    schedule 15.04.2021

Это случилось со мной, потому что у меня был атрибут href=# в теге привязки. не используйте в теге привязки. Это будет исправлено, если вы используете routerLink в соответствии с угловым кодом, как показано ниже, и пытаетесь избежать использования метода ngInit (0 в appComponent.ts

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