How to get clean mark-up using Angular conditionals

75 views
Skip to first unread message

William Huster

unread,
Mar 17, 2014, 1:58:58 PM3/17/14
to ang...@googlegroups.com

This is a cross-post from StackOverflow, but I'm afraid it might be too subjective and will get deleted.

Anyway, I need some guidelines on how to produce clean mark-up when using Angular conditionals.

On my team, I'm the go-to CSS guy. Sometimes I get mark-up that's much more complex and nested than it needs to be, and I take it upon myself to clean it up.

For example, I just re-factored the following mark-up from this:

<div ng-show="isGroupingActive()">
    <div style="padding-left: {[{ getPadding(row) }]}px;">
        <div ng-show="row.children"
             ng-click="showHideGroup(row)">
                <div ng-switch on="row.expanded">
                    <div ng-switch-when="true">+</div>
                    <div ng-switch-when="false">-</div>
                </div>
        </div>
        <div ng-hide="row.children">
            .
        </div>
    </div>
</div>

To this:

<div ng-if="row.children && row.expanded"
      ng-click="showHideGroup(row)"
      style="padding-left: {[{ getPadding(row) }]}px;">-</div>

<div ng-if="row.children && !row.expanded"
      ng-click="showHideGroup(row)"
      style="padding-left: {[{ getPadding(row) }]}px;">+</div>

<div ng-if="!row.children"
      style="padding-left: {[{ getPadding(row) }]}px;">.</div>

I much prefer the single DOM element produced by version 2 over the unnecessary complexity of the nested DOM in the original. Further, I think this logic is far more readable and maintainable.

Comparing the two versions, it occurred to me that my colleague was writing HTML the same way he might write the conditionals in JavaScript (where he is more comfortable). He was observing DRY principles. Notice how my version repeats itself a few times. If we were to add more conditions, we'd have to repeat ourselves some more. Still, version 2 yields much cleaner, simpler HTML.

So my question: How do you react to my revision, and what is the best approach to this problem?

Sander Elias

unread,
Mar 17, 2014, 3:27:03 PM3/17/14
to ang...@googlegroups.com

Hi,
You should really do this a whole lot more dry!

<div ng-click="showHideGroup(row)"  style="padding-left: {[{ getPadding(row) }]}px;">
    {{ row.children ? row.expanded ? "-" : "+" : "." }}}
</div>`

regards
Sander

William Huster

unread,
Mar 17, 2014, 3:43:01 PM3/17/14
to ang...@googlegroups.com
Even better! This makes for even cleaner compiled DOM that doesn't have those ng-if comments.

Thanks.

William Huster

unread,
Mar 17, 2014, 4:38:04 PM3/17/14
to ang...@googlegroups.com
As a courtesy to anyone who might stumble across this post. Here's my final implementation:

<i ng-click="showHideGroup(row)"
   ng-class="getToggleIcon(row)"
   class="treeToggle"
   style="padding-left: {[{ getPadding(row) }]}px;">
</i>

I opted to use fontawesome icons instead of '+' / '-' / '.' as content inside of a div. The logic and icon class definitions were moved out of the template and into the getToggleIcon() function on scope.

William


--
William Huster
m1 | (202) 255-8444


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/W2MoRfpZEs0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.

Luke Kende

unread,
Mar 18, 2014, 2:34:29 AM3/18/14
to ang...@googlegroups.com
And for the sake of consistency/readability why not use ng-style instead of interpolation on style attribute?  (Forgive me since I have not seen this syntax before { [ { } ] } )

<i ng-click="showHideGroup(row)" 
    ng-class="getToggleIcon(row)" 
    class="treeToggle" 
    ng-style="getPadding(row)">
</i>

Another thing I'd mention here is related to performance. If your within an ng-repeat on a lot of rows and your scope functions are heavy, this approach can really degrade the performance of the app.  You might maintain state directly on the row object instead from the ng-click function:

$scope.showHideGroup = function(row){
  row.isOpen = !row.isOpen; //toggler
  if (row.isTreeNode){ 
    row.padLeft = 'padding-left: 10px';
    row.iconClass  = row.isOpen ? 'minus-sign' : 'plus-sign';
  }
  else {
    row.padLeft = 'padding-left: 20px';
    row.iconClass = 'dot-symbol';
  }
}

<i ng-click="showHideGroup(row)" 
    ng-class="row.iconClass" 
    class="treeToggle" 
    ng-style="row.padLeft">
</i>

The result will be that the digest cycle will not be executing your functions every time a property on scope changes but instead only when an actual click is performed.

I know this goes beyond your original question, and I'm sure Sander will correct me if I am wrong, but I have found this approach of getting functions off of my scope to be better practice for performance.

Sander Elias

unread,
Mar 18, 2014, 3:02:13 AM3/18/14
to ang...@googlegroups.com
Hi Luke,

You are not wrong. Those function calls are hurting the javascript part of the performance. However, javascript performance is seldom the culprit. In this case it might be a good idea, because it probably is used recursive!
I like the idea you proposed here, however, your current proposal will only work after being clicked at least once. I think that might be a problem here.

Regards
Sander

William Huster

unread,
Mar 18, 2014, 11:10:48 AM3/18/14
to ang...@googlegroups.com
Yeah, we are using the '{[{ }]}' syntax to prevent our Angular template interpolation from colliding with Django's, which is powering our app.

Great advice all around! I appreciate it. We are indeed recursively building a tree, and this piece renders an icon on each row based on the state of the row and position of the row in the hierarchy. This could be repeated hundreds of times, so performance is definitely a concern. I will share with my team.

Thanks!
William

--
William Huster
m1 | (202) 255-8444


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/W2MoRfpZEs0/unsubscribe.
Reply all
Reply to author
Forward
0 new messages