import { Component }          from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'my-app',
  templateUrl: './app.component.html'
})
export class AppComponent {
  myText = 'A String from the Component';
}
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { TickerDirective } from './ticker.directive';
@NgModule({
  imports: [
    BrowserModule,
  ],
  declarations: [
    AppComponent,
    TickerDirective
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

#ghost{
    display: inline-block;
    height: 0;
    position: absolute;
}

.tickerContainer{
    overflow-x: hidden;
    overflow-y: scroll;
    white-space: nowrap;

}
.myStyles{
    background: #eee;   
    color: blue;
    border: 1px solid #ddd;
    max-width: 200px;
    cursor: pointer;
}
<!DOCTYPE html>
<html>
  <head>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <title>Angular Tour of Heroes</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="styles.css">

    <!-- Polyfills for older browsers -->
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>

    <script src="https://unpkg.com/zone.js@0.6.25?main=browser"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.8"></script>
    <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>

    <script src="https://cdn.rawgit.com/angular/angular.io/b3c65a9/public/docs/_examples/_boilerplate/systemjs.config.web.js"></script>
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
    <my-app>Loading...</my-app>
  </body>
</html>
{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es2015", "dom"],
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true,
    "typeRoots": [
      "../../node_modules/@types/"
    ]
  },

  "files": [
    "app/app.module.ts",
    "app/main-aot.ts"
  ],

  "angularCompilerOptions": {
   "genDir": "aot",
   "skipMetadataEmit" : true
 }
}
import { Directive, ElementRef, HostListener, Input, Renderer, OnInit } from '@angular/core';

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

    margin: number; // margin of the text nodes which decrements to tick to the left
    interval: any;  // used to kill the setTimout 
    firstNode: any; // the node which displays first and without mouseover 
    view: any[];    // an array of nodes attached to the main node to provide a seemless scroll
    textWidth: number;
    idle: boolean;

    @Input('speed') speed: number;                  // milliseconds between ticks
    @Input('padding-right') paddingRight: number;
    @Input('size') size: number;
    @Input('trigger') trigger: string;
    @Input('text') text: string;

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

    @HostListener('mouseenter') onMouseEnter(): void  {
        if ( this.trigger === 'onMouseEnter') {
            this.initTicker();
        }
    }

    @HostListener('click') onClick(): void  {
        if ( this.trigger === 'onClick') {
            if ( this.idle ) {
                this.initTicker();
            } else {
                this.reset();
            }
            this.idle = !this.idle;
        }
    }

    initTicker(): void {
        if (this.tickerNeeded()) {
            this.margin = 0;

            this.view = [
                this.createTickerNode( '<T>', this.text ),
                this.createTickerNode( '<T>', this.text )
            ];

            this.r.attachViewAfter( this.firstNode, this.view );
            this.moveLeft();
        }
    }

    @HostListener('mouseleave') onMouseLeave(): void {
        if (this.tickerNeeded() && this.trigger === 'onMouseEnter') {
            this.reset();
        }
    }

    reset(): void {
        clearInterval( this.interval );
        this.r.detachView( this.view );
        this.r.setElementStyle( this.firstNode, 'margin-left', '0' );
    }

    ngOnInit(): void {
        this.setIgnoredAtts();
        this.textWidth = this.getTextWidth();
        this.firstNode = this.createTickerNode( this.firstNode, this.text );
        if ( this.trigger === 'auto' && this.tickerNeeded()) {
            this.initTicker();
        }
    }

    setIgnoredAtts(): void {
        if ( !this.paddingRight ) { this.paddingRight = 16; }
        if ( !this.speed )        { this.speed = 25; }
        if ( !this.trigger )      { this.trigger = 'onMouseEnter'; }
        if ( !this.size )         { this.size = 16; }
        if ( !this.text )         { this.text = 'You need to add the [text] attribute to the "ticker" directive'; }
        this.idle = true;
    }

    createTickerNode( self: any , text: string ): any {
        self = this.r.createElement( this.el.nativeElement, 'span' );
        this.r.setElementStyle( self, 'padding-right', this.paddingRight + 'px');
        this.r.setElementStyle( self, 'font-size', this.size + 'px');
        
        // this.r.setText( self, text ); // not working, oddly
        self.innerHTML = this.text; // quick fix
        return self;
    }

    moveLeft(): void {
        let resetMargin = ( this.textWidth + this.paddingRight ) * -2 ;
        this.interval = setInterval(() => {
            this.r.setElementStyle( this.firstNode, 'margin-left', this.margin-- + 'px' );
            if (this.margin < resetMargin) { this.margin = 0; }
        }, this.speed);
    }

    getTextWidth(): number {
        let t = this.r.createElement( document.getElementById('ghost'), 'div' );
        
        // this.r.setText( t, this.text ); // not working, oddly
        t.innerHTML = this.text; // quick fix
        
        this.r.setElementStyle( t, 'font-size', this.size + 'px');
        let w = t.offsetWidth;
        t.innerHTML = '';
        return w;
    }

    tickerNeeded(): boolean {
        return this.textWidth > this.el.nativeElement.parentElement.offsetWidth - 2;
    }
}
<h1>Angular2 Ticker Directive</h1>

<h3>Starts on Mouse Enter (defaults only)</h3>
<div class="tickerContainer myStyles">
  <div ticker></div>
</div>
<hr>

<h3>Starts on Click</h3>
<div class="tickerContainer myStyles">
  <div ticker [trigger]="'onClick'" [text]="myText" [speed]="15" [padding-right]="0" [size]="40"></div>
</div>
<hr>

<h3>Auto Start</h3>
<div class="tickerContainer myStyles">
  <div ticker [trigger]="'auto'" [text]="'A statically-typed, long string'" [speed]="50" [padding-right]="40" [size]="20"></div>
</div>

<div id="ghost"></div>