Проблема маршрутизации Angular2 и ngOnInit вызывается дважды
У меня очень странная проблема с маршрутизацией Angular 2 туда, где мой ngOnInit
в компоненте, к которому я направляюсь, вызывается дважды, а маршрут в браузере сбрасывается на исходный маршрут.
у меня есть NotificationListComponent
и NotificationEditComponent
в MaintenanceModule
,
В моем корне 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 { }
И у меня есть /maintenance/list
маршрут, определенный в моем MaintenanceModule
, который указывает на мой NotificationListComponent
, также как и /maintenance/edit/:id
маршрут, который указывает на мой NotificationEditComponent
,
maintenance.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
событие связано с edit(id: number)
метод в моем NotificationListComponent
tification-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;
}
}
tification-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()">×</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
функция, она достигает этой точки останова дважды.
tification-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
);
}
}
По-видимому, это также вызывает другие проблемы, потому что при попытке связать input
значения к значениям в моем NotificationEditComponent
используя, например, [(ngModel)]="notification.notificationId"
значение не отображается на экране, хотя я вижу с расширением Augury chrome, а также записываю объект на консоль, что значение заполняется в компоненте.
tification-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()">×</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
и по-прежнему возникает та же проблема, когда я использую реальный сервис, а не просто имитировать данные.
tification.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!');
}
}
И служба, кажется, работает нормально - я вижу, что конечная точка успешно возвращает данные, и я вижу, что данные выглядят правильно, когда я смотрю на мой NotificationEditComponent
с хромовым расширением Augury. Но данные не отображаются в шаблоне, и маршрут в URL возвращается к /maintenance/list
хотя шаблон для /maintenance/edit/:id
маршрут по-прежнему отображается.
Обновление 2:
В соответствии с предложением @user3249448 я добавил следующее AppComponent
для некоторой отладки:
constructor(private _router: Router) {
this._router.events.pairwise().subscribe((event) => {
console.log(event);
});
}
Вот результат этого, когда я нажимаю на одну из ссылок "изменить":
2 ответа
Наконец удалось решить проблему после получения справки по отладке от @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)
По сути, мой шаблон обрабатывался до того, как был возвращен вызов к моему веб-сервису, и мой шаблон не мог быть правильно отображен, потому что notification
было undefined
вот так я и получил NavigationError
, что вызвало описанное поведение (не кажется ли, что эта ошибка должна быть зарегистрирована на консоли без необходимости добавления дополнительного кода отладки в ваш AppComponent
?).
Чтобы это исправить, все что мне нужно было сделать, это добавить *ngIf
к моему fieldset
который содержал всю информацию о notification
,
<p-fieldset [legend]="'Edit Notification'" *ngIf="notification">
И теперь мой шаблон загружается правильно, как только данные возвращаются из веб-службы.
Я предполагаю, что ваш браузер интерпретирует вашу ссылку редактирования как фактическую, а не делает то, что вы хотите, чтобы она делала. Это связано с тем, что вы видите вспышку - похоже, что браузер пытается перейти по указанной ссылке.
Попробуйте изменить элемент, на который вы щелкаете в своем списке уведомлений, чтобы отредактировать уведомление от тега привязки до простого span или div. Если вы не получаете вспышку, то вы знаете, что это была ваша проблема.