0

I'm trying to clear the input field of a filter when a user presses the esc key. As soon as I enter the mark up for the directive, the filter stops working completely.

My mark up is as follows:

<div ng-app='App'>
  <div ng-controller="MyCtrl">
    <input type="text" ng-model="itemSearch" clear-input clear="clearFilter()" />
    <button ng-click="clearFilter()">Clear</button>
    <ul>
      <li ng-repeat="item in items|filter:itemSearch"> <span>{{item.value}}</span>
      </li>
    </ul>
  </div>
</div>

And the JavaScript is as follows:

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

function MyCtrl($scope) {
  $scope.items = [
    {value: 'one two three'}, 
    {value: 'four five six'}
  ];
  $scope.clearFilter = function () {
    $scope.itemSearch = '';
  };
}

app.directive('clearInput', function () {
  function isEscapeKey(keyCode) {
    if (keyCode === 27) {
      return true;
    }
      return false;
    }
    return {
        restrict: 'A',
       scope: {
        clear: '&'
    },
    link: function (scope, element) {
      element.keyup(function (event) {
        if (isEscapeKey(event.keyCode)) {
          scope.clear();
        }
      });
    }
  };
});

I've got the sample code here: http://jsfiddle.net/darrenthomas/cbcpq/1/

I would like to point out that I'm not an experienced JavaScript programmer and that I'm new to AngularJS. I've also looked at How do I pass multiple attributes into an Angular.js attribute directive? but am unable to get working solution.

Community
  • 1
  • 1
Darren
  • 67
  • 1
  • 5

5 Answers5

1

Remove :

scope: {
        clear: '&'
    },

in directive.We don't need isolate scope here.

Like Webnet says use:

element.bind('keyup', function (event) {
    if (isEscapeKey(event.keyCode)) {
      scope.clear();
    }
  });

After, add $apply to your directive:

app.directive('clearInput', function () {
    function isEscapeKey(keyCode) {
        if (keyCode === 27) {
            return true;
        }
        return false;
    }
    return {
        restrict: 'A',
        link: function (scope, element) {
            element.bind('keyup', function (event) {
                if (isEscapeKey(event.keyCode)) {                                  
                     scope.$apply(function(){scope.clearFilter();});
                }
            });
        },
    };
});

Working Demo Fiddle

Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • Thanks. To make sure I understand, I needed to specify the 'require' attribute to get this to work? I'm confused as to why I need this? The require also has the '^' prefix: From the docs: "The ^ prefix means that this directive searches for the controller on its parents (without the ^ prefix, the directive would look for the controller on just its own element)." Can you please explain what this is doing? or point me at an explanation? – Darren Oct 31 '13 at 08:37
  • `^ – Will look for the directive on parent elements, if not available on the same element.` As you know `ng-model` is other directive. You are right, I implemented with `require` but after changed my answer. You can remove it. Mostly you need it if you want to use model in compiled template, for example – Maxim Shoustin Oct 31 '13 at 08:45
1

If you only want to clear the field, you do not need to isolate the scope in the custom directive you wrote. You can simply use the $eval() function of scope to indirectly execute the clearFilter() function

app.directive('clearInput', function () {
function isEscapeKey(keyCode) {
    if (keyCode === 27) {
        return true;
    }
    return false;
}
return {
    link: function (scope, element, attrs) {
        element.bind('keyup',function (event) {
            if (isEscapeKey(event.keyCode)) {
                scope.$eval(attrs.clear);
                scope.$apply();
            }
        });
    },
  };
});

Here is an updated Fiddle

kubuntu
  • 2,525
  • 1
  • 22
  • 24
0

In your example, the browser's console (accessible via Ctrl + Shift + I in Chrome) shows the error TypeError: Object [object Object] has no method 'keyup'

Your code should be...

  element.bind('keyup', function (event) {
    if (isEscapeKey(event.keyCode)) {
      scope.clear();
    }
  });

But it doesn't look like clear() is defined in your controller either.

Ben
  • 60,438
  • 111
  • 314
  • 488
0

Updated the Fiddle. http://jsfiddle.net/cbcpq/10/ . Hope this helps

app.directive('clearInput', function () {
function isEscapeKey(keyCode) {
    if (keyCode === 27) {
        return true;
    }
    return false;
}
return {
    restrict: 'A',
    scope: {
        clear: '&'
    },
    link: function (scope, element) {
        $(element).keyup(function (event) {
            if (isEscapeKey(event.keyCode)) {
                scope.itemSearch = "";
                scope.$apply();
            }
        });
    },
};

});

satish
  • 2,425
  • 3
  • 23
  • 40
0

You probably have multiple answers to choose from. But if you want to make this clear-input directive completely reusable then I believe you will have to use $parse. Mostly because you cannot use an isolate scope with ng-model there. Atleast not without totally understanding what is happening.

Here is the code of the directive :

app.directive('clearInput', function ($parse) {
    function isEscapeKey(keyCode) {
        return keyCode === 27;
    }
    return {
        require : "^ngModel",
        link: function (scope, element,attrs,ctrl) {
            if(!ctrl){
                return;
            }
            var ngModelSetter = $parse(attrs.ngModel).assign;
            element.bind("keyup",function (event) {
                if (isEscapeKey(event.which)) {
                    ngModelSetter(scope,"");
                    scope.$apply();
                }
            });
        }
    };
});

A plunker demonstrating its use :

http://plnkr.co/edit/z0Td2tR3JiVPFsbgauhj?p=preview

ganaraj
  • 26,841
  • 6
  • 63
  • 59