2017-03-09 214 views
11

我有一個非常奇怪的問題,與Angular 2路由到我的路由組件中的ngOnInit被調用兩次,並且瀏覽器中的路由被重置爲原始路線。Angular2路由問題和ngOnInit調用兩次

我有一個NotificationListComponent和一個NotificationEditComponentMaintenanceModule

在我的根目錄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 { } 

我有我的NotificationListComponent在我MaintenanceModule定義的/maintenance/list路線,這點,以及指向我NotificationEditComponent一個/maintenance/edit/:id路線。

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路線,我可以看到列表中的所有我的通知。對於列表中的每個通知有一個編輯圖標,它有它在我的NotificationListComponent

通知,list.component.ts綁定到edit(id: number)方法click事件:

@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; 
    } 
} 

通知列表.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函數中放置了斷點,它會兩次觸發該斷點。

通知-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 
    ); 
    } 
} 

這似乎也可能會導致其他問題,因爲當試圖綁定我NotificationEditComponent使用input值值,例如[(ngModel)]="notification.notificationId",值沒有顯示在屏幕上,儘管我可以用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鉻擴展的NotificationEditComponent。但是數據不會顯示在模板中,並且即使仍顯示/maintenance/edit/:id路由的模板,URL中的路由也會返回到/maintenance/list

更新2:

至於建議由@ user3249448,我增加了以下我AppComponent一些調試:

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

這裏是一個輸出,當我點擊「編輯之一「鏈接:

Route logging

+0

您使用的是最新的Angular2版本嗎?前段時間有一個問題導致了這種行爲,但之前AFAIR被修復了。 –

+0

嘗試刪除通配符路由並查看問題是否消失。我記得閱讀有關通配符路由需要在列表中的最後一個,我不太確定在你的配置情況。 – wolfhoundjesse

+0

我使用** 2.4.8 **和'@ angular/router'版本** 3.4.8 **。即使沒有通配符路由,問題仍然存在。 –

回答

5

從@ user3249448獲取調試幫助後終於能夠解決問題。

原來我得到這個NavigationError,即使正在記錄到控制檯沒有錯誤:

enter image description here

這裏是完整的堆棧跟蹤:

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中添加額外的調試代碼?)。

要解決這個問題,我所要做的就是將*ngIf添加到我的fieldset,其中包含有關notification的所有信息。

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

現在我的模板一旦從Web服務返回數據就會正確加載。

+0

免費提示,你也可以使用elvis運算符http://stackoverflow.com/questions/34833358/angular-2-typeerror-l-thing0-is-undefined-in-thing-title-in-appcomponent/34833436#34833436 –

+0

官方文檔在這裏:https://angular.io/docs/ts/latest/guide/template-syntax.html#!#safe-navigation-operator –

0

我的猜測是,您的瀏覽器解釋你的修改鏈接作爲一個實際的李而不是做你想做的事情。這是基於您看到閃光燈這一事實 - 聽起來像瀏覽器試圖訪問有問題的鏈接。

嘗試更改您在通知列表中單擊的項目以編輯從錨標記到簡單跨度或div的通知。如果你沒有得到閃光燈,那麼你知道這是你的問題。

+0

不,更改爲一個div不起作用。這不是實際的頁面閃爍。該頁面實際上正確地呈現編輯模板(除了模型沒有正確綁定輸入),只有地址欄閃爍到正確的編輯路徑,然後返回到列表路徑,而編輯模板仍然顯示在實際頁面上。 –