Wednesday, August 14, 2013

AngularJS Missing Directives: Focus & Blur Part 2

In the first part of this series about supporting focus and blur events I elected to follow the template in the AngularJS source code for event directives. This proved to be quite insightful as the AngularJS team just released 1.2.0rc1 which now supports these events natively in the same manner. This means you can just drop in the new version, remove the custom directives and any dependencies on these directives should still work! Check it out for yourself!

But to illustrate this point even further, lets continue with a previous discussion from the first part of this series. Namely the discussion about which adverb should be used for these event directives, "on" or "while". In the first part the directives used the "on" approach for each event. But this doesn't mean we still can't support "while". In fact, we already did in the original example by using both "on" events and storing the state in a scoped variable, focused.

We can extrapolate that concept and create a directive that will do this automatically. There are a few ways as always to skin this cat. We could simply just bind the events independently of the focus and blur directives, but that isn't much fun. Alternatively we can use those directives (no need to reinvent the wheel) and just build on top of them. The later approach will also lead us down a new path which will include learning about a couple of very cool tools. Plus, it's more fun.

NOTE: This approach does have one possible issue: without specifying different scope variables to use between various elements within the same scope, they will of course share the same state indicator which would be very confusing. So, basically dumb in equals dumb out.

Okay, on to the fun! First give it a try so you know I am not full of it.

Boom! Still works. Now lets dig in. The first difference is the new directive myng-focused has replaced both myng-focus and myng-blur on the element. Also, only the scope variable focused is passed now and not an AngularJS expression. Once last warning, remember to specify unique scope variables to track states for different elements unless you want to have a field day tracking down that bug.

Now for the juicy parts but if you need to review how the previous directives work, go take a look here.

The myngFocused directive is basically doing four things.
  1. Getting the scope variable name from the attribute
  2. Removing the attribute (this is important due to number 4)
  3. Sets the attributes for the focus and blur directives
  4. Recompiles the element
Steps 1-3 are fairly straightforward although steps 2 & 3 do introduce $set. The trick here is that if no value is specified, the attribute is removed. Useful for step 4.

Step 4 is the key. Without it, the HTML is updated but AngularJS takes no notice of the new attributes on the element. That is where $compile comes into play. It's a fairly powerful and useful service in the AngularJS world and used quite extensively in the source code. We use it here to force Angular to recompile the element which triggers it to recognize the new directives, myngFocus and myngBlur.

IMPORTANT! This is where removing the directive for myngFocused is crucial. If we omitted this step, when the element is recompiled AngularJS would see that directive and would attempt to initiate it. This would put it into a loop. Bad. It's why nearly all examples only use $compile on children of an element. Consider yourself warned.

And there you go. It should really be apparent now how directives are the building blocks of AngularJS.

Finally, as promised, here is the same directive using the new hotness of 1.2.0rc1. As you can see the directive is identical aside from using the new native focus and blur directives.