Overriding AngularJS's Default POST Content-Type

I have been working on a custom WordPress plugin at work that utilizes AngularJS. After some playing around with it, I was excited to stretch my legs and use Angular in production. I come from a background heavily influenced by PHP and jQuery, so getting into Angular has been a challenge because it’s an entirely different way of thinking. It’s a fun, rewarding challenge, but I’ve found myself having to relearn how to do simple tasks. I’ve written hundreds of AJAX calls using jQuery and it’s super-helpful .serialize() function. Making AJAX requests with Angular is simple in practice, but I encountered a problem: Angular sends all of its requests with a JSON content-type. The PHP application I’m leveraging on the backend (WordPress in this case) isn’t expecting JSON. I needed to modify how Angular sends information. And after some digging around, I’ve found a solution.

WordPress’s ajaxurl

Here’s what lead to my problem, but if you’re more interested in the fix, you can skip to the next section. Our plugin is utilizing WordPress’s fantastic wp_localize_script(); function to handle all of our AJAX requests on the backend. When shipping data around, instead of coding the request URL in my scripts, we send things over to a defined variable ajaxurl. The ajaxurl is expecting a JSON object indicating which action to take, along with all the data you want to send to the server. My problem arose when I was sending a few variables over, but nothing happened. I checked the POST requests’ headers using Chrome’s inspector and saw the data was going just fine…as JSON. I needed to change the POST content-type.

The fix After several minutes of googling and reading StackOverflow posts, I found a blog with the solution. I mentioned above that I come from a jQuery background - this post happens to be titled “Make AngularJS’s $http service behave like jQuery.Ajax()” - perfect! The post there details what’s going on and how to fix it quite well. If you’re just interested in the fix, read through there and you’ll be good to go.

Cleaning it up

While the solution above works, I hated that I had that big httpProvided function hanging off the end of my module instantiation. I was sure there was a way I could package it up as it’s own file and include it into my Angular app, but wasn’t quite sure how. So, I reached out to the authors of one of my favorite webdev blogs, Scotch.io, and asked for his advice. He explained that I could wrap the httpProvider up as its own module and inject that into my app. So, I took the author’s code from above, and packed it up like so, in a file called module-http-provider.js:

// in module-http-provider.js
/**
   * httpProvider
      * Injectable Angular Module to override default HTTP requests
         * This is done because Angular normally passes around JSON requests
            * PHP expects x-www-form-urlencoded requests
               *
                  * @return {httpProvider}
                     */

 angular.module('httpProvider',[]).config(function($httpProvider) {
     // Use x-www-form-urlencoded Content-Type
       $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';

  /**
     * The workhorse; converts an object to x-www-form-urlencoded serialization.
        * @param {Object} obj
           * @return {String}
              */
                var param = function(obj) {
                      var query = '', name, value, fullSubName, subName, subValue, innerObj, i;

    for(name in obj) {
            value = obj[name];

if(value instanceof Array) {
  for(i=0; i<value.length; ++i) {
    subValue = value[i];
    fullSubName = name + '[' + i + ']';
    innerObj = {};
    innerObj[fullSubName] = subValue;
    query += param(innerObj) + '&';
  }
}
else if(value instanceof Object) {
   for(subName in value) {
      subValue = value[subName];
      fullSubName = name + '[' + subName + ']';                                                                                                                      innerObj = {};                                                                                                                                        innerObj[fullSubName] = subValue;                                                                                                                                                  query += param(innerObj) + '&';                                                                                                                                           }                                                                                                                                                               }
 else if(value !== undefined && value !== null)                                                                                                                                                                                 query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
 }
 return query.length ? query.substr(0, query.length - 1) : query;
};

// Override $http service's default transformRequest
$httpProvider.defaults.transformRequest = [function(data) {
  return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];
});

After including this file in your view, you can inject it into your main Angular app using the injector array:

var app = angular.module('yourApp', ['httpProvider']);

Now, whenever you submit data over AJAX, it will go with the PHP-friendly application/x-www-form-urlencoded content-type.