2

I've got a small Plunk I'm using for playing around with the new Router 3.0 alpha currently available in Angular 2. It works well in general, but the issue is that once I click on a link that routes to the 'detail' component with a particular ID, it never changes when I click on a different link with a different ID. The component is never being reinstantiated, so it only ever shows what it was passed the very first time it is loaded.

Here's the component in question:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ContactsService } from './contacts.service';

@Component({
  selector: 'contacts-detail',
  template: `
    <h2>{{contact.name}}</h2>
  `
})
export class ContactsDetailComponent implements OnInit { 

  constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
  }

  ngOnInit() {
    this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
    console.log('Fetching user', this.route.snapshot.params.id);
  }
}

Here is the Plunk demonstrating the problem. Click on one author name and then another to see it not change.

Michael Oryl
  • 20,856
  • 14
  • 77
  • 117

3 Answers3

8

In your ContactsDetailComponent, change the OnInit to this:

ngOnInit() {
    this.sub = this.route.params.subscribe(params => {
     let id = +params['id']; 
     this.contact = this.contactsService.getContact(id);
   });
  }

Worked for me in your Plunk.

R. Richards
  • 24,603
  • 10
  • 64
  • 64
  • That certainly works for me, but it seems odd. I'll go with this if nothing else comes my way, though. Thanks very much for taking the time to answer. – Michael Oryl Jun 24 '16 at 15:15
  • it's not odd, it's component reuse. – TDaver Jun 24 '16 at 15:31
  • @TDaver Let me change my choice of words then; it's not obvious at all. I looked for something like a ngOnReload interface to work with, but found nothing. That would seem like a more Angular2'ish way to do what needs to be done. – Michael Oryl Jun 25 '16 at 00:34
  • @TDaver Look at my answer to this question. It uses `ngDoCheck()` to catch the changes. I don't know if it is the precise lifecycle hook to use, though. I expect it would fire too often in a non-trivial component. Plunk in my answer showing it in use. – Michael Oryl Jun 25 '16 at 01:12
1

There appear to be multiple lifeCycle hooks that could possibly be used for this. I managed to get the desired behavior using the DoCheck interface and implementing the associated ngDoCheck() method in the component class, as seen below.

import { Component, DoCheck } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ContactsService } from './contacts.service';

@Component({
  selector: 'contacts-detail',
  template: `
    <h2>{{contact.name}}</h2>
  `
})
export class ContactsDetailComponent implements AfterViewChecked, DoCheck { 

  constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
  }

  ngDoCheck() {
    this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
  }
}

Here's a plunk with the updated code.

I'm not convinced this is the best/correct lifecycle hook to use, though. Perhaps there is some sort of hook available from the Router that would serve this better.

Michael Oryl
  • 20,856
  • 14
  • 77
  • 117
  • It’s an interesting approach (and it obviously works). From the little bit of documentation there is around `DoCheck`, it seems like it is meant to be used with directives. Any idea if there is an advantage to getting parameter values from the snapshot versus the observable? – R. Richards Jun 25 '16 at 11:29
  • @R.Richards I'm not sure this is the correct thing at all. I did a test with a form field in the component tied to an `@Input` variable and the `ngDoCheck()` method fired at every keystroke in the input field, which obviously is not what we really want. There just 'feels' like there ought to be something better, but I did see an article (not official) mention that the new router expects people to handle this type of situation like you do in your answer... – Michael Oryl Jun 25 '16 at 12:13
  • 1
    Wow. Thank you @Michael Oryl !! I would like to buy you a cup of coffee. I have been trying to get one of my components to refresh itself when one of its values changes and after numerous attempts I had almost completely given up. I tried implementing the ngDoCheck() method as you suggested and now it just works. Viola. Several hours of frustration have now come to an end. – b0rgBart3 Oct 03 '17 at 20:32
0

Another way to do this:

ngOnInit() {
    this.route.params.forEach((params: Params) => {
      let id = +params['id']; 
      this.contact = this.contactsService.getContact(id);
    });
}

Here retrieve the route params from an Observable. The advantage of using an Observable over Snapshot is to reuse the component without instantiating it again. Looks like this is the recommended way of doing this as per Angular 2.0 final documentation.

chenk
  • 392
  • 7
  • 27