Reputation: 9205
The code below serves the same component
AppComponent
for three different routes, including/
,/route2
, and/route3
.The problem is that the
title
andbodyHTML
properties ofAppComponent
do not change values when the different routes are selected.What specific changes need to be made to the code below so that the app will serve different values for
title
andbodyHTML
when each of the different routes are selected by the user?Below are the steps to reproduce the problem on any computer in a few minutes:
First, I created a seed app using Angular-CLI in the following steps:
cd C:\projects\angular-cli
ng new routes-share-component
cd C:\projects\angular-cli\routes-share-component
ng serve
Next, I changed only 4 files as follows:
I added app.routing.ts
with the following content:
import { Routes, RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
const appRoutes: Routes = [
{ path: '', component: AppComponent },
{ path: 'route2', component: AppComponent },
{ path: 'route3', component: AppComponent }
];
export const routing = RouterModule.forRoot(appRoutes);
I changed app.component.ts
to become the following:
import { Component, OnInit, OnChanges } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Router } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnChanges {
title = 'This is the title!';
bodyHTML = 'Here is the content!'
constructor(private _router:Router, private route:ActivatedRoute) {
console.log('inside the constructor!');
console.log(route.url);
console.log(_router.url);
}
ngOnInit() {
console.log('inside ngOnInit()');
let currentUrl = this._router.url; /// this will give you current url
console.log(currentUrl);
if(currentUrl=='/'){this.title='Home Page Title';this.bodyHTML='Body goes here.';}
if(currentUrl=='/route2'){this.title='Route 2 Title';this.bodyHTML='Body goes here.';}
if(currentUrl=='/route3'){this.title='Route 3 Title';this.bodyHTML='Body goes here.';}
console.log(this.route.url);
}
ngOnChanges() {
console.log('inside ngOnChanges()!');
let currentUrl = this._router.url; /// this will give you current url
console.log(currentUrl);
if(currentUrl=='/'){this.title='Home Page Title';this.bodyHTML='Body goes here.';}
if(currentUrl=='/route2'){this.title='Route 2 Title';this.bodyHTML='Body goes here.';}
if(currentUrl=='/route3'){this.title='Route 3 Title';this.bodyHTML='Body goes here.';}
console.log(this.route.url);
}
}
Similarly, app.component.html
was simplified into the following:
<div style="text-align:left">
<h1>{{title}}</h1>
<p>{{bodyHTML}}</p>
</div>
And app.module.ts
becomes the following, with app.routing.ts
included:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { routing } from './app.routing';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule, routing
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Note that the ngOnInit()
block prints in the console, but that the ngOnChanges()
block does NOT. This means that the title is always Home Page Title
, no matter which route is selected.
What specific changes need to be made to the above code so that each route prints different values in the browser for title
and bodyHTML
?
Per @BeetleJuice's suggestion, I tried the following new version of AppComponent
, but it is showing compilation errors at the lines routerSub:Subscription
, and at the line this.routerSub = this.router.events.filter(....)
.
import { Component, OnInit, OnChanges } from '@angular/core';
import { NavigationEnd, Router, ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'This is the title!';
bodyHTML = 'Here is the content!';
routerSub:Subscription;
constructor(private router:Router) {
console.log('inside the constructor!');
console.log(router.url);
}
ngOnInit(){
// listen to NavigationEnd events
this.routerSub = this.router.events.filter(e=>e instanceof NavigationEnd)
// capture the new URL
.map(e.NavigationEnd => e.url)
.subscribe(url => {
/* TODO: use URL to update the view */
});
}
// When the component is destroyed, you must unsubscribe to avoid memory leaks
ngOnDestroy(){
this.routerSub.unsubscribe();
}
}
What else needs to be changed?
Upvotes: 2
Views: 366
Reputation: 40946
Two things you need to realize:
When Angular navigates to a new route that uses the same component, it does not re-initialize the component. So your ngOnInit
runs only the first time the component is loaded.
ngOnChanges is not triggered when any component property changes. It is triggered when a data-bound property (as denoted by @Input() someProp
) is changed by a parent component. See the docs. So your ngOnChanges
is not getting triggered either.
One way to update your model when the route changes but the Component doesn't is to inject the Router, listen to the Router.events
observable and react to the route change there
app.component.ts
import {NavigationEnd, Router} from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
...
routerSub:Subscription;
ngOnInit(){
// listen to NavigationEnd events
this.routerSub = this.router.events.filter(e=>e instanceof NavigationEnd)
// capture the new URL
.map((e:NavigationEnd) => e.url)
.subscribe(url => {
/* TODO: use URL to update the view */
});
}
// When the component is destroyed, you must unsubscribe to avoid memory leaks
ngOnDestroy(){
this.routerSub.unsubscribe();
}
By the way, it may not be a good idea to have multiple routes that resolve to the same component. Consider having one parameterized route instead!
Upvotes: 2