【问题标题】:Angular 4 ngOnInit not called after router.navigate在 router.navigate 之后未调用 Angular 4 ngOnInit
【发布时间】:2017-11-03 22:15:31
【问题描述】:

我有 3 个选项卡,其中一个选项卡显示一个包含员工列表的表格。第一次加载时效果很好。ngOnInit 使用 http get 从服务器获取数据。之后,当我单击添加新员工以打开一个表单时,该表单从用户那里获取输入,当单击该提交时,我调用一个函数,该函数调用 http post 服务将该数据发布到我的服务器并在其中插入记录,然后在那之后它被重定向回员工组件,但是现在已经加载了员工组件,除非我重新编译我的代码,否则我看不到我插入到表中的新记录。

employee.component.ts(加载员工表)

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

问题是我从 ngOnInit 调用我的 API,它在组件第一次加载时完美加载。当我提交表单时,它会转到我的 API,然后被重定向回员工组件,但数据并未按应有的方式更新。

P.S:对于这么小的帖子,我很抱歉。我对这个网站有点陌生。

更新:

自从我发布此主题以来已经一年多,我看到很多人从中受益或没有受益。不过我想指出,我已经了解导致错误的原因,现在我将尝试让您了解解决方案。

这里要适应的最重要的概念是 Angular Life Cycle Hooks。 发生的情况是,我们在第一次加载组件时调用 ngOnInit,这只会在 Angular 应用程序启动时触发一次。这类似于类构造函数,但它只触发一次。所以你不应该在这里放置任何与 DOM 相关的修改。你应该了解 Angular Life Cycle Hooks 来解决这个问题。自过去 8 个月以来我搬到 Vuejs 后,我没有一个可行的解决方案,但有空闲时间我会在这里发布更新。

【问题讨论】:

  • 需要一些代码兄弟
  • 如果您能在此处发布一些演示此行为的 plunker 将非常高兴
  • 我已经添加了代码兄弟。我很抱歉没有第一次这样做。 @丹尼尔库克
  • 我也有类似的问题。也无法访问构造函数。 Angular 5

标签: angular ngoninit


【解决方案1】:

在EmployeesComponent中添加以下行

ionViewWillEnter(){
  this.ngOnInit();
}

重定向后会手动调用ngOnInit

【讨论】:

    【解决方案2】:

    1.导入要导航的组件

    例如。 import { SampleComponent} from './Sample.component';

    2.向要导航的构造函数添加组件

    constructor( private Comp: SampleComponent){}
    

    3.将此代码添加到您需要导航的地方

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

    eg.在员工插入后重定向到员工列表页面

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

    【讨论】:

    • 需要适当的缩进。
    【解决方案3】:

    我认为发生的情况是您的路由调用超出了 Angular 生命周期,因为您正在进行异步调用,对吗?

    首先检查您的日志是否显示如下内容:

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

    如果是这种情况,解决方案真的很简单,你必须告诉 Angular 在它们的生命周期内调用你的路由指令。

    下一个代码应该可以解决您的问题:

    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']));
           }  
         );
    }
    
    

    【讨论】:

    • 你是救生员。我遇到了一些关于路由器的问题,我没有注意到关于 Angular Zone 的警告日志。
    • 我搬到了 Ractive,Angular 让一切变得非常复杂和耗时,就像开发人员在与框架作斗争。
    【解决方案4】:

    确保您在 app.component.html 中有 <route-outlet></route-outlet>。这应该适用于最新版本的 Angular 5。

    【讨论】:

      【解决方案5】:

      当您的组件与路由链接时,最好在构造函数中添加您的代码以订阅 ActivatedRoute 参数并强制更改检测,例如:

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

      【讨论】:

        【解决方案6】:

        如果你降级@angular/router@4.1.3 似乎在routerLink 上工作,如果你导航它将触发ngOnInit

        【讨论】:

          【解决方案7】:

          作为一般规则,在路由时,角度路由器将尽可能重用组件的相同实例。

          因此,例如,从/component/1 导航到/component/2,其中 url 映射到相同的组件(但具有不同的参数)将导致路由器在导航到 /component/1 时实例化 Component 的实例,然后在导航到 /component/2 时重新使用同一个实例。根据您所描述的内容(ngOnInit 只被调用一次),似乎这就是您遇到的情况。如果没有看到您的模板和路线配置,很难确定。我知道您说您的 URL 正在从 /employees 更改为 /form,但这可能无关紧要,具体取决于您的模板和路由配置的设置方式。如果您愿意,可以在此处发布该代码(您的模板和路由器配置)以供检查。

          除此之外,另一种选择是路由器将其所有事件公开为流。因此,您可以订阅该流并对其采取行动,而不仅仅是依赖ngOnInit

          在您的 employee.component.ts 中

          .....
          export class EmployeesComponent implements OnInit {
          
          .....
          
          ngOnInit() {
          
          
             this.router.events
                        // every navigation is composed of several events,
                        // NavigationStart, checks for guards etc
                        // we don't want to act on all these intermediate events,
                        // we just care about the navigation as a whole,
                        // so only focus on the NavigationEnd event
                        // which is only fired once per router navigation
                        .filter(e => e instanceof NavigationEnd)
                        // after each navigation, we want to convert that event to a call to our API
                        // which is also an Observable
                        // use switchMap (or mergeMap) when you want to take events from one observable
                        // and map each event to another observable 
                        .switchMap(e => this.employeeService.getEmployees())
                        .subscribe(res => this.employeeObj = res);    
          }
          

          编辑

          我看到一段奇怪的代码:

          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(user){   // <--  change this line
              this.employeeService.addEmployee(user)
              .subscribe(
                  (response) => { this.router.navigate(['/employees']); }  
              );
          
            }
          }
          

          但总的来说,如果您导航到员工组件,然后导航到表单组件,然后返回到员工组件,那么当您第二次点击员工组件时,您的员工列表不会刷新。

          您能否使用console.log 语句来确保在上面的屏幕流中多次调用ngOnInit?因为,根据您的路由器配置,当您导航到员工列表以形成并返回时,您的员工组件应重新初始化(再次调用ngOnInit

          【讨论】:

          • 先生,这个解决方案也不起作用。显示我发布所有文件的屏幕截图?
          • 您可以发布文件的代码。你遇到了什么错误?
          • 是的,先生,这里是屏幕截图: 已加载表中的员工组件并从 API 获取数据:prntscr.com/ffzznd 用于使用 API 将数据插入数据库的表单组件:prntscr.com/ffzzxs用于发布 http 并获取对我的 API 的请求的服务文件:prntscr.com/fg006t
          • 先生有什么事吗?仍然找不到任何解决方案
          • 再一次,您遇到的错误是什么?此外,作为一般规则,请发布实际代码而不是屏幕截图。我无法从屏幕截图中复制、粘贴和调整代码示例,因此很难解决代码中的问题
          【解决方案8】:

          请尝试在employee 组件中添加router 事件。这样每次/employee url 状态被路由时,它都会获取员工详细信息。

          employee.ts 组件

          constructor(private employeeService: EmployeeService, private router:Router) 
          { }
          
          ngOnInit() {    
            this.router.events.subscribe(
              (event: Event) => {
                     if (event instanceof NavigationEnd) {
                          this.employeService.getEmployees().subscribe(res => this.employeeObj = res);
                     }
              });
          }
          

          【讨论】:

          • return this.http.post('MY_API',empform) 单独不会为您获取结果。您必须映射结果。 angular.io/docs/ts/latest/guide/server-communication.html如果您已经这样做了,请更新问题。
          • 我已经做到了,先生。我仍然无法更新员工组件。
          • 是的,先生,没错。它不是。如何向您展示我的模板文件?
          • 请注意,如果您的 url 路由在您的 form 组件加载时没有更改,那么即使您执行 router.navigateemployee 组件也不会被刷新。
          • 先生,URL 从 localhost:4200/employee 更改为 localhost:4200/form,然后在我提交表单时又更改回 localhost:4200/employee。
          猜你喜欢
          • 1970-01-01
          • 2018-06-22
          • 1970-01-01
          • 2016-06-26
          • 2017-10-04
          • 2018-03-30
          • 1970-01-01
          • 2020-01-10
          • 2020-06-09
          相关资源
          最近更新 更多