Unable to POST data in AngularJS - javascript

I am building a small application for fetching data from one JSON file and posting it to another JSON file. I am using AngularJS and ngResource and $http services to achieve the functionality.
I was able to read data using GET method, but POST is not posting data to my another JSON file.
Below are my controller and services definitions:
'use strict';
/* Controllers */
var boookbAppControllers = angular.module('boookbAppControllers', []);
boookbAppControllers.controller('boookbAppCtrl', ['$scope','$http','Book',
function($scope,$http,Book) {
$scope.way=["Normal","$http","RestFul"]
$http.get('data/books.json').success(function(data) {
$scope.b1 = data;
$scope.addBook1=function(){
$http.post('data/newbooks.json',{ "catalogRefIds" : $scope.books[0].title, "productId" : $scope.books[0].author}).then(function(response,status){
$scope.a=status;
alert($scope.a);
});
}
$scope.b2 = Book.query();
$scope.newBookName="";
$scope.userBook="";
$scope.userBookAuthor="";
$scope.newBookAuthor="";
$scope.bookData={};
$scope.addBook=function(){
$scope.userBook=$scope.newBookName;
$scope.userBookAuthor=$scope.newBookAuthor;
Book.add({},$scope.bookData);
}
});
}]);
'use strict';
/* Services */
var boookbAppServices = angular.module('boookbAppServices', ['ngResource']);
boookbAppServices.factory('Book', ['$resource',
function($resource){
return $resource('data/:bookId.json', {}, {
query: {method:'GET', params:{bookId:'books'}, isArray:true},
add: {method:'POST',params:{bookId:'books'},isArray: true}
});
}]);
I checked traces of POST data and no data was being sent.

I don't know what you are trying to do by POSTing to a .json file. That being said, assuming you are actually handling the POST request server side and not seeing any data, this is most likely the issue:
Angular posts data differently than jQuery, for example - it sends it over as application/json and not application/x-www-form-urlencoded. So, in PHP for example, if you check $_POST['key'] it won't find it. Your options there are either handle it server side:
$_JSON = file_get_contents("php://input");
$_JSON = json_decode($_JSON, TRUE);
echo $_JSON['key'];
Or you can transform it to form-data it client-side:
boookbAppControllers.config(function($httpProvider) {
$httpProvider.defaults.headers.post = {'Content-Type': 'application/x-www-form-urlencoded'};
$httpProvider.defaults.transformRequest = function(data) {
if (data === undefined) return data;
var clonedData = clone(data);
for (var property in clonedData)
//you could do this with hasOwnProperty probably,
//but this works unless you prepend variable keys
//with $
if (property.substr(0, 1) == '$')
delete clonedData[property];
return $.param(clonedData);
};
});

Please have a look at $http.post syntax: we need to pass data with the keyword data encapsulating actual data.
$http.post('data/newbooks.json', data : { "catalogRefIds" : $scope.books[0].title, "productId" : $scope.books[0].author}).then(function(response,status){
$scope.a=status;
alert($scope.a);
});

When posting JSON data use Content-Type as text/plain, tried and tested in angular 1.5.11
$http({
method : 'POST',
url : 'http://example.com',
headers: {
'Content-Type': 'text/plain',
},
data : JSON.stringify({data})
})

Related

Angular http request with custom data

I am trying to do a http request to a php file on my server. My code i am using at the moment is as follows:
App.controller('GetSales', ['$scope', '$http', function ($scope, $http) {
$http({
method: 'POST',
url: '/app/controller/apis/_sales.php?period_start=2015-07-01&period_end=2015-07-31&join=leads&status=0&category=0user=1'
})
.success(function (data) {
$scope.sales = data;
});
}]);
Isnt there a better way to do this?
When i add these var as data it doesnt get posted to my page?
data: {
period_start: '2015-07-01',
period_end: '2015-07-31',
join: 'leads',
status: '',
category: '',
user: '1'
};
In php i get the data like this, its also sanitized for security reason:
$user = filter_var($_REQUEST['user'], FILTER_SANITIZE_NUMBER_INT);
$period_start = $_REQUEST['period_start'].' 00:00:00';
At first sight you are tryng to call an HTTP POST service, but you send parameter like it was a GET service, try something like that:
App.controller('GetSales', ['$scope', '$http', function ($scope, $http) {
$http.post('/app/controller/apis/_sales.php',
{
period_start: '2015-07-01',
period_end: '2015-07-31',
join: 'leads',
status: '',
category: '',
user: '1'
})
.success(function (data) {
$scope.sales = data;
})
.error(function (data, status) {
console.log(status);
});
I would use json_decode( file_get_contents('php://input') ) on your serverside. Also, please don't forget to sanitize your user sent data!
var dataParams = {
period_start: '2015-07-01',
period_end: '2015-07-31',
join: 'leads',
status: '',
category: '',
user: '1'
};
App.controller('GetSales', ['$scope', '$http', function ($scope, $http) {
$http.post('/app/controller/apis/_sales.php', dataParams)
.success(function (data) {
$scope.sales = data;
});
}]);
You will want to watch ever using the variable data as it will most likely collide with another variable, such as in your demonstration where you have named your post params as data while the return response is also aliased as data in the $.post success. This may not cause an issue in this case - but it usually will, so I renamed it for you out of habit.
Your server side could look something like this depending on what your usernames strings consist of:
public static function sanatize_client_string($dirtyString){
$cleanString = htmlspecialchars(strtolower(preg_replace("/[^a-z]+/i", "[FORBIDDEN_CHAR]", $dirtyString)));
return $cleanString;
}
$client_data = sanatize_client_string(json_decode( file_get_contents('php://input')));
Now you can access the username like:
echo $client_data['user']; // Will echo 1 based on the post data you are sending
This is what a simple serverside data-router could look like, as using normal $_POST has never worked for Angular data for me either:
/**
* Collect all Angular HTTP Request data
*/
$client_data = json_decode( file_get_contents('php://input') );
$app_state = utils::sanatizeClientString($client_data->appState); // <- name of prop must match client angularPostObj.x = serverModel.x
/**
* Cache the view path to the model
*/
$module_exists_in_model = isset($app['view_data']['views'][$app_state]);
/**
* If the angular post request matches data in the model, return the requested dataset, while if no object is found
* under that address, the error object will be returned which will send and error data to the view.
*
* This error condition will never happen aside from an attack because the clientside AngularJS router would prevent any
* unregistered paths from being even sent to the server. This would happen using a post mocking service or by
* forcing the string change in the code inspector while using the site.
*/
$module_exists_in_model ?
$view_model = $app['view_data']['views'][$app_state] :
$view_model = $app['view_data']['views']['error'];
// Call the view from Angular post data, passing it to a Class that sends a response as valid JSON
Render_app::echo_json($view_model);
I was informed of this by: http://www.cleverweb.nl/javascript/a-simple-search-with-angularjs-and-php/ and How to post a data in Angular?.
The point is... use $client_data = json_decode( file_get_contents('php://input') ); instead of $client_data = $_POST['username'];

Proper use of transformers vs interceptors

When POSTing to an endpoint in a service layer to update a user's profile, I need to strip certain values from the request payload (the profile with the desired modifications from the client) and re-attach them in the response payload (the updated profile from the server). I am currently performing behavior using Angular's request and response transformers, like this:
myService.updateProfile = function (profile) {
return $http({
method: 'POST',
withCredentials: true,
url: root + 'users/profile',
data: profile,
transformRequest : requestTransformer,
transformResponse : responseTransformer
});
};
// the map used during transformation below
var myMap = {
0: 'foo',
1: 'bar',
2: 'etc'
};
// prependTransform() and appendTransform() are similar to the example provided in Angular transformer docs here:
// https://docs.angularjs.org/api/ng/service/$http#overriding-the-default-transformations-per-request
var requestTransformer = httpTransformer.prependTransform($http.defaults.transformRequest, function(profileRequest) {
profileRequest.myKey = myMap.indexOf(profileRequest.myValue);
delete profileRequest.myValue;
return profileRequest;
});
var responseTransformer = httpTransformer.appendTransform($http.defaults.transformResponse, function(profileResponse) {
profileRequest.myValue = myMap[profileRequest.myKey];
delete profileRequest.myKey;
return profileResponse;
});
I prepend a transformer to the default request transformers and append a transformer to the default response transformers. My question is, is there a better way to do this? Perhaps using interceptors, as documented here, instead? If so, how?
I think your solution is fine but if you want an alternative, you can intercept specific requests like so. HTTP interceptors are mostly useful for handling global HTTP requests/responses (auth, error handling, etc.).
In any case, the "response" payload should be taken cared of from the API/server-side.
$provide.factory('userProfileInterceptor', function() {
return {
request: function(config) {
if (config.url.indexOf('/users/profile') >=0){
if (config.params.myValue) delete config.params.myValue;
}
return config;
},
response: function(response) {
if (response.config.url.indexOf('/users/profile') >=0){
delete response.data.myKey;
}
return response;
}
};
});
$httpProvider.interceptors.push('userProfileInterceptor');

Laravel 4 can't get data from Angular Ajax

I am trying to develop my application in Laravel 4 and Angular JS, my application allows user to retrieve their Information through the system via Text Change.
Angular is used to pass data input from the user to Laravel which in turn retrieves the Information from the Database.
However Laravel is unable to retrieve the data passed from Angular.
View
<div data-ng-controller="ReservationController">
<input id='ERI' type='text' data-ng-model="scanRID" data-ng-change="queryRes()" name='exampleInput' maxlength='3' />
</div>
Angular Factory
app.factory('exampleFactory', function($http) {
var factory = {};
factory.getExample = function(scanRID) {
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param(scanRID)
});
};
return factory;
});
Angular Controller
app.controller('exampleController', function($scope, $http, exampleFactory) {
$scope.queryRes = function() {
if($scope.scanRID.length == 3) {
exampleFactory.getExample($scope.scanRID)
.success(function (data) {
// Do Something Here
})
.error(function (data) {
console.log(data);
});
}
};
});
Laravel 4 Routes
Route::get('Example', 'ExampleController#show');
Laravel 4 ExampleController
class ExampleController extends \BaseController {
public function show()
{
$id = Input::get('scanRID'); // This here might be wrong. It's always empty!
$data = ExampleModel::find($id); // Able to query and retrieve.
return Response::JSON($data); // Returns empty.
}
}
Laravel 4 ExampleModel
class ExampleModel extends Eloquent {
// The id of this table is what I want, but I can't retrieve it.
protected $fillable = ['id', 'ExampleData1', 'ExampleData2'];
protected $table = 'exampleTable';
}
I have searched everywhere for a solution, it seems that everyone is able to successfully make the Ajax call. I think there is something that I am missing out that I am unaware about.
I have also tried setting CSRF Token, but however, I do not think that is an issue. So my last resort is to turn to the experts and hope someone is able to help me.
On a side note, I am fairly new to Laravel and Angular, so if you do post a solution, please explain to me the issue as I would like to learn more about Angular and Laravel.
Thank you for reviewing my issue.
You are not passing the value of scanRID by scanRID parameter instead pass only the value without parameter. So you are try to get the value from scanRID using Input::get('scanRID'); but without having scanRID parameter. that should be the case ur not getting the value :)
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param({scanRID:scanRID}) //Change Here
});
OR
return $http({
method: "GET",
url: LARAVEL_CONTROLLER + 'Example',
params: {scanRID:scanRID} //Change Here
);
change like this

How to specify different URLs for collections vs. single records with ngResource

I am trying to use ngResource to consume a firebase rest API. This API uses slightly different URLs for retrieving collections vs individual records. For example, I have a collection that I access with:
https://angular-resource-test.firebaseIO.com/systems.json
But if I want to access an individual system I use:
https://angular-resource-test.firebaseIO.com/systems/1.json
How do I specify a parameter-based URL for this in the resource declaration?
Normally I would do something like
app.factory('System', ['$resource', function($resource) {
var System = $resource(
'https://angular-resource-test.firebaseIO.com/systems/:id.json',
{id: "#id"},
{update: {method: "PUT"}}
);
return System;
}]);
But that fails for the collection because it lacks the trailing .json. Alternatively, when I specify a URL that works for the collection, the case of selecting individual rows fails. See this jsfiddle for an example:
http://jsfiddle.net/D5E6w/15/
There are 2 methods that send an HTTP GET in a resource.
get : Expect to receive an object
query : Expect to receive an array
Your api isn't very restful and because of this, you will need 2 resources to do what you want since they use different URI (see Carl's answer). I dont know if you can edit your REST service, but the good way to do it would be :
https://angular-resource-test.firebaseIO.com/systems/ for a query (expect an array)
https://angular-resource-test.firebaseIO.com/systems/:id for a get. (expect an object)
With this service, you could use your resource :
var System = $resource(
'https://angular-resource-test.firebaseIO.com/systems/:id',
{id: "#id"},
{update: {method: "PUT"}}
);
You would do your calls like this :
var systems = System.query({});
var system = System.get({id:1});
Your problem should be solved by using isArray: true for your query and get methods for System and SystemType. It has to do with how your json is formatted on the server-side. See angular docs for expanded discussion. http://jsfiddle.net/D5E6w/24/
{
update: { method: "PUT" },
query: { method: "GET", isArray: true },
get: { method: "GET", isArray: true }
}
Here is a working example with both a collection of systems and a single record. Note the isArray value for each. http://jsfiddle.net/D5E6w/51/
here is my solution with replacing the entire $resource URL. I needed that because I am using HAL Rest response and when paginating for instance, I wanted to replace the entire URL and not only params in it.
app.factory('$rest', ['$resource', 'HALParser', function($resource, HALParser) {
return function($url) {
$url = ($url == null) ? 'http://localhost:8000/:type' : $url;
return $resource($url, {type: ''}, {
update: { method:'PUT' },
get : {
method: 'GET',
transformResponse: [function(data) {
return (new HALParser()).parse(angular.fromJson(data));
}]
}
});
}
}]);
and then,
app.controller('VendorsController', ['$scope', '$rest',
function($scope, $rest) {
$scope.data = $rest().get({type: 'vendors'});
$scope.create = function() {
$rest().save({type: 'vendors'}, {name: $scope.item});
};
$scope.load = function($item) {
console.log($item);
};
$scope.page = function($url) {
$scope.data = $rest($url).get();
};
}]);
I simply wrapped $resource return from my service info a function with an argument URL.

AngularJS: How to send auth token with $resource requests?

I want to send an auth token when requesting a resource from my API.
I did implement a service using $resource:
factory('Todo', ['$resource', function($resource) {
return $resource('http://localhost:port/todos.json', {port:":3001"} , {
query: {method: 'GET', isArray: true}
});
}])
And I have a service that stores the auth token:
factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
return tokenHandler;
});
I would like to send the token from tokenHandler.get with every request send via the Todo service. I was able to send it by putting it into the call of a specific action. For example this works:
Todo.query( {access_token : tokenHandler.get()} );
But I would prefer to define the access_token as a parameter in the Todo service, as it has to be sent with every call. And to improve DRY.
But everything in the factory is executed only once, so the access_token would have to be available before defining the factory and it cant change afterwards.
Is there a way to put a dynamically updated request parameter in the service?
Thanks to Andy Joslin. I picked his idea of wrapping the resource actions. The service for the resource looks like this now:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
return resource;
}])
As you can see the resource is defined the usual way in the first place. In my example this includes a custom action called update. Afterwards the resource is overwritten by the return of the tokenHandler.wrapAction() method which takes the resource and an array of actions as parameters.
As you would expect the latter method actually wraps the actions to include the auth token in every request and returns a modified resource. So let's have a look at the code for that:
.factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
// wrap given actions of a resource to send auth token with every
// request
tokenHandler.wrapActions = function( resource, actions ) {
// copy original resource
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) {
tokenWrapper( wrappedResource, actions[i] );
};
// return modified copy of resource
return wrappedResource;
};
// wraps resource action to send request with auth token
var tokenWrapper = function( resource, action ) {
// copy original action
resource['_' + action] = resource[action];
// create new action wrapping the original and sending token
resource[action] = function( data, success, error){
return resource['_' + action](
angular.extend({}, data || {}, {access_token: tokenHandler.get()}),
success,
error
);
};
};
return tokenHandler;
});
As you can see the wrapActions() method creates a copy of the resource from it's parameters and loops through the actions array to call another function tokenWrapper() for every action. In the end it returns the modified copy of the resource.
The tokenWrappermethod first of all creates a copy of preexisting resource action. This copy has a trailing underscore. So query()becomes _query(). Afterwards a new method overwrites the original query() method. This new method wraps _query(), as suggested by Andy Joslin, to provide the auth token with every request send through that action.
The good thing with this approach is, that we still can use the predefined actions which come with every angularjs resource (get, query, save, etc.), without having to redefine them. And in the rest of the code (within controllers for example) we can use the default action name.
Another way is to use an HTTP interceptor which replaces a "magic" Authorization header with the current OAuth token. The code below is OAuth specific, but remedying that is a simple exercise for the reader.
// Injects an HTTP interceptor that replaces a "Bearer" authorization header
// with the current Bearer token.
module.factory('oauthHttpInterceptor', function (OAuth) {
return {
request: function (config) {
// This is just example logic, you could check the URL (for example)
if (config.headers.Authorization === 'Bearer') {
config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken);
}
return config;
}
};
});
module.config(function ($httpProvider) {
$httpProvider.interceptors.push('oauthHttpInterceptor');
});
I really like this approach:
http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app
where the token is always automagically sent within the request header without the need of a wrapper.
// Define a new http header
$http.defaults.headers.common['auth-token'] = 'C3PO R2D2';
You could create a wrapper function for it.
app.factory('Todo', function($resource, TokenHandler) {
var res= $resource('http://localhost:port/todos.json', {
port: ':3001',
}, {
_query: {method: 'GET', isArray: true}
});
res.query = function(data, success, error) {
//We put a {} on the first parameter of extend so it won't edit data
return res._query(
angular.extend({}, data || {}, {access_token: TokenHandler.get()}),
success,
error
);
};
return res;
})
I had to deal with this problem as well. I don't think if it is an elegant solution but it works and there are 2 lines of code :
I suppose you get your token from your server after an authentication in SessionService for instance. Then, call this kind of method :
angular.module('xxx.sessionService', ['ngResource']).
factory('SessionService', function( $http, $rootScope) {
//...
function setHttpProviderCommonHeaderToken(token){
$http.defaults.headers.common['X-AUTH-TOKEN'] = token;
}
});
After that all your requests from $resource and $http will have token in their header.
Another solution would be to use resource.bind(additionalParamDefaults), that return a new instance of the resource bound with additional parameters
var myResource = $resource(url, {id: '#_id'});
var myResourceProtectedByToken = myResource.bind({ access_token : function(){
return tokenHandler.get();
}});
return myResourceProtectedByToken;
The access_token function will be called every time any of the action on the resource is called.
I might be misunderstanding all of your question (feel free to correct me :) ) but to specifically address adding the access_token for every request, have you tried injecting the TokenHandler module into the Todo module?
// app
var app = angular.module('app', ['ngResource']);
// token handler
app.factory('TokenHandler', function() { /* ... */ });
// inject the TokenHandler
app.factory('Todo', function($resource, TokenHandler) {
// get the token
var token = TokenHandler.get();
// and add it as a default param
return $resource('http://localhost:port/todos.json', {
port: ':3001',
access_token : token
});
})
You can call Todo.query() and it will append ?token=none to your URL. Or if you prefer to add a token placeholder you can of course do that too:
http://localhost:port/todos.json/:token
Hope this helps :)
Following your accepted answer, I would propose to extend the resource in order to set the token with the Todo object:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
resource.prototype.setToken = function setTodoToken(newToken) {
tokenHandler.set(newToken);
};
return resource;
}]);
In that way there is no need to import the TokenHandler each time you want to use the Todo object and you can use:
todo.setToken(theNewToken);
Another change I would do is to allow default actions if they are empty in wrapActions:
if (!actions || actions.length === 0) {
actions = [];
for (i in resource) {
if (i !== 'bind') {
actions.push(i);
}
}
}

Categories

Resources