thebiglebowski11
thebiglebowski11

Reputation: 1461

angular directive clear field

I am trying to implement an angular directive in my ionic app to add an 'X' button to a text field that when clicked clears the text. The code is based on this repo: https://github.com/amageed/angular-resetfield

I am unable to get the reset anonymous function called... I.e. the resetting log statement does not show, but the linking statement does:

.directive('resetField', ['$compile', '$timeout', function($compile, $timeout) {
    return {
        require: 'ngModel',
        scope: {},
        link: function(scope, el, attrs, ctrl) {

            console.log('linking ');

            // limit to input element of specific types
            var inputTypes = /text|search|tel|url|email|password/i;
            if (el[0].nodeName === "INPUT") {
                if (!inputTypes.test(attrs.type)) {
                    throw new Error("Invalid input type for resetField: " + attrs.type);
                }
            } else if (el[0].nodeName !== "TEXTAREA") {
                throw new Error("resetField is limited to input and textarea elements");
            }

            // compiled reset icon template
            var template = $compile('<i ng-show="enabled" ng-click="reset();" class="icon ion-close reset-field-icon"></i>')(scope);
            el.addClass("reset-field");
            el.after(template);

            scope.reset = function() {
                console.log('resetting');
                ctrl.$setViewValue(null);
                ctrl.$render();
                $timeout(function() {
                    el[0].focus();
                }, 0, false);
                scope.enabled = false;
            };

            el.bind('input', function() {
                scope.enabled = !ctrl.$isEmpty(el.val());
            })
            .bind('focus', function() {
                $timeout(function() {
                    scope.enabled = !ctrl.$isEmpty(el.val());
                    scope.$apply();
                }, 0, false);
            })
            .bind('blur', function() {
                $timeout(function() {
                    scope.enabled = false;
                    scope.$apply();
                }, 0, false);
            });
        }
    };
}]);

Here is the usage in HTML:

<input type="text" placeholder="Search" ng-model="query" autocorrect="off" reset-field>

Upvotes: 4

Views: 3361

Answers (2)

Manube
Manube

Reputation: 5242

The problem arises because the blur event (always) fires before the click event.

When the blur event fires, $scope.enabled becomes false, so the button immediately disappears, before the click event can fire.

What you need is an event that (always) fires before blur, namely ng-mousedown


All you need to do is change:

ng-click="reset();">

to

ng-mousedown="reset();">

and it works.


As the events now always fire in the correct order, there is nothing else to change in your code.

The X icon could be anywhere on the page, and it would still work.

If you wish to place it over the input box, just add:

style="position:relative;right:15px;top:1px;cursor: pointer;cursor: hand;"

to the <i> tag.


Please find a working plunker of your angular directive clear field code.


To learn about the difference between the click and mousedown events, please read this page.

And to test the order of DOM events, please check this pen.

----------

UPDATE: How to handle the case of a Cordova environment

I (belatedly) realised the Cordova environment did not handle events the same way the browser does.

This post gives an equivalence table of the mousedown, mouseup and mousemove events.

As touchstart is equivalent to mousedown, ng-mousedown="reset();"> should be replaced in this solution by ng-touchstart="reset();">;

ng-touchstart does not exist out-of-the-box, but according to this github repo, you could create the directive like so:

.directive("ngTouchstart", function () {
  return {
    controller: function ($scope, $element, $attrs) {
      $element.bind('touchstart', onTouchStart);

      function onTouchStart(event) {
        var method = '$scope.' + $element.attr('ng-touchstart');
        $scope.$apply(function () {
          eval(method);
        });
      };
    }
  };
});

As of yet, I have not come round to testing this solution in a Cordova environment, but I will...

----------

ADDITIONAL INFORMATION: after I posted the answer above, you made the following comments:

"this did not work. could it have something to do with the fact that I am using the ng-model in a query? Also, I tried putting the reset code into bind, the clear does work, but the filter isn't reapplied."

and

"yes, it works in the plunker but not in the ios sim."


ANSWER TO YOUR COMMENTS

My answer is based solely upon the directive code and the HTML code you provided.

Your question was: "I am unable to get the reset anonymous function called... I.e. the resetting log statement does not show"

I took the best of care to answer your question as precisely as possible, explain the issue and provide a working plunker to prove the point: the reset function cannot be called because the focus event takes precedence; when you replace ng-click by ng-mousedown it works, as thoroughly detailed by provided sources and shown in the plunker I provided.

The question in your original post has then been answered, the issue was explained, and a working solution provided.


You mention ios. I just tested my plunker in an iPad: it works perfectly. If there is a specific device with which the solution I provided is not compatible, I will be happy to try and find a workaround.

Now you seem to have additional, unrelated issues, that could not be foreseen by the code provided in the original post.

If it is the case, please ask another question; you could copy/paste the code in my plunker, provide a new plunker and as many details as possible regarding your current issues.

Again, I will be happy to try to answer to the best of my abilities...

Upvotes: 2

nilsi
nilsi

Reputation: 10761

I got your code working, check this JS Bin

I changed:

class="icon ion-close reset-field-icon"

to font awesomes:

class="fa fa-times-circle"

And it worked.

There is some other code that you left out in your question as well. Take a look and find out what you missed.

Upvotes: 0

Related Questions