简体   繁体   中英

Child component not updating on parent route change

I have been trying to learn angular 2 following along with this tutorial [Build an application with Angular 2 and Firebase][1] and trying to extend on it. But I have hit a snag when trying to nest multiple routes.

App structure:

Goals – (has router-outlet)
 > Single Goal with Experiments list – (has router-outlet)
  > Single Experiment – (has router-outlet)
   > Experiment Notes

Router setup:

export const routerConfig : Route[] = [
  {
    path: 'goals',
    children: [
      {
        path: ':id', component: SingleGoalComponent,
        children: [
          {
            path: 'experiments',
            children: [
              { path: ':id', component: ExperimentDetailsComponent,
                children: [
                  { path: '', redirectTo: 'notes', pathMatch: 'full' },
                  { path: 'notes', component: ExperimentNotesComponent }
                ]
              },
              { path: 'new', component: NewExperimentComponent },
              { path: '' }
            ]
          },
          { path: '', redirectTo: 'experiments', pathMatch: 'full' }
        ]
      },
      { path: '', component: GoalsComponent }
    ]
  },
  { path: 'notes', component: NotesComponent },
  { path: '', redirectTo: 'goals', pathMatch: 'full' },
  { path: '**', redirectTo: 'goals', pathMatch: 'full' }
];

The Problem

If I click on Experiment 1 in the Experiment List I got to goals/1/experiments/1/notes the url is correct and I see the correct Experiment 1's Notes .

If I then click on Experiment 2 in the Experiment List goals/1/experiments/2/notes the url is correct the experiment details are correct but the notes are still Experiment 1's Notes .

If I then refresh the browser, Experiment 2 to loads and the notes are now Experiments 2's Notes which is correct.

This is how I get the experimentId for retrieving the notes

experiment-notes.component.ts

experimentId: string;
  goalId: string;

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private experimentsService: ExperimentsService,
    private _location: Location) { }

  ngOnInit() {

    Observable.combineLatest(this.route.parent.params, this.route.parent.parent.params)
      .forEach((params: Params[]) => {
        this.experimentId = params[0]['id'];
        this.goalId = params[1]['id'];
      });

    console.log('Experiment ID: ' + this.experimentId + '| Goal Id: ' + this.goalId);

    this.notes$ = this.experimentsService.findAllNotesForExperiment(this.experimentId);

I'm sure it's an obvious mistake I'm making but for the life of me I can't see where I am going wrong with this.

This is because the ngOnInit() method is only calling once during creating the component. When u click on Experiment 2, you don't create a new experiment-component. You just use the old one.

The Url is changing because you are still subscribed on the route params. But your Service call is out of the Observable. SO just put the service call into your obserable, then everytime when the route param is changed, it will load the new data.

ngOnInit() {

    Observable.combineLatest(this.route.parent.params, this.route.parent.parent.params)
      .forEach((params: Params[]) => {
        this.experimentId = params[0]['id'];
        this.goalId = params[1]['id'];

        console.log('Experiment ID: ' + this.experimentId + '| Goal Id: ' + this.goalId);
        this.notes$ = this.experimentsService.findAllNotesForExperiment(this.experimentId);
      });

The API has changed quite a bit in the latest version angular 5.2.5 As Emre says the problem is that ngOnInit is called only once when the child component is first created, after creation the component needs to be notified of changes to the url so that it can fetch the parameter again, this can be done by adding a listener on the Router object and then using the route object to get the part required. Here is some example code based on the tour of heroes sample app:

import {Component, Input, OnInit} from '@angular/core';
import {Hero} from '../hero';
import {HeroService} from "../hero.service";
import {ActivatedRoute, Router} from "@angular/router"; //Import Router and ActivatedRoute classes
import {Location} from '@angular/common';
import {MessageService} from "../message.service";

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
  @Input() hero: Hero;

  constructor(private route: ActivatedRoute,
              private heroService: HeroService,
              private messageService: MessageService,
              private location: Location,
              private router: Router) {
  }

  ngOnInit(): void {
    this.router.events.subscribe((val) => {//Use Router class to subscribe to events
      const id = +this.route.snapshot.paramMap.get('id');//When a route event occurs use the active route to update the parameter needed
      this.getHero(id);//Do what you would have initially done with the url value
    });
  }

  getHero(id): void {
    this.messageService.add(`HeroDetailComponent: fetching hero: ${id}`);
    this.heroService.getHero(id)
      .subscribe(hero => this.hero = hero);
  }

  goBack(): void {
    this.location.back();
  }
}

The most relevant parts are in ngOnInit()

activatedRoute has parent property. Simply need to subscribe to params in children component like below:

this.route.parent.params.subscribe((params: Params) => {
  // some stuff
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM