CodeMed
CodeMed

Reputation: 9205

use Angular 2 router to pass different values to the same component

The code below serves the same component AppComponent for three different routes, including /, /route2, and /route3.

The problem is that the title and bodyHTML properties of AppComponent 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 and bodyHTML 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:


Create the Seed App:

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


Change Only 4 files:

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?


@BeetleJuice's suggestions:

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

Answers (1)

BeetleJuice
BeetleJuice

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

Related Questions