Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
    }
}
like image 552
Nate May Avatar asked Apr 14 '26 08:04

Nate May


2 Answers

First, in your original, you are executing moveleft during your interval setup, instead of providing the function. So it will get executed when you set it up, but never again.

ie. setInterval( this.moveLeft, 100) instead of setInterval( this.moveLeft(), 100)

But the real issue is to provide an arrow function to the setInterval, instead of the function directly, as simply giving it the function will change the context of this (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)

eg. setInterval( () => { this.moveLeft(); }, 100)

like image 72
Fiddles Avatar answered Apr 17 '26 01:04

Fiddles


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);    
}
like image 29
Roman C Avatar answered Apr 17 '26 01:04

Roman C



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!