📜 ⬆️ ⬇️

AngularJS + PHP. We force $ http-service to be like jQuery.ajax ()

Newbies in Angulyar are often confused due to the fact that the fast functions of the $ http service (eg, $http.post() ) are not interchangeable with the equivalent functions of Jikveri (eg, jQuery.post() ), despite that the relevant manuals describe their use in a similar way. That is, if the code in Jikvery had the following form:

 (function($) { jQuery.post('/endpoint', { foo: 'bar' }).success(function(response) { // ... }); })(jQuery); 

You may find that the following is exactly the same does not work out of the box in Angulyar:

 var MainCtrl = function($scope, $http) { $http.post('/endpoint', { foo: 'bar' }).success(function(response) { // ... }); }; 

The problem you may encounter is that the server cannot receive { foo: 'bar' } parameters from Angulyar’s request.
')
The difference is in how Jikvery and Angulyar serialize and transmit data. Basically, the problem lies in the language in which the server part is written and which simply does not understand the transfer of Angulyar with the default settings - it's a hell of a shame because Angulyar, of course, does not do anything wrong. By default, Jikvery transfers data using Content-Type: x-www-form-urlencoded and the usual string foo=bar&baz=moe . Angulyar, however, transmits data using Content-Type: application/json and { "foo": "bar", "baz": "moe" } JSON strings, which, unfortunately, some server languages ​​— especially PHP — were not originally transform into an object.

Fortunately, Angulyar’s developers took care of supporting this method with a $ http-service in order to install x-www-form-urlencoded for all our broadcasts. There are many solutions offered on the forums and StackOverflow, but they are not perfect, because they require either to change the server code or the $ http usage pattern. Therefore, I present to you the best possible solution that does not require any changes to the server or client code, but suggests making some minor changes to the work of $ http in the settings of the application module:

 //    ... angular.module('MyModule', [], function($httpProvider) { //  x-www-form-urlencoded Content-Type $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; //   transformRequest  $http- $httpProvider.defaults.transformRequest = [function(data) { /** *  ;    x-www-form-urlencoded . * @param {Object} obj * @return {String} */ var param = function(obj) { var query = ''; var name, value, fullSubName, 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; }; return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data; }]; }); 

Note: The large snippet above should always be used with the param() function. Do NOT use jQuery.param() instead; which will cause chaos when you try to use Angular’s ​​$ resource, because jQuery.param() will reject every $ resource class method passed to it! (This is a jQuery feature due to which the properties of the object to parameterize that contain functions are called and the value returned by them is used as a parameterized, but in the case of Angular, this can be detrimental, since we usually pass "real" instances of objects with .d.)

Just use the above snippet and everything will be fine!

Now you can move on using $http.post() and other similar methods, assuming that the existing server code is expecting x-www-form-urlencoded data. Here are some examples of the final result for everyday, complete code (an example of what you hoped and dreamed of):

Html template
 <div ng-app="MyModule" ng-controller="MainCtrl"> <p ng-show="loading">Loading...</p> <p ng-hide="loading">Response: {{response}}</p> </div> 

Client Code (AngularJS)
 var MainCtrl = function($scope, $http) { $scope.loading = true; $http.post('/endpoint', { foo: 'bar' }).success(function(response) { $scope.response = response; $scope.loading = false; }); }; 

Server code (PHP)
 < ? header('Content-Type: application/json'); // -! $_POST  ; PHP   //      echo json_encode($_POST); ?> 

Other notes
This begs the question, is it still possible to read Angular's JSON request in PHP? Well, of course, if you read the PHP input stream and decode JSON:

 < ? $params = json_decode(file_get_contents('php://input')); //  ,     PHP  (. ) $params = json_decode(trim(file_get_contents('php://input')), true); ?> 

The obvious drawback is that the code is a bit less intuitive (we are used to $ _POST, after all), and if the server handlers are already written using $ _POST, you will have to rewrite the server code. If a good framework is used, you can probably make global changes so that the input handler transparently identifies JSON requests, but I digress.

Translator's note: If possible, it is better to configure the server to accept Content-Type: application/json , which is more logical. The article describes the approach for extreme cases when nothing can be done on the server.

Additive. Supplement from nervgh

 'use strict'; (function() { angular.extend( angular, { toParam: toParam }); /** *  ,      , *       url *   [url]http://api.jquery.com/jQuery.param/[/url] *  [url]http://stackoverflow.com/questions/1714786/querystring-encoding-of-a-javascript-object/1714899#1714899[/url] * * @param object * @param [prefix] * @returns {string} */ function toParam( object, prefix ) { var stack = []; var value; var key; for( key in object ) { value = object[ key ]; key = prefix ? prefix + '[' + key + ']' : key; if ( value === null ) { value = encodeURIComponent( key ) + '='; } else if ( typeof( value ) !== 'object' ) { value = encodeURIComponent( key ) + '=' + encodeURIComponent( value ); } else { value = toParam( value, key ); } stack.push( value ); } return stack.join( '&' ); } }()); 


 'use strict'; // change default settings var app = angular.module( 'app', [ ... ]); app .config( function( $httpProvider ) { // [url]http://habrahabr.ru/post/181009/[/url] $httpProvider.defaults.headers.post[ 'Content-Type' ] = 'application/x-www-form-urlencoded;charset=utf-8'; $httpProvider.defaults.transformRequest = function( data ) { return angular.isObject( data ) && String( data ) !== '[object File]' ? angular.toParam( data ) : data; }; }); 

Source: https://habr.com/ru/post/181009/


All Articles