lomboboo
lomboboo

Reputation: 1233

Angular 2 route param changes, but component doesn't reload

So, basically I need to reload my component after id of url parameter was changed. This is my player.component.ts:

import {Component, OnInit, AfterViewInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
declare var jQuery: any;

@Component({
  selector: 'video-player',
  templateUrl: './player.component.html',
  styleUrls: ['./player.component.less']
})

export class VideoPlayerComponent implements OnInit, AfterViewInit {
  playerTop: number;
  currentVideoId: number;

  constructor(
    private _route: ActivatedRoute,
    private _router: Router
  ) {

  }

  ngOnInit(): void {
    this._route.params.subscribe(params => {
      this.currentVideoId = +params['id'];
      console.log( this.currentVideoId );
      this._router.navigate(['/video', this.currentVideoId]);
    });
  }

  ngAfterViewInit(): void {
    if (this.videoPageParams(this.currentVideoId)) {
      console.log( "afterViewInit" );
      let params = this.videoPageParams(this.currentVideoId);
      let fakeVideoItemsCount = Math.floor(params.containerWidth / params.videoItemWidth);
      this.insertFakeVideoItems( this.currentVideoId, fakeVideoItemsCount);
      this.changePlayerPosition( params.videoItemTop );
    }

  }

  videoPageParams( id ): any {
    let videoItemTop = jQuery(`.videoItem[data-id="${id}"]`).position().top;
    let videoItemWidth = jQuery('.videoItem').width();
    let containerWidth = jQuery('.listWrapper').width();
    return {
      videoItemTop,
      videoItemWidth,
      containerWidth
    };
  }

  changePlayerPosition( videoItemTop ): void {
    this.playerTop = videoItemTop;
  }

  insertFakeVideoItems( id, fakeVideoItemsCount ): void {
    let fakeVideoItemHTML = `<div class="videoItem fake"></div>`;
    let html5playerHeight = jQuery('#html5player').height();
    let videoItemIndex = jQuery(`.videoItem[data-id="${id}"]`).index() + 1;
    let videoItemInsertAfterIndex;
    let videoItemRow = Math.ceil(videoItemIndex / fakeVideoItemsCount);
    let videoItemRowBefore = videoItemRow - 1;
    if ( videoItemIndex <= 4 ) {
      videoItemInsertAfterIndex = 0;
    } else {
      videoItemInsertAfterIndex = (videoItemRowBefore * fakeVideoItemsCount);
    }
    let videoItemInsertAfter = jQuery('.videoItem').eq(videoItemInsertAfterIndex);

    for ( let i = 0; i < fakeVideoItemsCount; i++ ) {
      $(fakeVideoItemHTML).insertBefore(videoItemInsertAfter);
    }
    jQuery(`.videoItem.fake`).css('height', html5playerHeight);

  }

}

player.component.html:

<video
    class="video"
    preload="auto"
    [attr.data-id]="currentVideoId"
    src="">
</video>

<videos-list></videos-list>

videoList.component.html

<div class="videoItem" *ngFor="let video of videos" [attr.data-id]="video.id">
    <a [routerLink]="['/video', video.id]">
        <img [src]='video.thumbnail' alt="1">
    </a>
</div>

So when I click <a [routerLink]="['/video', video.id]"> in videoList.component.html it changes route to /video/10 for example, but the part from player.component.ts which manipulates the DOM doesn't fire again - DOM manipulation doesn't update.

I tried to manually navigate to route via this._router.navigate(['/video', this.currentVideoId]); but somehow it doesn't work.

QUESTION Is there any way to run functions that manipulate DOM each time route param changes in the same URL?

Upvotes: 3

Views: 1862

Answers (1)

AVJT82
AVJT82

Reputation: 73387

DOM will not update because ngOnInit is only fired once, so it will not update even if you try to "renavigate" back to the parent from the child, since the parent haven't been removed from the DOM at any point.

One option to solve this, is that you could use a Subject, that when the routing is happening, let's send the chosen video id to parent, which subscribes to the change and does whatever you tell it to do, meaning calling functions that will update the DOM, so probably what you want re-executed is the inside ngOnInit and ngAfterViewInit

You mentioned that you had tried using

this._router.navigate(['/video', this.currentVideoId])

so let's look at that. Probably have some click event that fires a function. Let's say it looks like the following, we'll just add the subject in the play

navigate(id) {
  VideoPlayerComponent.doUpdate.next(id)
  this._router.navigate(['/video', this.currentVideoId])
}

Let's declare the Subject in your parent, and subscribe to the changes:

public static doUpdate: Subject<any> = new Subject();

and in the constructor let's subscribe to the changes...

constructor(...) {
    VideoPlayerComponent.doUpdate.subscribe(res => {
        console.log(res) // you have your id here
        // re-fire whatever functions you need to update the DOM
    });
}

Upvotes: 4

Related Questions