I would like to have service providing a resource as in the following code:
angular.module('myApp.userService', ['ngResource'])
.factory('UserService', function ($resource)
{
var user = $resource('/api/user', {},
{
connect: { method: 'POST', params: {}, isArray:false }
});
return user;
}
Then when using the connect action, I would like to dynamically pass a HTTP header, meaning that it may change for each call. Here is an example, in the controller, please see the comment in the code :
$scope.user = UserService;
$scope.connect = function ( user )
{
var hash = 'Basic ' + Base64Service.encode(user.login + ':' + user.password);
// I would like this header to be computed
// and used by the user resource
// each time I call this function
$scope.user.headers = [{Authorization: hash}];
$scope.user.connect( {},
function()
{
// successful login
$location.path('/connected');
}
,function()
{
console.log('There was an error, please try again');
});
}
Do you know a way to do that, either directly or via a trick?
Final thoughts
Accepted answer does not fully answer the question as the headers are not totally dynamic because the factory returns actually a factory (!) which is not the case in my code.
As $resource is a factory, there is no way to make it dynamic.
I finally destroy the resource object each time the user connects. This way, I have the resource with a header computed when the user connects.
The solution provided by #Stewie is useful for that, so I keep it as accepted.
Here is how I did the connect, which can be used multiple times as the resource is destroyed/recreated when (re)connecting:
this.connect = function (user)
{
self.hash = 'Basic ' + Base64Service.encode(user.login + ':' + user.password);
console.log("CONNECT login:" + user.login + " - pwd:" + user.password + " - hash:" + self.hash);
if (self.userResource)
{
delete self.userResource;
}
self.userResource = $resource('/api/user/login', {}, {
connect: {
method: 'POST',
params: {},
isArray: false,
headers: { Authorization: self.hash }
}
});
var deferred = $q.defer();
self.userResource.connect(user,
function (data)
{
//console.log('--------- user logged in ----- ' + JSON.stringify(data));
// successful login
if (!!self.user)
{
angular.copy(data, self.user);
}
else
{
self.user = data;
}
self.setConnected();
storage.set('user', self);
deferred.resolve(self);
},
function (error)
{
self.user = {};
self.isLogged = false;
storage.set('user', self);
deferred.reject(error);
}
);
return deferred.promise;
};
Starting from angularjs v1.1.1 and ngResource v.1.1.1 this is possible to accomplish using the headers property of the $resource action object.
You may wrap your resource in a function which accepts custom headers as a parameter and returns a $resource object with your custom headers set at the appropriate action definitions:
PLUNKER
var app = angular.module('plunker', ['ngResource']);
app.controller('AppController',
[
'$scope',
'UserService',
function($scope, UserService) {
$scope.user = {login: 'doe#example.com', password: '123'};
$scope.connect = function() {
// dropping out base64 encoding here, for simplicity
var hash = 'Basic ' + $scope.user.login + ':' + $scope.user.password;
$scope.user.headers = [{Authorization: hash}];
UserService({Authorization: hash}).connect(
function () {
$location.url('/connected');
},
function () {
console.log('There was an error, please try again');
}
);
};
}
]
);
app.factory('UserService', function ($resource) {
return function(customHeaders){
return $resource('/api/user', {}, {
connect: {
method: 'POST',
params: {},
isArray: false,
headers: customHeaders || {}
}
});
};
});
I set my Services up a little differently.
angular.module('MyApp').factory('UserService',function($resource, localStorageService) {
var userResource = function(headers) {
return $resource("api/user", {},
{
get: {
method: 'GET',
headers: headers
},
create: {
method: 'POST',
headers: headers
}
}
);
};
return {
api: userResource,
get: function(userid){
var service = this;
return service.api({"token": "SomeToken"}).get({userid: userid}, function (data){
return data;
});
},
create: function(user){
var service = this;
return service.api({"token": localStorageService.get("token")}).create({user: user}, function (data){
return data;
});
}
};
});
Related
i have an error between angularjs controller and $http services
The error is saying that the privileges is not defined. i am trying to post an object to an API
any idea or help, thanks in advance
var userpermissions = angular.module("userpermissions", [])
.service("Permissions", function ($http) {
var urlBase = "/UserPermissionAPI/api";
this.save = function (url) {
return $http({
method: "POST",
url: urlBase + '/' + url,
data: privileges,
async: false,
})
};
})
.controller("userpermission", function ($scope, Permissions) {
$scope.insert = function () {
var promisePost = Permissions.delete("UserPermission/delete?staffkey=" + $scope.staffkey + '&module=' + $scope.modulecd);
promisePost.then(function (pl) {
var privileges = {
Staff_Key: $scope.staffkey, Update_Per: $scope.updates, Save_Per: $scope.saves, Delete_Per: $scope.deletes, Search_Per: $scope.searches,
Add_Admin_User: $scope.staffkeyurl, Module_Code: $scope.modulecd, Report_Per: $scope.reports
};
var promisePost = Permissions.save("UserPermission/save");
promisePost.then(function () {
toastr.success("Successfully saved");
})
}, function (err) {
console.log("Err" + err);
});
}
You are not passing previleges anywhere in your service, change it as
var privileges = {
Staff_Key: $scope.staffkey, Update_Per: $scope.updates, Save_Per: $scope.saves, Delete_Per: $scope.deletes, Search_Per: $scope.searches,
Add_Admin_User: $scope.staffkeyurl, Module_Code: $scope.modulecd, Report_Per: $scope.reports
};
var promisePost = Permissions.save("UserPermission/save", previleges);
and the method inside the service to accept previleges,
this.save = function (url,previleges) {
return $http({
method: "POST",
url: urlBase + '/' + url,
data: privileges,
async: false,
})
};
var promisePost = Permissions.save("UserPermission/save");
This line needs to be changed.
In this line if you send privileges object as a one more parameter and change your save function to accept it, then this will work. Check below.
var promisePost = Permissions.save("UserPermission/save", privileges);
this.save = function (url, privileges) {
return $http({
method: "POST",
url: urlBase + '/' + url,
data: privileges,
async: false,
})
};
I am attempting to make my code more reusable, but have come across a problem when making $http calls, if I use my normal way:
vm.loginUser = function () {
var userData = {
username: vm.userName,
password: vm.userPassword,
grant_type: "password",
client_id: "E0..."
};
console.log('userData: ', userData);
var config = {
headers: {"Content-Type": "application/x-www-form-urlencoded"},
transformRequest: function (data) {
var str = [];
for (var d in data)
if (data.hasOwnProperty(d)) str.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
return str.join("&");
}
};
console.log('config: ', config);
$http.post('https://a.n.org.uk/token', userData, config)
.then(function (response) {
console.log('Button clicked: ', response);
}, function (response) {
console.log(response.data.error_description);
}, function (response) {
console.log(response.data);
});
This works fine and doesn't give me any problems. I have made a factory that is basically the same but doesn't seem to work with giving me OPTIONS error in the console and preflight check... No 'Access-Control-Allow-Origin' header'I wondered if this is a simple fix, I am using Web API.
factory("homeResource", function ($http, $q) {
return {
getUser: getUser
};
function getUser(userData) {
var request = $http({
method: "post",
url: "https://a.n.org.uk/token",
data: userData,
config: {
headers: {"Content-Type": "application/x-www-form-urlencoded"},
transformRequest: function (data) {
var str = [];
for (var d in data)
if (data.hasOwnProperty(d)) str.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
return str.join("&");
}
}
});
return (request.then(handleSuccess, handleError));
}
function handleSuccess(response) {
return ( response.data );
}
In my Ctrl page I pass in my homeResource and call it like so homeResource.getUser(userData).then(function (res) {console.log(res);}); and get the errors mentioned. Is there a way to make this work?
try the following factory method
app.factory('homeResource', function($http) {
return {
getUser: function(userData) {
var config = {
headers: {"Content-Type": "application/x-www-form-urlencoded"},
transformRequest: function (data) {
var str = [];
for (var d in data)
if (data.hasOwnProperty(d))
str.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
return str.join("&");
}
};
return $http.post('https://a.n.org.uk/token', userData, config);
}
}
});
and use it in your controller like this.
app.controller('AppCtrl',function($scope, homeResource){
vm.loginUser = function () {
var userData = {
username: vm.userName,
password: vm.userPassword,
grant_type: "password",
client_id: "E0..."
};
homeResource.getUser(userData).success(function(res){
console.log("response is",res);
})
.error(function(err) {
console.log("err is",err);
});
}
})
Your error related to cross-origin request - in other words you send request to another domain or specific route miss Access-Control-Allow-Origin server header.
Check ports, http/https and domain. Seems it's not related to your refactor.
I have this http request, working fine.
Controller
$scope.removeRow = function (od){
var temp = "order_id=" + od.order_id + "&product_id=" + od.product_id + "&variant_id=" + od.varient_id;
var req = $http({
method: 'POST',
url: 'http://<domain name>/api2/v1/delete_item_in_order',
data: temp,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
req.then(
function (response) {
alert('success')
},
function (error) {
//$scope.details = response.data;
alert(error.message);
}
);
}
Service code to get resource object:
sampleApp.factory('Order', function ($resource) {
return $resource('http://<domain name>/api2/v1/orders/:id', {id: '#_id'}, {
'get': {method:'GET'}
});
});
Question
How to add custom method removeRow in Order service, such that I can use $resource instead of $http in $scope.removeRow() in controller?
Rather than returning a single function, you can return an object with as many methods in following way
sampleApp.factory('Order', function ($resource) {
var removeRow = function() {console.log()};
var getResource = function)() {
$resource('http://<domain name>/api2/v1/orders/:id', {id: '#_id'}, { 'get': {method:'GET'} });
}
return { removeRow : removeRow,
getResource : getResource
}
});
There is no need to specify the get method inside $resource, this is already predefined.
Factory:
sampleApp.factory('Order', function ($resource) {
return $resource('http://<domain name>/api2/v1/orders/:id', {id: '#_id'}, null}).$promise;
});
Calling Method:
$scope.removeRow = function (od){
var temp = "order_id=" + od.order_id + "&product_id=" + od.product_id + "&variant_id=" + od.varient_id;
Order.save(temp).then(function(result){
alert('success');
}, function(err){
alert(err.message);
});
};
myapp.factory('serviceName', function( $http, webStorage){
var factory = {};
var resoureurlBase=some base url;
factory.genericService = function(method, payload, methodName, callbackFn, callbackError, param) {
var httpRequest = null;
if (param && param == true) {
httpRequest = $http({
url: resoureurlBase+methodName,
method: method,
params: payload,
headers: {
'Content-Type': 'application/json'
}
});
} else {
httpRequest = $http({
url: resoureurlBase+methodName,
method: method,
data: payload,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
}
httpRequest.then(function(response) {
if (callbackFn && callbackFn.call) {
callbackFn.call(null, response);
}
},
function(response) {
if (callbackError && callbackError.call) {
callbackError.call(response);
}
});
httpRequest.error(function(data, status, headers, config) {
});
};
return factory;
});
/*
I have written service like above how can i handle in controller
i mean
how to write call back function in controller
how to inject
etc..
*/
Simple DI(dependency injection) it into your controller:-
myapp.controller('myCtrl',['$scope','serviceName',function($scope,serviceName){
// use serviceName to call your factory function
}]);
Ref:- https://docs.angularjs.org/guide/di
You need to call service like
serviceName.genericService(--parmas--).then(function(d){
//success
})
because from service serviceName, you're returning a promise that need to resolved using .then only.
Controller
var mainController = function($scope, serviceName) {
var callbackFn = function() {
console.log('Success');
}
var callbackError = function() {
console.log('Error');
}
var parameter = {
param1: 1
},
method = 'something', payload = 100, methodName = 'something';
serviceName.genericService(method, payload, methodName, callbackFn, callbackError, parameter).then(
//success function
function(data) {
//call after call succeed
},
//error function
function(error) {
//call after call error
});
};
myapp.controller('mainController', ['$scope', 'serviceName', mainController()];
Hope this could help you. Thanks.
I am trying to create a factory that will return a token to the controller. This code only works as far as getting the token from the server but does not pass the token back into the controller. The token just comes back empty inside the controller. Please advise. Thank you.
securityApp.factory("getTokenFromServer", function ($http, $q) {
var token;
function getToken(userName, password) {
var deferred = $q.defer();
$http({
method: 'POST', url: 'http://localhost:62791/token', data: { username: userName, password: password, grant_type: 'password' }, transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
}
}).then(function (data) {
token = data.access_token;
deferred.resolve(token);
});
return deferred.promise;
}
return {
getToken: getToken
};
});
securityApp.controller('membersController', function ($scope, $http, getTokenFromServer) {
$scope.username = 'aharris1#test.com';
$scope.password = 'SuperPass1!';
getTokenFromServer.getToken($scope.username, $scope.password).then(function (data) {
$scope.token = data;
alert($scope.token);
$http({ method: 'GET', url: '/api/Members/?access_token=' + $scope.token, headers: { 'Authorization': 'Bearer ' + $scope.token } })
.then(function (response) {
$scope.members = response.data;
});
});
});
That is because you are not accessing the data in the right way. When using then chain of the promise the result is a combination of data, status etc.. (when opposed to success call back which breaks up pieces and give you data right up as the first argument) So i believe you should look for result.data.access_token instead of result.access_token
.then(function (result) {
token = result.data.access_token;
deferred.resolve(token);
});
And with you have you can just simplify your api method to return http promise itself rather creating a defered object:-
securityApp.factory("getTokenFromServer", function ($http, $q) {
function getToken(userName, password) {
return $http({
method: 'POST', url: 'http://localhost:62791/token', data: { username: userName, password: password, grant_type: 'password' }, transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
}
}).then(function (result) {
return result.data.access_token; //Return the data
}, function(errorResponse) {
return $q.reject(errorResponse.data);//Reject or return
});
}
return {
getToken: getToken
};
});