Sovos
Sovos

Reputation: 3390

best way to trigger AngularJS ng-show

I'm building an autocomplete box in AngularJS. The relevant code looks like this

<input type="text" ng-model="tag">
<div ng-show="tag">
  <ul>
    <li ng-repeat="t in user.tags | filter:{name:tag}">
      <a>{{t.name}}</a>
    </li>
  </ul>
</div>

I'd like to know what is the best way to show the list of suggestions when "tag" has no value (i.e. I want to show all the tags when the user press the down key. No need to mention the keypressing code on the answer).

Upvotes: 12

Views: 53922

Answers (2)

sksallaj
sksallaj

Reputation: 4010

I came across this question while I was setting up an angular project of my own.

When I did the accepted answer, I found the my browser kept on increase in memory. If you created the angular scope method "ShowTags()", this will continuously get polled. You can verify this by setting a breakpoint in this method, it will continuously keep getting hit. If you check the task manager and show the browser running your website, the memory keeps going up and won't stop.

In my opinion, scope functions should only be used when using event triggers: click event, change event, keypressed are some of the examples.

showing or hiding aren't events, so this is why it gets polled like that.

To fix and provide the same functionality, turn this into a scope variable.

change the html tag from:

<div ng-show="ShowTags()">

to

<div ng-show="ShowTags">

And in your controller:

$scope.KeyPressed = false;
$scope.Tags = '';

then create a watch event on what you want to watch for:

//initialize showtag when page loads
$scope.ShowTags = $scope.KeyPressed && $scope.Tags !== '';

//watch for any new changes on keypressed
$scope.$watch('KeyPressed', function (newValue, oldValue) {
     if (newValue && $scope.Tags !== '') {
         $scope.ShowTags = true;
     } else {
         $scope.ShowTags = false;
     }
}

//watch for any new changes on keypressed
$scope.$watch('Tags', function (newValue, oldValue) {
     if (newValue !== "" && $scope.KeyPressed) {
         $scope.ShowTags = true;
     } else {
         $scope.ShowTags = false;
     }
}

Or you can change to a "watchCollection" instead of having multiple watches like:

$watchCollection('[KeyPressed, Tags]', function (newValue) { }

but with this, newValue will be an array, and you'd have to access the specific indexes to get the newValues of whatever variable is being watched on.

like.. newValue[0] is the value of KeyPressed, and newValue[1] is the value of Tags

Or to go along with the accepted answer and minimize the amount of watches:

$scope.TruthyVal= function () {
    return $scope.KeyPressed && $scope.Tags !== '';
};

$scope.$watch('TruthyVal', function (newValue, oldValue) {
     if (newValue) {
         $scope.ShowTags = true;
     } else {
         $scope.ShowTags = false;
     }
}

Which looks at the values of KeyPressed and Tags, and changes the value of TruthyVal. If TruthyVal is changed, then it goes into the watched logic.

Upvotes: 15

tocallaghan
tocallaghan

Reputation: 9522

ng-show works with any expression that results in a bool, so all you need to do is replace "tag" with "tag === ''", or some equivalent if your tag is going to be undefined or null.

If you only want to show when a certain key is pressed I would create another variable which you set to true when the down key is pressed and check for that also, e.g.

$scope.KeyPressed = false;
$scope.Tags = '';

$scope.ShowTags = function () {
    return $scope.KeyPressed && $scope.Tags !== '';
};

Then in you div:

<div ng-show="ShowTags()">

See jsfiddle for example

If you need to change any of the variables from within a jquery plugin you may need to use

$scope.$apply()

Upvotes: 31

Related Questions