7

My AngularJS app needs to be able to detect both the start and stop of a touch event (without swiping). For example, I need to execute some logic when the touch begins (user presses down their finger and holds), and then execute different logic when the same touch ends (user removes their finger). I am looking at implementing ngTouch for this task, but the documentation for the ngTouch.ngClick directive only mentions firing the event on tap. The ngTouch.$swipe service can detect start and stop of the touch event, but only if the user actually swiped (moved their finger horizontally or vertically) while touching. Anyone have any ideas? Will I need to just write my own directive?

gregtzar
  • 2,458
  • 3
  • 26
  • 31

3 Answers3

13

Update 11/25/14:

The monospaced angular-hammer library is outdated right now, so the Hammer.js team recommend to use the ryan mullins version, which is built over hammer v2.0+.


I dug into ngTouch and from what I can tell it has no support for anything other than tap and swipe (as of the time of this writing, version 1.2.0). I opted to go with a more mature multi-touch library (hammer.js) and a well tested and maintained angular module (angular-hammer) which exposes all of hammer.js's multi-touch features as attribute directives.

https://github.com/monospaced/angular-hammer

gregtzar
  • 2,458
  • 3
  • 26
  • 31
  • 7
    @PhươngNguyễn Because the repo it is forked from is older, no longer actively maintained, contains no version tags, and at the time I posted this did not support bower either. I dug into the code of all the different versions and the monospaced version is the best. – gregtzar Jun 16 '14 at 18:17
  • 5
    Also not sure why so many people are downvoting this answer. It would be great if some of them would post reasons why so that it could be improved. I've been using this solution in multiple enterprise/production angular projects now for nearly a year and it has worked pretty well, and as of now ngTouch has still got some serious problems and lacks support for different gestures. If you know of any better options, please do tell. – gregtzar Jun 16 '14 at 18:22
  • It's me who downvoted the answer. And I already commented the reason. Though I agree that for the sake of suggesting a library that handle touch event, your answer is good. I'd always credit the original owner of a repo unless there is valid reason not to do so. – Phương Nguyễn Jun 17 '14 at 09:57
  • 7
    @PhươngNguyễn It's not a matter of credit (Interested parties can see who developed the original library just fine via github) but it's about providing a current/valid library to use. The original is dated and I wouldn't use it in production, so why would I point people to it here? Welcome to the world of open source software. FYI: Several other people have downvoted the answer (cancelling out the upvotes) without comment. – gregtzar Jun 17 '14 at 18:45
  • 2
    @Egg "FYI: Several other people have downvoted the answer (cancelling out the upvotes) without comment.". Welcome to stackoverflow :) – Umair Nov 05 '14 at 10:29
4

It is a good implementation:

// pressableElement: pressable-element
.directive('pressableElement', function ($timeout) {
    return {
        restrict: 'A',
        link: function ($scope, $elm, $attrs) {
            $elm.bind('mousedown', function (evt) {
                $scope.longPress = true;
                $scope.click = true;

                // onLongPress: on-long-press
                $timeout(function () {
                    $scope.click = false;
                    if ($scope.longPress && $attrs.onLongPress) {
                        $scope.$apply(function () {
                            $scope.$eval($attrs.onLongPress, { $event: evt });
                        });
                    }
                }, $attrs.timeOut || 600); // timeOut: time-out

                // onTouch: on-touch
                if ($attrs.onTouch) {
                    $scope.$apply(function () {
                        $scope.$eval($attrs.onTouch, { $event: evt });
                    });
                }
            });

            $elm.bind('mouseup', function (evt) {
                $scope.longPress = false;

                // onTouchEnd: on-touch-end
                if ($attrs.onTouchEnd) {
                    $scope.$apply(function () {
                        $scope.$eval($attrs.onTouchEnd, { $event: evt });
                    });
                }

                // onClick: on-click
                if ($scope.click && $attrs.onClick) {
                    $scope.$apply(function () {
                        $scope.$eval($attrs.onClick, { $event: evt });
                    });
                }
            });
        }
    };
})

Usage example:

<div pressable-element
    ng-repeat="item in list"
    on-long-press="itemOnLongPress(item.id)"
    on-touch="itemOnTouch(item.id)"
    on-touch-end="itemOnTouchEnd(item.id)"
    on-click="itemOnClick(item.id)"
    time-out="600"
    >{{item}}</div>

var app = angular.module('pressableTest', [])

.controller('MyCtrl', function($scope) {
    $scope.result = '-';

    $scope.list = [
        { id: 1 },
        { id: 2 },
        { id: 3 },
        { id: 4 },
        { id: 5 },
        { id: 6 },
        { id: 7 }
    ];

    $scope.itemOnLongPress = function (id) { $scope.result = 'itemOnLongPress: ' + id; };
    $scope.itemOnTouch = function (id) { $scope.result = 'itemOnTouch: ' + id; };
    $scope.itemOnTouchEnd = function (id) { $scope.result = 'itemOnTouchEnd: ' + id; };
    $scope.itemOnClick = function (id) { $scope.result = 'itemOnClick: ' + id; };
})

.directive('pressableElement', function ($timeout) {
    return {
        restrict: 'A',
        link: function ($scope, $elm, $attrs) {
            $elm.bind('mousedown', function (evt) {
                $scope.longPress = true;
                $scope.click = true;
                $scope._pressed = null;

                // onLongPress: on-long-press
                $scope._pressed = $timeout(function () {
                    $scope.click = false;
                    if ($scope.longPress && $attrs.onLongPress) {
                        $scope.$apply(function () {
                            $scope.$eval($attrs.onLongPress, { $event: evt });
                        });
                    }
                }, $attrs.timeOut || 600); // timeOut: time-out

                // onTouch: on-touch
                if ($attrs.onTouch) {
                    $scope.$apply(function () {
                        $scope.$eval($attrs.onTouch, { $event: evt });
                    });
                }
            });

            $elm.bind('mouseup', function (evt) {
                $scope.longPress = false;
                $timeout.cancel($scope._pressed);

                // onTouchEnd: on-touch-end
                if ($attrs.onTouchEnd) {
                    $scope.$apply(function () {
                        $scope.$eval($attrs.onTouchEnd, { $event: evt });
                    });
                }

                // onClick: on-click
                if ($scope.click && $attrs.onClick) {
                    $scope.$apply(function () {
                        $scope.$eval($attrs.onClick, { $event: evt });
                    });
                }
            });
        }
    };
})
li {
  cursor: pointer;
  margin: 0 0 5px 0;
  background: #FFAAAA;
}
<div ng-app="pressableTest">
    <div ng-controller="MyCtrl">
        <ul>
            <li ng-repeat="item in list"
                pressable-element
                on-long-press="itemOnLongPress(item.id)"
                on-touch="itemOnTouch(item.id)"
                on-touch-end="itemOnTouchEnd(item.id)"
                on-click="itemOnClick(item.id)"
                time-out="600"
                >{{item.id}}</li>
        </ul>
      <h3>{{result}}</h3>
    </div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

Based on: https://gist.github.com/BobNisco/9885852

Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
3

The monospaced angular-hammer library is outdated right now, so the Hammer.js team recommend to use the ryan mullins version, which is built over hammer v2.0+.

Sergio A.
  • 101
  • 3
  • 1
    That's some good intel, thanks! I went ahead and added it to answer to make sure people don't miss it. – gregtzar Nov 25 '14 at 19:00