Using Gestures Hold, Double Tap, and more in the Ionic 2 Beta

At the time of this writing, Angular 2 and Ionic 2 are both still in beta. This means there could still be bugs, missing features, and lacking documentation. One area I initially got stuck on was gestures in Ionic 2. In Ionic 1 they were quite simple, but in Ionic 2 they didn’t even seem to exist. They do exist, and here’s how to use them.

Ionic 2 makes use of the popular hammerjs library to handle it’s gestures. They’ve also built their own Gesture class that effectively acts as a wrapper to hammerjs: Gesture.ts.  Digging into the Ionic 2 source a bit more I found a good example of how to use the Gesture class in the Slides component.  The slides allow pinch zoom by making use of a Gesture, so I used this as a blueprint for my own simple press directive.

This concept should work for any gesture supported by hammerjs, in this case I wanted to capture a long press, a gesture referred to by hammerjs as simply press.  We’re going to create a simple attribute directive that you can apply to any element.  In our example we simply log out “pressed!!” to the console after a long press by the user.

Here’s the directive:

import {Directive, ElementRef, Input, OnInit, OnDestroy} from 'angular2/core';
import {Gesture} from 'ionic-angular/gestures/gesture';

@Directive({
  selector: '[longPress]'
})
export class PressDirective implements OnInit, OnDestroy {
  el: HTMLElement;
  pressGesture: Gesture;

  constructor(el: ElementRef) {
    this.el = el.nativeElement;
  }

  ngOnInit() {
    this.pressGesture = new Gesture(this.el);
    this.pressGesture.listen();
    this.pressGesture.on('press', e => {
      console.log('pressed!!');
    })
  }

  ngOnDestroy() {
    this.pressGesture.destroy();
  }
}

Let’s see what’s happening here.  This is largely a regular attribute directive created following the official Angular 2 documentation, but notice at the top we also import Gesture from ionic-angular.

Then in ngOnInit, which runs automatically when the app starts as long as your class implements OnInit, we set up our pressGesture.  First we set pressGesture to a new Gesture, and pass the element to Gesture in it’s constructor.  We now tell pressGesture to start listening for events.

Since pressGesture is now listening for events, we can now tell it what to do when any event defined by hammerjs occurs.  In this case, if the user long presses, we log a message to the console.

Usage is as simple as adding the directive to your page:

import {Page, NavController} from 'ionic-angular';
import {OnInit} from 'angular2/core';
import {AlarmService} from './alarm.service';
import {Page3} from "../page3/page3";
import {PressDirective} from './press.gesture.directive';

@Page({
  templateUrl: 'build/pages/page1/page1.html',
  providers: [AlarmService],
  directives: [PressDirective]
})





And then adding the attribute defined as the directive selector to any element:

<ion-navbar *navbar>
  <ion-title>Alarms</ion-title>
</ion-navbar>

<ion-content class="page1" longPress>

 

Edit:  A great question was asked in the comments section, how can you adjust the time you have to press.  Here’s the answer.

Hammerjs has multiple “recognizers”.  One for press, one for tap, etc (see their docs for more info).  You can set options for Hammerjs, and within those options set additional options for each recognizer.  Ionic’s Gesture class also takes options and passes them directly along to Hammerjs.  So to change the time for press you must update the Gesture instantiation with options for the Press gesture.  Here’s what my ngOnInit function looks like now in press.gesture.directive.ts

ngOnInit() {
  this.pressGesture = new Gesture(this.el, {
    recognizers: [
      [Hammer.Press, {time: 6000}]
    ]
  });
  this.pressGesture.listen();
  this.pressGesture.on('press', e => {
    console.log('pressed!!');
  });
}

You can see that I pass in options, and one of those options is recognizers. That option takes an array of arrays, where each child array is a recognizer and another options set.  Here I set the press option’s time property to 6000ms, or 10 seconds.

Detecting a double tap is equally simple, here is ngOnInit for double tap detection:

ngOnInit() {
  this.pressGesture = new Gesture(this.el, {
    recognizers: [
      [Hammer.Tap, {taps: 2}]
    ]
  });
  this.pressGesture.listen();
  this.pressGesture.on('tap', e => {
    console.log('pressed!!');
  });
}

Have any ideas how to make this better?  Questions? Let me know in the comments below.