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);
}
}
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)
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With