1

I have a table that lists customers and for customers that also have a client list allows the user to click the table row to show that list. The HTML looks like this:

        <tbody ng-repeat ="item in customers | clientSettingsFilter:customerId">
            <tr ng-class="{'row-hover': item.clientSettings.length>0}" ng-click="item.showClients=!item.showClients">
                <td><span ng-class="{true:'glyphicon glyphicon-chevron-down', false:'glyphicon glyphicon-chevron-right'}[item.showClients]"  ng-hide="item.clientSettings.length==0"></span></td>
                <td>{{item.id}}</td>
                <td>{{item.name}}</td>
                <td><button type="button" class="btn btn-default btn-xs" ng-click="go('/client-config/defaults/'+item.id)">Defaults</button></td>
            </tr>
            <tr ng-show="item.showClients">
                 ..... // client data

The bizarre behavior I'm having is this:

If I leave the 'showClients' property undefined in the customers data set, everything works as expected, except that the chevron icon does not show at first. After clicking once, it shows up and the toggle works as expected. I was thinking this might be because the ng-class is looking for true or false, and undefined doesn't satisfy either of these.

If I pre-define the showClients property to either true or false, the chevron shows correctly on page load and the client list shows correctly, but the toggle no longer functions, as though ng-click is either not doing anything or for some reason is unable to change the value. I'm not sure how to debug an in-line directive like that.

EDIT

Per request, here is the relevent code from the controller:

filter('clientSettingsFilter', function () {
    return function (customers, customerId) {
        var filtered = [];

        if (!customerId)
            return filtered;

        customerId = customerId.toLowerCase();

        angular.forEach(customers, function (item) {
            if (item.id.toLowerCase().indexOf(customerId) !== -1) {

                // format some text properties, omitted for brevity

                // if this line is uncommented, the ng-click does not work
                //item.showClients = false;
                filtered.push(item);
            }
        });

        return filtered;
    };
});
3
  • add you js code also please Commented Dec 11, 2014 at 11:42
  • @Floradu88 Edited. The only thing that happens in the controller is whether or not I set the showClients property. Commented Dec 11, 2014 at 11:50
  • Would You Please Give Us a detailed customer object to understand flow better? Commented Dec 11, 2014 at 13:25

1 Answer 1

3

The conditional you are using in ng-class will only add something when value is either true or false, not when it's undefined.

Instead use the more verbose ternary operator:

ng-class="item.showClients ? 'glyphicon-chevron-down' : 'glyphicon-chevron-right'"

And might as well move the class glyphicon to the ordinary class attribute:

class="glyphicon"

Demo: http://plnkr.co/edit/bxgp4HyFkOygc0foxAKN?p=preview

The behavior you are witnessing when uncommenting item.showClients = false; in your filter is due to how the digest loop works.

If item.showClients is false and you click the tr the following will happen (a bit simplified):

  1. The expression in ng-click will execute, setting item.showClients to true
  2. The digest loop will start
  3. The filter will run and set item.showClients to false again

Filters are meant for filtering, not for modification.

Also note that when using a filter with ng-repeat it will fire each digest cycle, and as each digest loop consists of multiple digest cycles (minimum of two) it's important to keep filters simple or they will have a bad impact on performance.

Sign up to request clarification or add additional context in comments.

3 Comments

Couldn't produce the scenario where the ng-click didn't function.
Ok, I get that. But showClients still comes in undefined. Watch what happens when you define it in your list: plnkr.co/edit/J59fLIa3wggKPj6v58U5?p=preview (all i changed was line 29, item.showClients = false, if you comment it out it works again)
Ah, this makes sense. So if I want to massage any data or, for example, set the initial state of showClients to true for certain customers, I should be doing that in the controller in the result function for the actual query, not in the filter, correct?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.