27

I'm a newcomer to Angular2, I'm used to the Angular 1 digest cycle, meaning if I update the scope of a view I can manually trigger a digest by calling $scope.$digest(). However, I'm not sure how to do this in Angular2, esp given that the new framework doesn't have the implicit data binding that the old version had.

Here's my problem. I have a route that loads a component when a url with a parameter is hit:

// Router
export const AppRoutes : RouterConfig = [
    {
    path: 'my/:arg/view',
    component: MyComponent  
    }
]

Then I have this component:

// Component
export class MyComponent {
    constructor(private route : ActivatedRoute,
      private r : Router) {
    let id = parseInt(this.route.params['value'].id);
    console.log(id);
    // do stuff with id
    }

    reloadWithNewId(id:number) {
        this.r.navigateByUrl('my/' + id + '/view');
    }
}

Lets say I navigate to url /my/1/view. It will call the constructor and the number 1 is logged. However, if I call reloadWithNewId with a new id, reloadWithNewIf(2), the constructor is not called again. How do I manually reload the component?

dopatraman
  • 13,416
  • 29
  • 90
  • 154
  • 6
    The accepted solution (subscribing to the params in ngOnInit()) is the preferred method, but it doesn't always work. For example, I'm using ng2-charts https://github.com/valor-software/ng2-charts , a component that is not responding correctly to changes in x-axis labels (https://github.com/valor-software/ng2-charts/issues/547). If there is a bug such as this in a third party component, is there a way to literally reload that component? – Ho Li Fuk Jan 25 '17 at 00:08

4 Answers4

20

There shouldn't be a need to reload the component. Just update the model and the view updates itself:

export class MyComponent {
    constructor(private route : ActivatedRoute,
      private r : Router) {}

    reloadWithNewId(id:number) {
        this.r.navigateByUrl('my/' + id + '/view');
    }

    ngOnInit() {
      this.sub = this.route.params.subscribe(params => {
         this.paramsChanged(params['id']);
       });
    }

    paramsChanged(id) {
      console.log(id);
      // do stuff with id

    }
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • whats the advantage of using the `ngOnInit` hook vs the constructor like @madhu-ranjan has done? – dopatraman Aug 08 '16 at 19:09
  • 2
    You could do the `this.route.params.subscribe(...)` stuff in the constructor as well. `@Input()`s are not yet available when the constructor is executed but are when `ngOnInit()` is executed (not relevant in this actual use case). – Günter Zöchbauer Aug 08 '16 at 19:13
  • 1
    It's working. but,I have used Activated router instead of route this.activatedRoute.paramMap.subscribe(params => { this.eci = params.get('id'); } – gnganapath Feb 07 '20 at 09:40
14

You can force angular2 to re-initialize component by navigating to some other url and then back to one you want.

Here is my simple workaround using Router class methods (reference: https://angular.io/docs/ts/latest/api/router/index/Router-class.html)

Component constructor:

constructor(private router: Router) {}

In template (example):

(click)="onTabChange(id)"

Then inside component class:

onTabChange() {
  if (this.router.navigated === false) {
    // Case when route was not used yet
    this.router.navigateByUrl(`/module/${id}`);
  } else {
    // Case when route was used once or more
    this.router.navigateByUrl(`/index`).then(
      () => {
        this.router.navigateByUrl(`/module/${id}`);
      });
  }
}
Germaniero
  • 198
  • 1
  • 5
  • 1
    A bit hacky, but it's the only answer that answers the question and is far more efficient than a refresh. – Levi Fuller Jul 19 '17 at 00:54
  • 5
    Only working solution if there is a way to remove the fake URL from the history. Otherwise the browser back button do not do what is expected. – benek Feb 12 '18 at 15:29
  • 1
    @benek Use `skipLocationChange`. `this.router.navigateByUrl('/blank', { skipLocationChange: true })` – Thanh Nguyen Jul 14 '20 at 09:26
  • 1
    @ThanhNguyen this works great for me, but is there anyway to avoid the dummy route with the latest angular versions? – Clueless Sep 17 '20 at 02:16
3

Constructor will not be called again when you are just updating the params. Because the Component class is already instantiated,

you can subscribe to params changes like below if you want to detect the change,

  constructor(route: ActivatedRoute) {
    route.params.subscribe(param => {
        let id = param['id '];
        // Do your stuff with id change.
    });
  }
Madhu Ranjan
  • 17,334
  • 7
  • 60
  • 69
  • why not just use the `ngOnInit` hook instead of the constructor? – dopatraman Aug 08 '16 at 19:10
  • @dopatraman, ngOnInit will not be fired if you are just updating the params by firing reloadWithNewId because the Component is already initialized. Here is the [Plunker](https://plnkr.co/edit/nnpRhYK9thLOth5uxbxD?p=preview), Hope this helps!! – Madhu Ranjan Aug 08 '16 at 20:09
  • I faced the issue too, and implementing the route.params.subscribe within the ngOnInit is actually working :) – KCarnaille Feb 15 '17 at 16:19
1

I think the changeDetectorRef is the proper tool for this job:

HTML

<my-component *ngIf="!reloading"
              (loadNext)="onLoadNext()">
</my-component>

TS

  constructor(
    private cdr: ChangeDetectorRef
  ) { }

  onLoadNext() {
      this.reloading = true;
      this.cdr.detectChanges();
      this.reloading = false;
      this.cdr.detectChanges();
  }

Why would you want this, instead of "updating the model and the view updates itself", as in the answer that Günther provided? This way you're sure the component gets reinitialized and there aren't some left over values from before. That's why I find it cleaner to do it like this.

Example: https://stackblitz.com/edit/commatrainer-v1

bersling
  • 17,851
  • 9
  • 60
  • 74