Eric Gonzalo Eric Gonzalo - 7 days ago 4
TypeScript Question

Using mobile events in Angular2

I was wondering if I could get some help in regards to events for mobile devices. I was looking around for a way to bind functions to swipe events in Angular 2. I saw in this this issue on Github that mentions that Angular 2 uses Hammer.js for mobile event handling.

I'm having some trouble getting the event to work because I get the following error:


EXCEPTION: Hammer.js is not loaded, can not bind swipeleft event


A snippet of my code is below:

import {Component, View, AfterContentInit} from 'angular2/core';
import {HelperService} from "./helper-service";
import {HammerGesturesPluginCommon} from 'angular2/src/platform/dom/events/hammer_common'

@View({
template: `<div [id]="middleCircle" (swipeleft)="doThis()"></div>`
})

export class ColumnDirective implements AfterContentInit {
constructor(private helperService:HelperService) {}
doThis(){
console.log('This thing has been done.');
}
}


If I add in Hammer Gestures to my constructor, I get this error:

constructor(private helperService:HelperService, private hammerGesturesPluginCommon: HammerGesturesPluginCommon) {}



EXCEPTION: No provider for t! (ColumnDirective -> t)


Any help with this issue would be appreciated!

Answer

After a lot of fiddling, I had no success getting Angular2's HammerGesturesPluginCommon to work (so far). Inspired by Bill Mayes answer, I am submitting this as an elaboration of his answer which I was able to get working (tested on an Ipad mini and an Android phone).

Basically, my solution is as follows.

First, manually reference hammer.js in the script tags of your index.html file (I also reference hammer-time to eliminate the 300ms delay):

<script type="text/javascript" src="/lib/hammer/hammer.js"></script>
<script type="text/javascript" src="/lib/hammer/hammer-time.js"></script>
<script type="text/javascript" src="/lib/es6-shim/es6-shim.js"></script>
<script type="text/javascript" src="/lib/systemjs/dist/system-polyfills.js"></script>
<script type="text/javascript" src="/lib/angular2/bundles/angular2-polyfills.min.js"></script>
<script type="text/javascript" src="/lib/systemjs/dist/system.src.js"></script>
<script type="text/javascript" src="/lib/whatever/else.js"></script>

Second, install the Type Definitions for hammerjs (tsd install hammerjs -save). Then you can can create an Angular2 attibute directive like this:

/// <reference path="./../../../typings/hammerjs/hammerjs.d.ts" />
import {Directive, ElementRef, AfterViewInit, Output, EventEmitter} from 'angular2/core';
@Directive({
    selector: '[hammer-gestures]'
})
export class HammerGesturesDirective implements AfterViewInit {
    @Output() onGesture = new EventEmitter();
    static hammerInitialized = false;
    constructor(private el: ElementRef) {

    }
    ngAfterViewInit() {

        if (!HammerGesturesDirective.hammerInitialized) {

            let hammertime = new Hammer(this.el.nativeElement);
            hammertime.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
            hammertime.on("swipeup", (ev) => {
                this.onGesture.emit("swipeup");
            });
            hammertime.on("swipedown", (ev) => {
                this.onGesture.emit("swipedown");
            });
            hammertime.on("swipeleft", (ev) => {
                this.onGesture.emit("swipeleft");
            });
            hammertime.on("swiperight", (ev) => {
                this.onGesture.emit("swiperight");
            });
            hammertime.on("tap", (ev) => {
                this.onGesture.emit("tap");
            });

            HammerGesturesDirective.hammerInitialized = true;
        }


    }
}

You need to set Hammer.DIRECTION_ALL if you want to detect vertical (up/down) swipes (left/right swipes are default, not vertical). More options can be found about the hammer api here: http://hammerjs.github.io/api/#hammer

Finally, in your parent component you can do something like this:

import {Component} from "angular2/core";
import {HammerGesturesDirective} from "./path/HammerGesturesDirective";

    @Component({
        selector: "my-ng2-component",
        template: `<div style='width:100px; height: 100px;background-color: red' 
            (onGesture)="doSwipe($event)" hammer-gestures></div>`,
        directives: [HammerGesturesDirective]
        
    })
    export class MyNg2Component {
        constructor() { }

        doSwipe(direction: string) {
            alert(direction);
        }

    }

This way you only need to reference the attribute hammer-gestures when you want to enable hammer gestures on any particular element. Note: the element seems to need a unique id to work.