Updating Variable in Component not updating UI in HammerJS Function

I am making a music player app in Ionic V3 with a player component that slides up from any parent view that includes the component in the template. I am passing the time position/song title from the parent as an @Input and that is working fine.

The problem is that I have a “timeline” which includes a position bar and a pin that represents the current position. I am trying to update the time of the song on the UI when I drag the pin in the player, but the time won’t update on the UI from within a HammerJS function (this is how I am handling the drag event to move the pin).

I added a sample button in the child component to see if updating the UI from outside of the HammerJS function would work, and that updates fine (changes the time string to ‘Hello inside updatePos function`’). I’ve also tried utilizing NgZone in the HammerJS function and that didn’t work.

Parent Page Component Reference

<page-player [activeSongName]="activeSongName" [(trackPlayerPosition)]="trackPosition"  (playPause)="togglePlaybackParent($event)"></page-player>

Child Component (Music Player)

<div ion-fixed id="myElement">

    <!-- Dummy button to test updating position outside of HammerJS -->
    <button (click)="updatePos()">Update Position</button>

    <div class="musicControls">
      <ion-icon size="medium" name="shuffle-outline"></ion-icon>
      <ion-icon size="medium" name="skip-backward-outline"></ion-icon>
      <ion-icon size="medium" *ngIf="isPlayingTrack" name="play-outline" (click)="togglePlayback(!isPlayingTrack)"></ion-icon>
      <ion-icon size="medium" *ngIf="!isPlayingTrack" name="pause-outline" (click)="togglePlayback(!isPlayingTrack)"></ion-icon>
      <ion-icon size="medium" name="skip-forward-outline"></ion-icon>
      <ion-icon size="medium" name="repeat-outline"></ion-icon>
    </div>

    <div class="holder">
      <div class="audio green-audio-player">
        <div class="controls">
          <p class="current-time">{{ trackPlayerPosition }}</p>
          <div class="slider" id="slider">
            <div class="progress">
              <div class="pin" id="pin"></div>
            </div>
          </div>
          <span class="total-time">{{ trackLength }}</span>
        </div>
      </div>

    </div>

  </div>

Child Component (Music Player) TypeScript

import { Component, EventEmitter, Input, Output, NgZone } from '@angular/core';
import { IonicPage, NavController, NavParams, Platform } from 'ionic-angular';
import Hammer from 'hammerjs';
import * as $ from 'jquery'

/**
 * Generated class for the PlayerPage page.
 *
 * See https://ionicframework.com/docs/components/#navigation for more info on
 * Ionic pages and navigation.
 */

@IonicPage()
@Component({
  selector: 'page-player',
  templateUrl: 'player.html',
})

export class PlayerPage {

  _trackPlayerPosition: string

  @Input()
  set trackPlayerPosition(position: string) {
    this._trackPlayerPosition = (position);
  }

  @Output() postitionEmitter = new EventEmitter<string>();

  get trackPlayerPosition(): string { return this._trackPlayerPosition; }

  height: any
  lastPosX = 0;
  isPinDragging: boolean = false
  sliderLeftBound: number
  sliderRightBound: number

  trackPosition: string = "0:00"


  trackLength: string = "3:21"

  trackLengthNum: number = 3.35

  constructor(public zone: NgZone, public platform: Platform) {
    this.platform.ready().then(() => {
      this.height = this.platform.height()
    })
  }

  ionViewDidLoad() {
    var pin = document.getElementById('pin');
    var slider = document.getElementById('slider');

    this.sliderLeftBound = slider.offsetLeft;
    this.sliderRightBound = slider.offsetWidth + slider.offsetLeft;

    if (pin != null) {
      var hammerPin = new Hammer(pin);

      hammerPin.add( new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL, threshold: 0 }))
      hammerPin.on("pan", this.pinDrag.bind(this), this.pinDrag)
    } else {
      console.log("Error getting pin")
    }
  }

  emitPosition(position: string) {
    console.log("Emitting position: " + position)
    this.postitionEmitter.emit(position)
  }

  updatePos() {
    // This correctlty updates the time position string in UI to "Hello"
    this.trackPlayerPosition = "Hello"
  }

  timeToString(time) {
    var minutes = Math.floor(time/1)
    var seconds = Math.floor((time%1)*60)

    // I have tried both inside and outside the zone
    this.zone.run(() => {
      console.log("Inside Zone")
      this.trackPosition = minutes + ":" + seconds
    })

    // failed attempt at emitting the position
    // this.postitionEmitter.emit(this.newTrackPosition);

    // Outputs the correct track position
    console.log("New Track Time: " + this.trackPosition)

  }

  pinDrag(ev) {

    var elem = ev.target;

    var sliderPosition = ev.center.x

    var relativePosition = sliderPosition / this.sliderRightBound

    // Outputs the correct position in the logs
    console.log("relativePosition: " + relativePosition * this.trackLengthNum)

    if (ev.center.x >= this.sliderLeftBound && ev.center.x <= this.sliderRightBound) {
      if ( ! this.isPinDragging ) {
        this.isPinDragging = true;
        this.lastPosX = elem.offsetLeft;
      }

      var posX = ev.deltaX + this.lastPosX;
      elem.style.left = posX + "px";

      if (ev.isFinal) {
        this.isPinDragging = false

        this.timeToString(relativePosition * this.trackLengthNum)
        // Outputs the correct position in the logs
        console.log("End drag pin: " + this.trackPosition)

        // Does NOT update the trackPosition on the UI
        this.trackPlayerPosition = this.trackPosition
      }
    }
  }

}