【问题标题】:Angular2 routing issue and ngOnInit called twiceAngular2 路由问题和 ngOnInit 调用了两次
【发布时间】:2017-07-30 17:16:39
【问题描述】:

我在使用 Angular 2 路由时遇到了一个非常奇怪的问题,我路由到的组件中的 ngOnInit 被调用了两次,并且浏览器中的路由被重置为原始路由。

我在MaintenanceModule 中有一个NotificationListComponent 和一个NotificationEditComponent

在我的根 AppModule 中,我设置了 RouterModule 以将任何未映射的路由重定向到 /maintenance/list

app.module.ts

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpModule,
    RouterModule.forRoot([
      {path: "", redirectTo: "maintenance/list", pathMatch: "full"},
      {path: "**", redirectTo: "maintenance/list", pathMatch: "full"}
    ], {useHash: true}),
    CoreModule.forRoot({notificationUrl: "http://localhost:8080/notification-service/notifications"}),
    MaintenanceModule
  ],
  providers: [NotificationService],
  bootstrap: [AppComponent]
})
export class AppModule { }

我在MaintenanceModule 中定义了/maintenance/list 路由,它指向我的NotificationListComponent,还有一个/maintenance/edit/:id 路由指向我的NotificationEditComponent

ma​​intenance.module.ts

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([
      {path: "maintenance/list", component: NotificationListComponent, pathMatch: 'full'},
      {path: "maintenance/edit/:id", component: NotificationEditComponent, pathMatch: 'full'}
    ]),
    FormsModule
  ],
  declarations: [
    NotificationListComponent,
    NotificationEditComponent
  ]
})
export class MaintenanceModule {}

当我的应用程序加载时,它正确地遵循/maintenance/list 路由,我可以在一个列表中看到我的所有通知。对于列表中的每个通知,都有一个编辑图标,它的 click 事件绑定到我的 NotificationListComponent 中的 edit(id: number) 方法

notification-list.component.ts

@Component({
  templateUrl: 'notification-list.component.html'
})
export class NotificationListComponent implements OnInit {

  notifications: Notification[];
  errorMessage: string;

  constructor(private _notificationService: NotificationService,
              private _router: Router) {}

  ngOnInit(): void {
    this._notificationService.getNotifications()
      .subscribe(
        notifications => this.notifications = notifications,
        error => this.errorMessage = <any>error);
  }

  clearError(): void {
    this.errorMessage = null;
  }
}

notification-list.component.html

<div class="row">
  <h1>Notification Maintenance</h1>

  <div *ngIf="errorMessage" class="alert-box alert">
    <span>{{errorMessage}}</span>
    <a class="right" (click)="clearError()">&times;</a>
  </div>

  <p-dataTable [value]="notifications" [sortField]="'code'" [responsive]="true" [sortOrder]="1" [rows]="10" [paginator]="true" [rowsPerPageOptions]="[10,50,100]">
    <p-header>Available Notifications</p-header>
    <p-column [field]="'code'" [header]="'Code'" [sortable]="true" [style]="{'width':'10%'}"></p-column>
    <p-column [field]="'name'" [header]="'Name'" [sortable]="true" [style]="{'width':'40%'}"></p-column>
    <p-column [field]="'roles'" [header]="'Roles'" [style]="{'width':'40%'}"></p-column>
    <p-column [field]="'notificationId'" [header]="'Edit'" [style]="{'width':'10%'}">
      <template let-row="rowData" pTemplate="body">
        <a [routerLink]="'/maintenance/edit/' + row['notificationId']"><span class="fa fa-pencil fa-2x"></span></a>
      </template>
    </p-column>
  </p-dataTable>
</div>

如您所见,edit(id: number) 方法应导航到 /maintenance/edit/:id 路由。当我单击图标导航到该路线时,浏览器会在地址栏中闪烁正确的路线(例如localhost:4200/#/maintenance/edit/2),但随后地址栏中的路线立即变回localhost:4200/#/maintenance/list。即使地址栏中路由返回/maintenance/list,我的NotificationEditComponent在实际应用中仍然可见。但是,我可以看到ngOnInit 方法在我的NotificationEditComponent 中被调用了两次,因为id 被记录到控制台两次,如果我在ngOnInit 函数中放置一个断点,它会命中该断点两次。

notification-edit.component.ts

@Component({
  templateUrl: "notification-edit.component.html"
})
export class NotificationEditComponent implements OnInit{

  notification: Notification;
  errorMessage: string;

  constructor(private _notificationService: NotificationService,
              private _route: ActivatedRoute,
              private _router: Router) {
  }

  ngOnInit(): void {
    let id = +this._route.snapshot.params['id'];
    console.log(id);
    this._notificationService.getNotification(id)
      .subscribe(
        notification => this.notification = notification,
        error => this.errorMessage = <any>error
      );
  }
}

这似乎也导致了其他问题,因为当尝试使用 [(ngModel)]="notification.notificationId"input 值绑定到我的 NotificationEditComponent 中的值时,该值没有显示在屏幕上,即使我可以使用 Augury chrome 扩展,以及将对象记录到控制台,查看该值已填充到组件中。

notification-edit.component.html

<div class="row">
  <h1>Notification Maintenance</h1>

  <div *ngIf="errorMessage" class="alert-box alert">
    <span>{{errorMessage}}</span>
    <a class="right" (click)="clearError()">&times;</a>
  </div>

  <p-fieldset [legend]="'Edit Notification'">
    <label for="notificationId">ID:
      <input id="notificationId" type="number" disabled [(ngModel)]="notification.notificationId"/>
    </label>
  </p-fieldset>

</div>

有人知道为什么会这样吗?

更新:

我删除了对NotificationService 的调用,并用一些模拟数据替换它们,然后路由开始工作!但是,一旦我向我的服务添加调用,我就会遇到上面描述的相同问题。我什至删除了CoreModule,只是将服务直接添加到我的MaintenanceModule,但每当我使用实际服务而不是模拟数据时,仍然会遇到同样的问题。

notification.service.ts

@Injectable()
export class NotificationService {
  private _notificationUrl : string = environment.servicePath;

  constructor(private _http: Http) {
  }

  getNotifications(): Observable<Notification[]> {
    return this._http.get(this._notificationUrl)
      .map((response: Response) => <Notification[]>response.json())
      .catch(this.handleGetError);
  }

  getNotification(id: number): Observable<Notification> {
    return this._http.get(this._notificationUrl + "/" + id)
      .map((response: Response) => <Notification>response.json())
      .catch(this.handleGetError);
  }

  postNotification(notification: Notification): Observable<number> {
    let id = notification.notificationId;
    let requestUrl = this._notificationUrl + (id ? "/" + id : "");
    return this._http.post(requestUrl, notification)
      .map((response: Response) => <number>response.json())
      .catch(this.handlePostError);
  }

  private handleGetError(error: Response) {
    console.error(error);
    return Observable.throw('Error retrieving existing notification(s)!');
  }

  private handlePostError(error: Response) {
    console.error(error);
    return Observable.throw('Error while attempting to save notification!');
  }
}

而且服务似乎运行良好 - 我可以看到端点成功返回数据,并且当我查看带有 Augury chrome 扩展名的 NotificationEditComponent 时,我可以看到数据看起来正确。但是模板中没有显示数据,即使/maintenance/edit/:id路由的模板仍然显示,URL中的路由返回到/maintenance/list

更新 2:

根据@user3249448 的建议,我将以下内容添加到我的AppComponent 以进行一些调试:

constructor(private _router: Router) {
  this._router.events.pairwise().subscribe((event) => {
    console.log(event);
  });
}

这是我单击“编辑”链接之一时的输出:

【问题讨论】:

  • 您使用的是最新的 Angular2 版本吗?前段时间有一个问题导致了这种行为,但不久前 AFAIR 已修复。
  • 尝试删除通配符路由,看看问题是否消失。我记得读过一些关于通配符路由需要在列表中最后的内容,但我不太确定您的配置是否属于这种情况。
  • 我使用的是 2.4.8@angular/router 版本 3.4.8。即使没有通配符路由,问题仍然存在。
  • 即使我也删除了空路由,问题仍然存在。
  • OK Html 看起来不错。让我们看看导航是如何流动的。我们需要注册各种路线更改/开始/结束事件,这会给我们一些想法,并且可能是导航前 url 是什么的原因。试试这个stackoverflow.com/questions/33520043/…

标签: angular routing ngoninit


【解决方案1】:

在得到@user3249448的调试帮助后终于能够解决问题。

结果我收到了这个NavigationError,即使控制台没有记录任何错误:

这是完整的堆栈跟踪:

TypeError: Cannot read property 'notificationId' of undefined
    at CompiledTemplate.proxyViewClass.View_NotificationEditComponent0.detectChangesInternal (/MaintenanceModule/NotificationEditComponent/component.ngfactory.js:487:49)
    at CompiledTemplate.proxyViewClass.AppView.detectChanges (http://localhost:4200/vendor.bundle.js:80125:14)
    at CompiledTemplate.proxyViewClass.DebugAppView.detectChanges (http://localhost:4200/vendor.bundle.js:80320:44)
    at CompiledTemplate.proxyViewClass.AppView.internalDetectChanges (http://localhost:4200/vendor.bundle.js:80110:18)
    at CompiledTemplate.proxyViewClass.View_NotificationEditComponent_Host0.detectChangesInternal (/MaintenanceModule/NotificationEditComponent/host.ngfactory.js:29:19)
    at CompiledTemplate.proxyViewClass.AppView.detectChanges (http://localhost:4200/vendor.bundle.js:80125:14)
    at CompiledTemplate.proxyViewClass.DebugAppView.detectChanges (http://localhost:4200/vendor.bundle.js:80320:44)
    at ViewRef_.detectChanges (http://localhost:4200/vendor.bundle.js:60319:20)
    at RouterOutlet.activate (http://localhost:4200/vendor.bundle.js:65886:42)
    at ActivateRoutes.placeComponentIntoOutlet (http://localhost:4200/vendor.bundle.js:24246:16)
    at ActivateRoutes.activateRoutes (http://localhost:4200/vendor.bundle.js:24213:26)
    at http://localhost:4200/vendor.bundle.js:24149:58
    at Array.forEach (native)
    at ActivateRoutes.activateChildRoutes (http://localhost:4200/vendor.bundle.js:24149:29)
    at ActivateRoutes.activate (http://localhost:4200/vendor.bundle.js:24123:14)

所以本质上,我的模板在返回对我的 Web 服务的调用之前被渲染,并且我的模板无法正确渲染,因为 notificationundefined,所以我得到了这个 NavigationError,这导致了所描述的行为(不是应该将此错误记录到控制台,而无需在您的AppComponent 中添加额外的调试代码吗?)。

要解决这个问题,我所要做的就是在我的 fieldset 中添加一个 *ngIf,其中包含有关 notification 的所有信息。

<p-fieldset [legend]="'Edit Notification'" *ngIf="notification">

现在,一旦从 Web 服务返回数据,我的模板就会正确加载。

【讨论】:

【解决方案2】:

我的猜测是您的浏览器将您的编辑链接解释为实际链接,而不是执行您希望它执行的操作。这是基于您看到 Flash 的事实 - 听起来浏览器正在尝试访问相关链接。

尝试更改您在通知列表中单击的项目,以将通知从锚标记编辑为简单的 span 或 div。如果你没有得到闪光灯,那么你知道那是你的问题。

【讨论】:

  • 不,更改为 div 不起作用。并且它不是闪烁的实际页面。该页面实际上正确呈现了编辑模板(除了模型未正确绑定到输入),它只是地址栏闪烁到正确的编辑路由,然后返回列表路由,而编辑模板仍然显示在实际页面上.
猜你喜欢
  • 1970-01-01
  • 2019-12-04
  • 2016-10-25
  • 2023-04-10
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 2019-04-04
相关资源
最近更新 更多