Используйте маршрутизатор Angular 2 для передачи разных значений одному и тому же компоненту

В приведенном ниже коде используется один и тот же компонент AppComponent для трех разных маршрутов, включая /, /route2 и /route3.

Проблема в том, что свойства title и bodyHTML AppComponent не изменяют значения при выборе разных маршрутов.

Какие конкретные изменения необходимо внести в код ниже, чтобы приложение обслуживало разные значения для title и bodyHTML, когда пользователь выбирает каждый из разных маршрутов?

Ниже приведены шаги, чтобы воспроизвести проблему на любом компьютере за несколько минут:


Создать приложение Seed:

Сначала я создал начальное приложение с помощью Angular -CLI в следующих шагах:

cd C:\projects\angular-cli
ng new routes-share-component
cd C:\projects\angular-cli\routes-share-component
ng serve


Изменить только 4 файла:

Затем я изменил только 4 файла следующим образом:

Я добавил app.routing.ts со следующим содержанием:

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);

Я изменил app.component.ts, чтобы стать следующим:

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

}

Аналогично, app.component.html было упрощено следующим образом:

<div style="text-align:left">
  <h1>{{title}}</h1>
  <p>{{bodyHTML}}</p>
</div>

И app.module.ts становится следующим, при включенном app.routing.ts:

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 { }

Обратите внимание, что блок ngOnInit() печатает в консоли, но блок ngOnChanges() НЕ. Это означает, что заголовок всегда Home Page Title, независимо от того, какой маршрут выбран.

Какие конкретные изменения необходимо внести в вышеуказанный код, чтобы каждый маршрут печатал разные значения в браузере для title и bodyHTML?


Предложения @BeetleJuice:

В предложении @BeetleJuice я попробовал следующую новую версию AppComponent, но она показывает ошибки компиляции в строках routerSub:Subscription и в строке 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();
  }
}

Что еще нужно изменить?

Ответ 1

Две вещи, которые вам нужно реализовать:

  • Когда Angular переходит к новому маршруту, который использует тот же компонент, он не повторно инициализирует компонент. Таким образом, ваш ngOnInit запускается только при первом загрузке компонента.

  • ngOnChanges - это не, срабатывающий при изменении любого свойства компонента. Он запускается, когда свойство привязки данных (обозначенное символом @Input() someProp) изменяется родительским компонентом. См. документы. Таким образом, ваш ngOnChanges не запускается.

Один из способов обновления модели при изменении маршрута, но Компонент не должен вводить маршрутизатор, слушать наблюдаемый Router.events и реагировать на изменение маршрута там

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();
}

Кстати, может быть неплохо иметь несколько маршрутов, которые разрешают один и тот же компонент. Рассмотрим вместо этого параметризованный маршрут