tl;dr

If you’re like me, you might be thinking “stop typing words, I just want to see the code!”. Well, okay. Here it is.

Background

I was recently working on an app. One one screen, we wanted to create this interaction with some text on the page:

  • Pressing the text would highlight the text
  • Letting go would un-highlight the text
  • Pressing the text for a long time would allow you to see some additional details about the text (in our case, take the user to a different screen)

Some additional contraints we faced:

  • Simply “tapping” the text should not highlight anything (so we couldn’t use the ionic (tap)="action()" directive).
  • The default (press) directive used a 250ms delay, which seemed a little laggy. We wanted to make ours shorter
  • As far as we could find, the default (press) directive didn’t allow for us to easily respond to releasing the press (press-up).

Our desired behavior will look something like this:

Quick Press (highlight on press, unhighlight on release):

Quick Press

Long Press (View Details):

Quick Press

So basically, we needed 2 custom directives:

  • A quick-press, with our own timeout (somewhere between the default tap and press), where we could respond to press and press-up events.
  • A long-press, where we could wait and then show the detail page.

The Code:

There are 4 main things to do:

1. Create the 2 custom directives:


quick-press.directive.ts:
import { Directive, ElementRef, OnInit, OnDestroy, Output, EventEmitter } from '@angular/core'
import { Gesture } from 'ionic-angular/gestures/gesture'
declare var Hammer;

@Directive({
  selector: '[quickPress]'
})

export class QuickPressDirective implements OnInit, OnDestroy {
  el: HTMLElement;
  pressGesture: Gesture;

  @Output('quickPress') onPress: EventEmitter<any> = new EventEmitter();
  @Output('quickPressUp') onPressUp: EventEmitter<any> = new EventEmitter();

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

  ngOnInit() {
    const pressOptions = {
      recognizers: [
        [Hammer.Press, { time: 100 }]
      ]
    };

    this.pressGesture = new Gesture(this.el, pressOptions);
    this.pressGesture.listen();

    this.pressGesture.on('pressup', e => {
      this.onPressUp.emit(e);
    });

    this.pressGesture.on('press', e => {
      this.onPress.emit(e);
    });
  }

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

long-press.directive.ts:

(I’m omitting the code for this, because it is literally the same as above, but substitute “quick” for “long”, and change the press options to time: 1000 or whatever time you like. The exported class is LongPressDirective


2. Add the directives to your app.module.ts file:


app.module.ts:
/*
 import { Everything } from './everywhere'
 ... etc.
*/

// import the directives:
import { LongPressDirective } from  '../directives/long-press.directive'
import { QuickPressDirective } from  '../directives/quick-press.directive'

/*...*/

// declare the directives:
@NgModule({
  declarations: [
    LongPressDirective,
    QuickPressDirective,
    /*...*/
  ],
  /*...*/
})


3. Write callbacks in some component:


my-component.ts:
@Component({
  selector: 'hot-off-the-press',
  templateUrl: 'my-component.html'
})

export class MyComponent {
  /*...*/

  quickPressed() {
    console.log('quick pressed!')
  }

  quickPressUpped() {
    console.log('quick press upped!')
  }

  longPressed() {
    console.log('long press!')
  }
}

And probably do something cooler, but you get the idea.


4. Connect the directives to the callbacks in the template:


my-component.html:
<div (longPress)="longPressed()" (quickPress)="quickPressed()" (quickPressUp)="quickPressUpped()">
<!-- ... -->
</div>

Note: you can also pass whatever arguments to these functions, including $event, which is emitted by the directives. We didn’t need them in our case.

Other Articles/Blogs/Docs

There are a few great articles about creating custom events. Here are a few that helped me the most. They definitely go into more detail on some aspects, so check them out:

Wrap Up

That is all. I hope it was helpful. Here is a gist with slightly more concise code. If you liked it, hated it, have questions or suggestions, let me know in the comments below! (Just kidding, this site doesn’t support comments yet. Email me or “star” the gist though!)

- tyguy