Sunday 19 October 2014

Use ngClassEven and ngClassOdd to Lists with Alternate Row Styling

At some point or another you'll have had a list or table that has been created programatically and requires alternate row/item styling. Prior to CSS3's nth-child() the only way to do this was to create separate classes for the alternate rows. Even when we did get nth-child(), not many of us had the luxury of being able to use this as we were still constrained by older browsers (IE I'm looking at you) and so couldn't use it anyway. I remember many a time creating a C# variable called cssClass that would be assigned to every other row based on a modulus calculation of the dataset's row's index that I may or may not have had to create another variable for. Thankfully AngularJs has now removed this pain from our development by creating this pair of directives for use within ngRepeats.

HTML I Wrote

First of all you need to create your repeater loop. I'm going to use the code from my post on creating lists with ngRepeat. I've modified this code so that it now contains ngClassEven and ngClassOdd on the top level (li) of the repeater.

<ul data-ng-controller="myController">
    <li data-ng-repeat="item in items" data-ng-class-even="'even'" data-ng-class-odd="'odd'">
        <div>
            <h1>{{item.firstName}} {{item.lastName}}</h1>
            <p>Gender: {{item.gender}}, Age: {{item.age}}</p>
        </div>
    </li>
</ul>


What's going to happen here is that as AngularJs creates your list it will calculate whether the li it's currently building has an index that is odd or even. This is the logic that I used to write in C#. If the index is even then it will populate the li's class attribute with the string that has been passed through as the value of ng-class-even, in the example above that's even. The same is true of ngClassOdd; if the index is odd then the class attribute will gain the value of ng-class-odd (in this case odd). You don't have to include both of these as I have. It's probably best to have a default style, and then override it with either odd or even. This operation is an append, so if you have a class attribute on the same element as either ngClassEven or ngClassOdd it will remain there.

HTML AngularJs Generated

This is the code that is rendered in the browser:

<ul data-ng-controller="myController" class="ng-scope">
    <!-- ngRepeat: item in items -->
    <li data-ng-repeat="item in items" data-ng-class-even="'even'" data-ng-class-odd="'odd'" class="ng-scope odd">
        <div>
            <h1 class="ng-binding">Glenda Fitzpatrick</h1>
            <p class="ng-binding">Gender: f, Age: 21</p>
        </div>
    </li>
    <!-- end ngRepeat: item in items -->
    <li data-ng-repeat="item in items" data-ng-class-even="'even'" data-ng-class-odd="'odd'" class="ng-scope even">
        <div>
            <h1 class="ng-binding">Frank Jones</h1>
            <p class="ng-binding">Gender: m, Age: 32</p>
        </div>
    </li>
    <!-- end ngRepeat: item in items -->
    <li data-ng-repeat="item in items" data-ng-class-even="'even'" data-ng-class-odd="'odd'" class="ng-scope odd">
        <div>
            <h1 class="ng-binding">Sarah Graham</h1>
            <p class="ng-binding">Gender: f, Age: 24</p>
        </div>
    </li>
    <!-- end ngRepeat: item in items -->
    <li data-ng-repeat="item in items" data-ng-class-even="'even'" data-ng-class-odd="'odd'" class="ng-scope even">
        <div>
            <h1 class="ng-binding">Grant Smith</h1>
            <p class="ng-binding">Gender: m, Age: 30</p>
        </div>
    </li>
</ul>

Gotchas

This is one of the examples of the single quotes in double quotes gotcha. I find this one all over the place, and I either find myself quickly resolving the issue by looking at the documentation to see why my directive usage isn't working, or after hours of making sure everything else is correct and then realising that it's probably this. I looked at ng-class-even="even" and wondered to myself why this wasn't working, then look at the docs and saw that annoying single quote that changed my code to ng-class-even="'even'".

Something that I didn't expect was that the indexing on ngRepeat is 1 based. Like many developers I'm used to working with 0 based arrays (I've not used a language with 1 based arrays since my first year of uni) so I thought this would put even on the first row, and then odd on the second. When I saw the results were the opposite I was surprised, but I think this is a much more intuitive way of doing it and offers an easier entry point into alternating row styling for new developers and those without a computer science background.

Although this is incredibly useful and solves the most frequent problem of repetitively styling loops, I long for an ngClassNth. I'm sure there aren't many use cases for wanting to style three, four, or five lines repeatedly, but I would still like to have that option available.

Note: I've used data-ng-repeat here so that my html passes validation. You can choose whether or not to prefix ng-repeat - and what with - based on your own needs.

No comments:

Post a Comment