Nate May Nate May - 29 days ago 16
TypeScript Question

Issue with setInterval() not accessing directive function in Angular2

I'm trying to make a news ticker directive that slides the element across its parent so that you can read the string if it's too long. I'm stuck because I can't get the

setInterval()
function to call the
moveLeft()
function in its current scope, and thereby move the target element (
this.node1
) over by
1px
. If I pass the element, function, and parameters into the
setInterval()
then the the DOM isn't adjusted, because I think the element is out of scope. How can I call the
moveLeft()
function in its current scope from a
setInterval()
function?
The function works fine when called outside of the
setInterval()
function.

import { Directive, ElementRef, HostListener, Input, Renderer, OnInit } from '@angular/core';

@Directive({ selector: '[ticker]' })
export class TickerDirective implements OnInit {

currMargin: number; // i.e.: 4
newMargin: string; // i.e.: '4px'
currInterval: any; // timeout function variable used to kill the timeout

container: any; // container element
node1: any;
node2: any;
tickerNodes: any[];

textValue: string;
@Input('text') text: string; // display string passed in on an attribute

constructor(private el: ElementRef, private renderer: Renderer) { }

@HostListener('mouseenter') onMouseEnter() {
// this is where the script is failing
this.currInterval = setInterval( this.moveLeft(), 100);
}

@HostListener('mouseleave') onMouseLeave() {
clearInterval(this.currInterval);
this.currMargin = 0;
}

moveLeft() { // slide the elements to the left
this.currMargin -= 1;
this.newMargin = this.currMargin + 'px';
this.renderer.setElementStyle(this.node1, 'margin-left', this.newMargin);
}

ngOnInit() { // instantiate the elements
this.currMargin = 0;
this.textValue = this.el.nativeElement.attributes[2].nodeValue; // sets the text value passed from the component to the directive through an attribute

// build the container
this.container = this.renderer.createElement(this.el.nativeElement, 'div');
this.renderer.setElementClass(this.container, 'ticker-container', true);

// create the left most element
this.node1 = this.renderer.createElement(this.container, 'div');
this.renderer.setElementClass(this.node1, 'ticker-node', true);
this.renderer.setText(this.node1, this.el.nativeElement.attributes[2].nodeValue);

// create the right most element
this.node2 = this.renderer.createElement(this.container, 'div');
this.renderer.setElementClass(this.node2, 'ticker-node', true);
this.renderer.setText(this.node2, this.el.nativeElement.attributes[2].nodeValue);

// render the elementss
this.tickerNodes = [this.node1, this.node2];
this.renderer.attachViewAfter(this.container, this.tickerNodes);
}
}

Answer

Use setInterval() inside the method you call

moveLeft() { // slide the elements to the left
    setInterval(() => {
            this.currMargin -= 1;
            this.newMargin = this.currMargin + 'px';
            this.renderer.setElementStyle(this.node1, 'margin-left', this.newMargin);
    }, 100);    
}