AngularJS Pro-Tip: Be Mindful Of HTML Attribute Ordering
When it comes to programming, I absolutely believe that the more mindful you are about your choices, the better you will become at programming over time. In AngularJS, one small aspect of the code that can benefit from mindfulness is the ordering of HTML attributes in your templates. In this post, I'm not saying that you have to use my approach; I'm simply sharing my approach and am strongly recommending that you use "an approach" and use it consistently.
In AngularJS, not all HTML attributes are equal; some have no significant effect; others have a profound impact on how your page is rendered. I believe that this diversity in effect merits a certain amount of thoughtfulness. We may disagree on the outcome, but I hope that we can all agree that attribute ordering is an important part of an AngularJS application.
These are the basic rules that I use when structuring my HTML:
- Directives that determine the existence of an element must absolutely come first in the order of attributes. This includes directives like ngRepeat, ngIf, and ngSwitchWhen. If a DOM element doesn't exist, the rest of the attributes are moot (for the most part).
- Once we know that an element is going to exist, I include the ngController directive. This maintains the scope and the state of the view-model which will be consumed by every subsequent directive on the element.
- Next, I would probably use the ngModel directive as this can have a direct affect on the view-model.
- After the controller and the two-way data binding, I include directives that affect the visibility of the element. This includes directives like ngShow and ngHide. After all, if an element is not visible, there's not much else that the subsequent attributes can do for it (for the most part).
- Directives that define interaction behaviors come next. These include directives like ngClick, ngMouseenter, and ngMouseleave. Once you've determined that an element will exist and is visible to the user (see items above), the next most important thing is knowing how the user can interact with it.
- After behaviors comes data attributes. These are things like "id", "title", "rel", and "alt". The Image ngSrc directive falls into this category; however, because the ngSrc directive has a very material affect on the rendering of the page, I would suggest making it the first of the data attributes. Other directives, like ngValue and ngDisabled, would also be part of this category of attributes.
- After the data attributes, I start styling the element. First, I use the "class" attribute for my static classes. Then, I use the ngClass directive to override the list of attached classes. ngClass comes after the "class" attribute because I like to think of it as "overriding" the class, not simply adding a class.
- After classes, I use local styles. This comes from the "style" attribute and then subsequently the "ngStyle" directive. Styles come after classes because they "override" classes; and similarly, ngStyle comes after "style" because it overrides the "style" attribute.
- And last, I include directives that affect the rendering of the node content. This includes directives like ngInclude and ngSwitch. By putting them last in the block of attributes, it puts them closest to the content that they most directly affect.
Here's a graphical example of the above rules:
It's not a perfect system. And, it's not a system that I am consistent with all the time. I've been a web developer for a good while, so I have a lot of emotional baggage to overcome. Plus, I'm still relatively new to AngularJS; and, the more I learn about it, the more I evolve my approach to building AngularJS applications.
Whether you agree with my decisions or not, I'm not too concerned. But, I would urge you to think about how you structure your HTML. Not only will this make the code easier to write, I believe it will also make the code easier to maintain, both by you and by your coworkers.
I like consistency like this, at least for developers' eyes that might be working on a project (or just you on your own). I'm wondering, have you looked at any stacktraces or anything to see the processing order to see if Angular has it's own routine for grabbing attributes, vs the order it's listed in the HTML?
You bring up a great point, and it ties in nicely with what I'm talking about here. When AngularJS collects the directives on a given element, is sorts them based on the "priority" defined in the directive configuration object. It then compiles them in descneding order of priority and then links them in a negative ascending order (from what I recall off the top of my head).
So, in some way, what I'm advocating here is ordering things in alignment with their AngularJS priority. This forces you (as the developer) to think about how AngularJS is actually applying the directives. And, it also gives insight to other developers (coworkers) as to how AngularJS is doing things.
For example, if you look at my printed example, the related directive priorities are:
ng-repeat -- priority: 1000
ng-controller -- priority: 500
ng-mouseenter -- priority: 0
ng-class -- priority: 0
ng-switch -- priority: 0
Then, on the internal DIVs:
ng-switch-when -- priority: 1200
As you can see, I am basically ordering them in the order of their priority and then in also using my own mental model.
I think this makes it much more clear that the developer understands how the directives are being used and which ones depend on which other ones.
I tend to go with "valid HTML attributes" first, then the directives (even if HTML attribute IS the directive)
the rest makes sense and keeps the code readable and sane ;-)
I can dig it. Consistency feels like the important part, so you know where to look for what.
"Mindful Of HTML Attribute Ordering"
are u fucking kidding me? lol realy...
Just to be clear, I'm not talking about HTML in general - I'm talking very specifically about AngularJS and the order of HTML attributes as they pertain the existence and behaviors of elements with applied behavior. What is your strategy for dealing with such things?
Regarding this comment:"When AngularJS collects the directives on a given element, is sorts them based on the "priority" defined in the directive configuration object. It then compiles them in descneding order of priority and then links them in a negative ascending order (from what I recall off the top of my head)"
It seems like the organization you're proposing is mainly for keeping in mind the organization of of priority and not performance? If it does a sort on the attributes and assigns its own priority, it seems like it's unnecessary extra work to actually order things in this way (with the obvious exception of things that exist lower in the hierarchy like bindings inside a repeat).
Is that accurate, or am I missing something?
You are correct in that it is not a performance consideration. In fact, none of the sorting of attributes is done for the AngularJS framework - rather I am proposing it only for the people who have to *read* your code later on. If you order your attributes in a way that is most "meaningful" then the developer has to think less about how the page is operating. Consider the following:
<div id="foo" class="blam" ng-click="doSomething()" ng-styles="styles" ng-if="false">...</div>
In this particular DIV, there is only *one relevant attribute* - the ng-if="false" attribute. This attribute will prevent the element from actually being linked and injected into DOM. This one attribute makes all of the other attributes on the moot because they have zero bearing on what the page is actually doing.
As such, why make the "other guys" have to read that far just to figure out what is going on? If you put the ng-if="false" as the first attribute in the source, the "other guys" can read your code and immediately see, "Oh, this element isn't being rendered," and they can stop mentally parsing the markup.
Remember, most of the cost of code comes from maintenance, not the initial authoring. As such, anything you can do to make it easier for *future you* or other programmers is well worth the additional effort.