Why is ngOnInit called twice?
Asked Answered
S

25

86

I trying to create new component, ResultComponent, but its ngOnInit() method is getting called twice and I don't know why this is happening. In the code, ResultComponent inherits @Input from the parent component mcq-component.

Here is the code:

Parent 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){}
    ....
}

Child Component (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);
    }
}

When run, console.log is showing up two times, the first time it shows the correct array but the second time it gives undefined. I've not been able to figure it out: why is ngOnInit in ResultComponent getting called twice?

Strategy answered 5/8, 2016 at 11:10 Comment(4)
Please don't use the snipped feature when the code isn't runnable.Cacomistle
Sorry I used snippet feature, but actually I was unable to insert code using {} feature.Strategy
Never had any issue with {}. What was the problem?Cacomistle
the code was not able to set properly within the code area. :(, that was the problem.Strategy
T
58

Why it is called twice

Right now, if an error happens during detecting changes of content/view children of a component, ngOnInit will be called twice (seen in DynamicChangeDetector). This can lead to follow up errors that hide the original error.

This information comes from this github issue


So it seems that your mistake might have an origin elsewhere in your code, related to this component.

Teratogenic answered 5/8, 2016 at 11:13 Comment(6)
thanks for your response, here actually I just controller isQuestionView flag, that's it.Strategy
@Shree, can you please explain this? I am also facing the same issue...Salespeople
@RavindraThorat Hi there are few probabilities of this, like 1. wrong closing tag (like <my-comp><my-comp>) 2. adding same component twiceStrategy
thanks for the quick response, but nothing is happening in my case. I have created a thread here #54633186. am not sure why its not working correctlySalespeople
In my case I had a silent error related to a bad mapping between JSON objects of mine. Fixing this error prevented this double call to ngOnInit(). Thanks :)Agnesagnese
ngOninit called twice only when i reload a page. can you help me out?Farewell
H
27

This was happening to me because of a faulty component html. I had forget to close the selector tag in the host component. So I had this <search><search>, instead of <search></search> - take note of the syntax error.

So related to @dylan answer, check your component html structure and that of its parent.

Hortatory answered 15/10, 2016 at 5:38 Comment(1)
You sir are a hero. I've been staring at this for an hour now. THIS was my problem.Tatiana
M
23

Well the Problem in my case was the way I was bootstrapping the Child Components. In my @NgModule decorator’s metadata object ,I was passing ‘child component’ in the bootstap property along with ‘parent component’. Passing the child components in bootstap property was resetting my child components properties and making OnInit() fired twice.

    @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
    })
Manipulator answered 15/12, 2016 at 16:18 Comment(0)
N
21

if you used platformBrowserDynamic().bootstrapModule(AppModule); in app.module.ts comment it and try. I had the same problem. I think this helps

Neona answered 3/1, 2018 at 6:0 Comment(0)
B
20

This may happen because you set the AppComponent as your base route

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

Note: AppComponent is called by default in angular so need not to call it again as you can see below:

@NgModule({
  declarations: [
    AppComponent,
  ],
  bootstrap: [AppComponent] // bootstraping here by default
})
export class AppModule { }
Blowzy answered 25/4, 2018 at 9:14 Comment(1)
This was the case for me.Nonsuch
F
15

The ngOnInit() hooks only once after all directives are instantiated. If you have subscription insidengOnInit() and it's not unsubscribed then it will run again if the subscribed data changes.

In the following Stackblitz example I have subscription inside ngOnInit() and console the result. It console twice because it loads once and data changes and it loads again.

Stackblitz

Forecastle answered 20/2, 2019 at 16:24 Comment(2)
No is not, it only logged once. The output was Angular, Angular8 then p. p only got logged once.Syndesmosis
I had a similar issue regarding a MatDialog that needed closing twice sometimes. I finally found that this was a side effect caused by a subscription that wasn't unsubscribed, making it so I went into ngOnInit() twice. Managing my subscription fixed it for my case.Thorough
Q
14

Putting this here in case someone wind up here. NgOnInit can also be called twice if your browser's default button type is "submit", say if you have the below, NgOnInit of NextComponent will be called twice in Chrome:

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

To fix it, set type:

<button class="btn btn-primary" type="button" (click)="navigateToNextComponent()">
Quern answered 27/1, 2017 at 3:1 Comment(1)
thanks, that was exactly the issue in my case. I guess this is only relevant if your buttons are part of a <form>, though.Reimers
S
5

This happens whenever there are any template errors.

In my case I was using a wrong template reference and correcting that fixed my issue..

Suckling answered 8/3, 2017 at 5:25 Comment(1)
Can you elaborate on "template errors"? Perhaps give an example.Hygroscopic
K
5

This can happen if you have multiple router outlets like this:

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

Note that if you do this it can also end up calling the constructor twice. The fact that the constructor is called twice is evidence that the component is being created twice, which is exactly what happens when a component is inside an *ngIf whose condition switches from true to false to true

A useful approach to debugging this is to create a class variable called rnd like this:

rnd = Math.random()

and console.log it inside the constructor and ngOnInit. If the value changes then you know on the second call that there it is a new instance of the component and its not the same instance being called twice. Whilst this won't fix your issue, it may be indicative of the problem.

Kaftan answered 30/4, 2021 at 8:53 Comment(1)
Conditional <router-outlet *ngIf="<condition>"></router-outlet> was my issue, thanks.Dysphoria
R
4

In my case, build generated duplicates of each file main-es5.js and main-es2015.js, deleting all *-es5.js duplicates and it worked.

If that is your case too, don't delete the duplicates just add those attributes

<script src="elements-es5.js" nomodule defer>
<script src="elements-es2015.js" type="module">
Remunerative answered 25/12, 2020 at 8:13 Comment(0)
G
3

This happened to me because I had unnamed <router-outlet>s inside of an *ngFor. It loaded for each iteration in the loop.

The solution, in that case, would be to have a unique name for each outlet or make sure that there is only one in the DOM at a time (perhaps w/ an *ngIf).

Giffer answered 28/8, 2018 at 17:24 Comment(1)
nice one Cyril :) I have multiple <router-outlet></router-outlet> inside *ngIf - commenting one out fixed the problem - now I just need to tweak the code a bit to fix this :D you r my hero for the dayKaftan
M
3

I had a similar issue myself, I was calling for a component and then referencing the same component via the router-outlet.

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

And the outlet route was also pointing to Layout.

Magog answered 5/11, 2018 at 18:25 Comment(2)
Thank you very much for your comment, since I used this method in the app.component.ts file, the function was repeated twice. I solved the problem. The comment from 6 years ago works in 2024. amazing, thank you very much, good luckRaiment
Glad it helped @UmutCanArda. Best of luck with your angular projects!Magog
W
3

For me, this happened because I registered same component twice in Route:

{
        path: 'meal-services',
        children: [
          {
            path: '',
            component: InitTwiceComponent,
            canActivate: [AppVendorOpsGuard],
            data: { roleWhitelist: [UserRoles.Manage] }
          },
          {
            path: ':itemID',
            component: InitTwiceComponent,
            canActivate: [AppVendorOpsGuard],
            data: { roleWhitelist: [UserRoles.Manage] }
          }]
      },
}
Windburn answered 5/3, 2019 at 19:47 Comment(0)
C
2

In my case, this is happened when Component implements both OnChanges and OnInit. Try to remove one of these classes. You can also use ngAfterViewInit method, it is triggered after the view initialized, so that it is guaranteed to called once.

Carlina answered 23/1, 2018 at 13:9 Comment(0)
C
2

This happened to me in a child component. The child component is to show when a certain condition is met using *ngIf. My solution is to replace ngIf with ngClass.

in HTML:

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

in CSS:

.show{ display: block; }

.hide{ display: none; }
Credendum answered 19/5, 2020 at 7:51 Comment(0)
D
2

When I stumbled over this issue in 2021, I had just recently migrated from an ancient ng4 to a more recent ng10. The app however was integrated in a monolithic Java backend via an index.jsp, which I did not properly migrate.
If you are in a similar position check your <script> tags. The *-es2015.js sources should have a type="module" while *-es5.js should have nomodule defer set, i.e.:

<script src=".../main-es5.js" nomodule defer></script>
<script src=".../main-es2015.js" type="module"></script>
Debtor answered 14/6, 2021 at 11:17 Comment(0)
C
1

In my case I did not unsubscribe to all the subscriptions that I had inside ngOnInit. I just unsubscribed from all the subscriptions within ngOnDestroy and that resolved my issue.

Cyclone answered 24/6, 2019 at 1:59 Comment(0)
M
1

In my case it was a html error that caused this in app.component.html i had something like

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

instead of

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

there was two opening tags instead of closing tag. and I had no errors on console and functionality was working fine except that i had multiple api calls happening with subscriptions due to ngOnInit getting called twice.

Malissa answered 5/6, 2020 at 9:37 Comment(0)
G
1

This can indicate that you are initiating the component twice by mistake. This can be for a few reasons:

  1. You have imported AppRoutingModule into more than 1 module. Search for it across your app and make sure you only have it included in the AppModule.

  2. You have imported AppModule into another module. This would also duplicate your AppRoutingModule as per point 1.

  3. You have more than one <router-outlet> in your markup. This one can get confusing, because if you have multiple nested routes, you will actually need more than one <router-outlet>. However, you will need to check that each route that has child-routes has only 1 <router-outlet>. Sometimes it is easier to copy each nested component inside the parent just to see what the actual output is going to be. Then you can see if you ended up with an extra <router-outlet> by mistake.

  4. Your wrapper component is specified in AppRoutingModule and inside the child component. Often I will have a wrapper component that handles standard layout things like header, footer, menu etc. All my child routes will then sit inside this when set out in my AppRoutingModule. Sometimes it is possible to use this wrapper component inside another component as well, thereby duplicating it as it is already specified in the AppRoutingModule. This would lead to an extra <router-outlet>.

Gileadite answered 16/2, 2021 at 12:22 Comment(0)
H
1

I had XComponent placed in

bootstrap: [AppComponent, XComponent]

in app.module.ts.

After removing XComponent ngOnInit was triggered only once.

Haukom answered 15/4, 2021 at 10:46 Comment(0)
A
1

This happened to me because I had the arribute href="#" in the anchor tag. do not use in the anchor tag . This will get fixed if you use the routerLink as per the angular code as shown below and try to avoid having ngInit(0 method in the appComponent.ts

<a  class="nav-link" [routerLink]="['/login']"> Sign In. </a>
Angloindian answered 9/7, 2021 at 16:3 Comment(1)
Can you elaborate on ? > try to avoid having ngInit(0 method in the appComponent.tsBarbarossa
R
1

My solution was to use ngIf in the component to prevent undefined inputs, in you case would be:

<result-comp *ngIf="answers" [answers]="answers"></result-comp>
Rybinsk answered 14/2, 2022 at 16:26 Comment(0)
L
1

In my case the problem was that Login page was getting called twice after hit Sign Out button. The cause was bad syntax on the Sign Out button. The code causing problem was:

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

Removing click event from icon tag and putting into link tag resolved the problem.

<a routerLink="/login" (click)="logout()">
  <i class="fa fa-sign-out"></i>
</a>
Lentamente answered 12/8, 2022 at 13:27 Comment(0)
W
1

Check your main.ts file. I think on default Angular apps have these two separate references for platformBrowserDynamic(). I commented out the second one and it worked:

const platform = platformBrowserDynamic(); platform.bootstrapModule(AppModule);

//platformBrowserDynamic().bootstrapModule(AppModule) // .catch(err => console.error(err));

Windlass answered 22/2, 2023 at 20:8 Comment(0)
P
0

ngOnInit firing twice could be caused by an template compilation error, as can be read above.

As some of you've already mentioned above as well, can it be that the missing closing tag is the issue?

<result-comp *ngIf = '!isQuestionView' [answers] = 'ansArray'><result-comp>

, should be:

<result-comp *ngIf = '!isQuestionView' [answers] = 'ansArray'></result-comp>.
Pitching answered 29/12, 2023 at 22:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.