Posting Form Data With $http In AngularJS

By default, when you go to post data in an AngularJS application, the data is serialized using JSON (JavaScript Object Notation) and posted to the server with the content-type, “application/json”. But, if you want to post the data as a regular “form post,” you can; all you have to do is override the default request transformation.

When you define an AJAX (Asynchronous JavaScript and XML) request in AngularJS, the $http service allows you to define a transform function for both the outgoing request and the incoming response. These are optional; and, by default, AngularJS provides transform functions that deal with JSON. This is a very flexible format because the post data can have an arbitrarily nested structure; but, it requires additional processing on the server.

If you want to post the data as a regular form post, two things need to happen:

  • The content-type needs to be reported as “application/x-www-form-urlencoded”.
  • The data needs to be serialized using “key=value” pairs (much like a query string).

Both of these requirements can be fulfilled within a request transform function, which has access to the outgoing headers collection and the non-serialized data. The goal of the transform function is to update the headers (as needed) and to return the modified data that will be injected into the underlying XMLHttpRequest object.

I didn’t find a serialization function in AngularJS that was designed for form-posts; so, I copied(ish) the .param() implementation in jQuery. In the following demo, I’m posting the data using this transform function. The server is then dumping out the content of the FORM scope and returning it in the result, which we are rending on the page.

    Posting Form Data With $http In AngularJS




<h1>
    Posting Form Data With $http In AngularJS
</h1>

<!-- Initialize scripts. --> <a href="http://../jquery/jquery-2.1.0.min.js">http://../jquery/jquery-2.1.0.min.js</a> <a href="http://../angularjs/angular-1.2.4.min.js">http://../angularjs/angular-1.2.4.min.js</a> // Define the module for our AngularJS application. var app = angular.module( "Demo", [] ); // -------------------------------------------------- // // -------------------------------------------------- // // I control the main demo. app.controller( "DemoController", function( $scope, $http, transformRequestAsFormPost ) { // I hold the data-dump of the FORM scope from the server-side. $scope.cfdump = ""; // By default, the $http service will transform the outgoing request by // serializing the data as JSON and then posting it with the content- // type, "application/json". When we want to post the value as a FORM // post, we need to change the serialization algorithm and post the data // with the content-type, "application/x-www-form-urlencoded". var request = $http({ method: "post", url: "process.cfm", transformRequest: transformRequestAsFormPost, data: { id: 4, name: "Kim", status: "Best Friend" } }); // Store the data-dump of the FORM scope. request.success( function( html ) { $scope.cfdump = html; } ); } ); // -------------------------------------------------- // // -------------------------------------------------- // // I provide a request-transformation method that is used to prepare the outgoing // request as a FORM post instead of a JSON packet. app.factory( "transformRequestAsFormPost", function() { // I prepare the request data for the form post. function transformRequest( data, getHeaders ) { var headers = getHeaders(); headers[ "Content-type" ] = "application/x-www-form-urlencoded; charset=utf-8"; return( serializeData( data ) ); } // Return the factory value. return( transformRequest ); // --- // PRVIATE METHODS. // --- // I serialize the given Object into a key-value pair string. This // method expects an object and will default to the toString() method. // -- // NOTE: This is an atered version of the jQuery.param() method which // will serialize a data collection for Form posting. // -- // https://github.com/jquery/jquery/blob/master/src/serialize.js#L45 function serializeData( data ) { // If this is not an object, defer to native stringification. if ( ! angular.isObject( data ) ) { return( ( data == null ) ? "" : data.toString() ); } var buffer = []; // Serialize each key in the object. for ( var name in data ) { if ( ! data.hasOwnProperty( name ) ) { continue; } var value = data[ name ]; buffer.push( encodeURIComponent( name ) + "=" + encodeURIComponent( ( value == null ) ? "" : value ) ); } // Serialize the buffer and clean it up for transportation. var source = buffer .join( "&amp;" ) .replace( /%20/g, "+" ) ; return( source ); } } ); // -------------------------------------------------- // // -------------------------------------------------- // // I override the "expected" $sanitize service to simply allow the HTML to be // output for the current demo. // -- // NOTE: Do not use this version in production!! This is for development only. app.value( "$sanitize", function( html ) { return( html ); } );

Notice that the call to the $http service is basically unchanged. The only difference, from a normal post, is that we are explicitly passing-in the “transformRequestAsFormPost” function as the “transformRequest” configuration option.

When we run the above code, we get the following page output:

angularjs-form-post

As you can see, the outgoing request data was serialized for consumption as a regular form post.

NOTE: This does not use the “multipart/form-data” content type, which is primarily used for form posts that include binary file uploads.

In general, I like posting data as JSON. But, it does require some preprocessing on the server. Sometimes, it’s nice to just to deal with normal form data that the server can consume automatically. It’s nice that AngularJS is flexible enough to use both formats.