Angular 4 ngOnInit not called after router.navigate

Viewed 35038

I have 3 tabs in which one tab shows a table with list of employees. Works good when it is loaded the first time.ngOnInit Fetches data from server using http get. After that when I click add new employee to open a form, which take input from user and when that submit is clicked I call a function which calls the http post service to post that data to my server where it inserts the records and then after that it is redirected back to employee component, but now that employee component was already loaded, I cannot see the new record that I inserted in table unless I recompile my code.

employee.component.ts ( Which loads the employee Table)

import { Component, OnInit, OnDestroy } from '@angular/core';
import { EmployeeService } from '../employee.service';
@Component({
  selector: 'app-employees',
  templateUrl: './employees.component.html',
  styleUrls: ['./employees.component.css']
})
export class EmployeesComponent implements OnInit {

public employeeObj:any[] = [{emp_id:'',empname:'',joindate:'',salary:''}] ;
constructor(private employeService:EmployeeService) { }

ngOnInit() {    
this.employeService.getEmployees().subscribe(res => this.employeeObj = res);
}

}

form.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { EmployeeService } from '../../employee.service';
import { Router } from '@angular/router';

@Component({
    selector: 'app-form',
    templateUrl: './form.component.html',
    styleUrls: ['./form.component.css'],

})
export class FormComponent implements OnInit {
empform;

ngOnInit() { 
this.empform = new FormGroup({
    empname: new FormControl(""),
    joindate: new FormControl(""),
    salary: new FormControl("")
})
} 
constructor(private employeeService: EmployeeService, private router:Router) 
{ }

 onSubmit = function(user){
    this.employeeService.addEmployee(user)
    .subscribe(
        (response) => { this.router.navigate(['/employees']); }  
    );

}
}

employee.service.ts

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/Rx';
@Injectable()
export class EmployeeService{
constructor(private http:Http){}
addEmployee(empform: any[]){
    return this.http.post('MY_API',empform);
}

getEmployees(){
    return 
this.http.get('MY_API').map((response:Response)=>response.json());
}
}

AppModule.ts

    import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';

import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { EmployeeService } from './employee.service';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { NavComponent } from './nav/nav.component';
import { ContainerComponent } from './container/container.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { EmployeesComponent } from './employees/employees.component';
import { CompaniesComponent } from './companies/companies.component';
import { InternsComponent } from './interns/interns.component';
import { FormComponent } from './employees/form/form.component';
import { ComformComponent } from './companies/comform/comform.component';
import { InternformComponent } from './interns/internform/internform.component';

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent,
    NavComponent,
    ContainerComponent,
    DashboardComponent,
    EmployeesComponent,
    CompaniesComponent,
    InternsComponent,
    FormComponent,
    ComformComponent,
    InternformComponent
  ],
  imports: [    
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    HttpModule,
    RouterModule.forRoot([
            {
                path:'dashboard',
                component:DashboardComponent
            },
            {
                path:'employees',
                component:EmployeesComponent
            },
            {
                path:'companies',
                component:CompaniesComponent
            },
            {
                path:'interns',
                component:InternsComponent
            },
            {
                path:'addemployee',
                component:FormComponent
            },
            {
                path:'comform',
                component:ComformComponent
            },
            {
                path:'internform',
                component:InternformComponent
            }       
      ])
  ],
  providers: [EmployeeService],
  bootstrap: [AppComponent]
})
export class AppModule { }

The problem is I am calling my API from ngOnInit which loads perfectly the first time the component loads. When I submit the form it goes to my API and then it is redirected back to the employee component, but the data is not updated as it should.

P.S : I am sorry for such small post. I am kind of new to this website.

Update :

It has been more than a year now since I posted this thread and I see a lot of people have benefit from it or maybe not. However I would like to point that I have already understood what caused the error and I will now try to make you understand the solution.

The most important concept to adapt here is the Angular Life Cycle Hooks. What happens is, we call ngOnInit the first time a component is loaded and this will only fire once when the angular application is bootstrapped. This is similar to a class constructor but it only fires once. So you should not put any DOM related modifications here. You should understand Angular Life Cycle Hooks to solve this problem. I do not have a working solution with me as I moved to Vuejs since last 8 months but in some free time I will post an update here.

8 Answers

When your component is linked with a route, better to add your code in the constructor subscribing to ActivatedRoute params and forcing change detection eg:

constructor(private route: ActivatedRoute, private changeDetector: ChangeDetectorRef) {
    super();
    this.route.params.subscribe((data) => {
    */update model here/*
    this.changeDetector.detectChanges();
   } 
}

What I think it happens is your route call is outside the Angular lifecycle because you are doing an async call right?

First check if your log show something like this:

WARN: 'Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?'

If this is the case the solution is really simple, you have to tell Angular to call your routing directive inside their lifecycle.

Next code should fix your problem:

import { NgZone } from '@angular/core';
import { Router } from '@angular/router';

...
constructor(
    private employeeService: EmployeeService, 
    private router:Router,
    private ngZone: NgZone) { }

 onSubmit = function(user) {
   this.employeeService.addEmployee(user)
     .subscribe(
       (response) => { 
         this.ngZone.run(() =>
           this.router.navigate(['/employees']));
       }  
     );
}

add the following line in EmployeesComponent

ionViewWillEnter(){
  this.ngOnInit();
}

it will manually invoke ngOnInit after redirection

If you downgrade @angular/router@4.1.3 seems to be working where on routerLink if you navigate it will trigger ngOnInit

Make sure you have <route-outlet></route-outlet> inside app.component.html. This should work just fine with the latest version of Angular 5.

1.import component which is to be navigated

eg. import { SampleComponent} from './Sample.component';

2.Add component to constuctor to be navigated

constructor( private Comp: SampleComponent){}

3.Add this code where u need to navigate

this.Comp.ngOnInit();
this._router.navigate(['/SampleComponent']);--/SampleComponent-your router path

eg.After employee Insert it redirects to employee list page

 this.service.postData(Obj)
          .subscribe(data => {
            (alert("Inserted Successfully"));
            this.Comp.ngOnInit();
            this._router.navigate(['/SampleComponent']);
          },
            error => {
              alert(error);
          });
Related