Solving AngularJS empty select option caused by jQuery UI effect

If you’re anything like me, while working with AngularJS you will soon want to integrate your “standard library” of jQuery and UI plugins into AngularJS directives. Recently on the job, I wrote an AngularJS directive to apply a jQuery UI effect to an element—I wanted to briefly shake a small form if an input was invalid (such as you often see in native app GUIs). The directive looked something like this:

Much to my surprise, during the shake effect a <select /> in the form went from having a non-empty option selected to having an empty option selected, and there was not an empty option in the select to begin with.

You may already be familiar with a select in AngularJS “mysteriously” gaining an empty option. That’s not mysterious at all as it turns out; it is reasonable behavior when the model associated with the select is not given a valid value. Here is a quick sample to demonstrate this problem and solution:

This was clearly not the problem I experienced. I had initialized the model correctly, and the empty selected option only appeared after the jQuery UI effect had finished. Admittedly, I still don’t know what jQuery UI is doing to cause this. However, without delving into the core of jQuery UI, I do at least have a solution to the problem for now; the answer lies in using ng-options instead of <option /> elements.

Here is a sample of the problem and solution side by side. Switch to the Result tab, then click the “Shake” button to see one select lose its value and the other retain its value as desired.

Cookie to the first person who can say what jQuery UI is doing to trigger the problem when using <option /> elements vs. ng-options!


Make AngularJS $http service behave like jQuery.ajax()

There is much confusion among newcomers to AngularJS as to why the $http service shorthand functions ($, etc.) don’t appear to be swappable with the jQuery equivalents (, etc.) even though the respective manuals imply identical usage. That is, if your jQuery code looked like this before:

You may find that the following doesn’t exactly work for you with AngularJS out of the box:

The problem you may encounter is that your server does not appear to receive the { foo: 'bar' } params from the AngularJS request.

The difference is in how jQuery and AngularJS serialize and transmit the data. Fundamentally, the problem lies with your server language of choice being unable to understand AngularJS’s transmission natively—that’s a darn shame because AngularJS is certainly not doing anything wrong. By default, jQuery transmits data using Content-Type: x-www-form-urlencoded and the familiar foo=bar&baz=moe serialization. AngularJS, however, transmits data using Content-Type: application/json and { "foo": "bar", "baz": "moe" } JSON serialization, which unfortunately some Web server languages—notably PHP—do not unserialize natively.

Thankfully, the thoughtful AngularJS developers provided hooks into the $http service to let us impose x-www-form-urlencoded on all our transmissions. There are many solutions people have offered thus far on forums and StackOverflow, but they are not ideal because they require you to modify either your server code or your desired usage pattern of $http. Thus, I present to you the best possible solution, which requires you to change neither server nor client code but rather make some minor adjustments to $http‘s behavior in the config of your app’s AngularJS module:

[box type=”info”]Do not use jQuery.param() in place of the above homegrown param() function; it will cause havoc when you try to use AngularJS $resource because jQuery.param() will fire every method on the $resource class passed to it! (This is a feature of jQuery whereby function members of the object to parametrize are called and the return values are used as the parametrized values, but for our typical use case in AngularJS it is detrimental since we typically pass “real” object instances with methods, etc.)[/box]

Now you can go forward with using $ and other such methods as you would expect with existing server code that expects x-www-form-urlencoded data. Here are a few sample frames of the final result for your day-to-day, end-to-end code (i.e. what you hoped and dreamed for):

The HTML template

The client code (AngularJS)

The server code (PHP)

Other notes

So you may wonder now, is it possible for PHP to read the JSON request from stock AngularJS? Why, of course, by reading the input to PHP and JSON decoding it:

Obviously the downside to this is that the code is a little less intuitive (we’re used to $_POST, after all), and if your server-side handlers are already written to rely on $_POST, you will now have to change server code. If you have a good framework in place, you can probably effect a global change such that your input reader will transparently detect JSON requests, but I digress.

Thank you for reading. I hope you enjoyed my first POST. (Get it?!)

[box type=”info”]EDIT March 12, 2014: Moved the param() function def up a level to help interpreter only create one such instance of that function.[/box]