I am new to Angular and having hard time to understand how to get the value from a resolved promise. I used $q.all([element1.$promise, e2.$promise]). I got the element1 which is a json object. For, element2 which is a scalar containing 2.0. I tried element[1] just like element[0] but it contains no property, so I don't know how to get the value. I tried .val, .data, etc. I know both the web api and angular are getting 2.0.
resolve: {
accountResource: "accountResource",
account: function(accountResource, $stateParams, $q) {
var info = accountResource.get({ accountId: $stateParams.accountId });
var currentBalance = accountResource.getCurrentBalance({ accountId: $stateParams.accountId });
return $q.all([info.$promise, currentBalance.$promise]);
}
vm.account = account[0];
vm.currentBalance = account[1];
resource function
function accountResource($resource) {
var webApiUrl = "https://localhost:44301";
return $resource(webApiUrl + '/api/account/:accountId', {}, {
get: {
method: 'GET'
},
query: {
method: 'GET',
isArray: true,
url: webApiUrl + '/api/account/search/:queryMethod/:queryString'
},
getCurrentBalance: {
method: 'GET',
url: webApiUrl + '/api/account/getcurrentbalance/:accountId'
}
});
}
You can call $q.all likes this (with dictionary instead of array):
return $q.all({info: info.$promise, currentBalance: currentBalance.$promise})
And after that get information in this way:
vm.account = account.info;
vm.currentBalance = account.currentBalance;
Try to rewrite your resource as this:
function accountResource($resource) {
var webApiUrl = "https://localhost:44301";
// Add second parameter {accountId: "#accountId"}
return $resource(webApiUrl + '/api/account/:accountId', {accountId: "#accountId"}, {
get: {
method: 'GET'
},
query: {
method: 'GET',
isArray: true,
url: webApiUrl + '/api/account/search/:queryMethod/:queryString'
},
getCurrentBalance: {
method: 'GET',
url: webApiUrl + '/api/account/getcurrentbalance/:accountId'
}
});
}
Updated
You can add transformResponse property to your getCurrentBalance function in accountResoure. And transform your value from API to json format.
getCurrentBalance: {
method: 'GET',
url: webApiUrl + '/api/account/getcurrentbalance/:accountId',
transformResponse: function (balanceFromApi) {
return { balance: balanceFromApi };
}
}
And finally you can get information, likes this:
vm.account = account.info;
vm.currentBalance = account.balance;
I tested it with $httpBackend service and everything is fine now. Hope this helps.
Related
I am new to AngularJS and I have a question here.
I am using $resource for my CRUD actions.
I currently have the code like this,
angular.module("dopAngular.services")
.factory("UserRoleService", ["$resource",
function ($resource) {
return $resource("api/UserRoleApi", {}, {
query: { method: "GET", isArray: true },
create: { method: "POST" },
get: { method: "GET" },
remove: { method: "DELETE" },
update: { method: "PUT" }
});
}]);
//below is the code in my controller
UserRoleService.query(function (data) {
vm.UserRoleLookups = data;
});
I would like to make my UserRoleService generic, which means I don't want to provide the specific URL for the API in the factory level.
I now modify my code a little bit,
angular.module("dopAngular.services")
.factory("UserRoleService", ["$resource",
function ($resource, url) {
return $resource(url, {}, {
query: { method: "GET", isArray: true },
create: { method: "POST" },
get: { method: "GET" },
remove: { method: "DELETE" },
update: { method: "PUT" }
});
}]);
My question is what I should do in my controller?
So, instead of directly returning $resource, we can encapsulate it with a function that accepts url as a param.
Something like this:
myApp.factory('UserRoleService', function($resource) {
return {
query: function(url) {
return $resource(url, {}, {
query: {
method: "GET",
isArray: true
},
get: {
method: "GET"
}
});
}
}
});
Now, in controller, you can call it like:
UserRoleService.query('//httpbin.org').get()
example fiddle
i have a resource like:
angular.module('mymodule')
.factory('someResource', someResource);
function someResource($resource) {
return $resource('/something', {}, {
find : {method: 'GET', isArray: true, transformResponse: convertFn},
create: {method: 'POST', isArray: true, transformResponse: convertFn},
update: {method: 'PUT', isArray: true, transformResponse: convertFn},
});
function convertFn(){
//...
}
}
is it possible to not to copypaste the transformResponse in every type of request? Define some default transformResponse?
possible solution just to modify the definition object by adding the property programmatically, but such solution looks hard to maintain.
angular.module('mymodule')
.factory('someResource', someResource);
function someResource($resource) {
var types = {
find : {method: 'GET', isArray: true},
create: {method: 'POST', isArray: true},
update: {method: 'PUT', isArray: true},
}
//add transform to each, es6
Object.keys(types).forEach(k => types[k].transformResponse = convertFn)
return $resource('/something', {}, types);
function convertFn(){
//...
}
}
edit
thx to georgeawg for the idea
another way could be: write a wrapper function for defaults like:
angular.module('mymodule')
.factory('someResource', someResource);
function someResource($resource) {
var types = {
find : defaults({method: 'GET'}),
create: defaults({method: 'POST', isArray: false}),
update: defaults({method: 'PUT'}),
}
return $resource('/something', {}, types);
function convertFn(){
//...
}
function defaults(opts) {
return Object.assign({
isArray: false,
transformResponse: convertFn
}, opts)
}
}
is there some cleaner solution for it?
How about:
angular.module('mymodule')
.factory('someResource', someResource);
function someResource($resource) {
return $resource('/something', {}, {
find : action('GET'),
create: action('POST'),
update: action('PUT')
});
function action(method) {
return { method: method,
isArray: true,
transformResponse: convertFn
};
}
function convertFn(){
//...
}
}
Using a Response Interceptor
Since the $resource uses the $http service under the hood, a response interceptor can transform responses:
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
'request': function(config) {
//
},
'response': function(response) {
if (response.config.url.startsWith("/something") {
response.data = convertFn(response.data);
};
return response;
function convertFn(data) {
//return new data
}
}
});
});
Have you looked at angular $http interceptors? ngResource would honor a response interceptor function. Here's a post that details usage.
It is possible to have a very generic base $resource and inherit it.
I found the answer on a SO post that i can't found anymore. If someone found it edit my post to add it.
Here is the code that I use :
angular.baseResourceServiceMaker = function(service){
return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint', 'Utils',
function($injector, $resource,TypeService, $http, _, BackEndpoint, Utils){
this.restUrl = BackEndpoint+'/rest/';
this.baseName = '';
this.resource = null;
// from angular-resource
var toString= function() {
var value = [];
_.forEach(this, function(e) {
value.push('' + e);
});
return '[' + value.join(', ') + ']';
};
var isObject = function isObject(value) {
// http://jsperf.com/isobject4
return value !== null && typeof value === 'object';
};
var isFile = function(obj) {
return toString.call(obj) === '[object File]';
}
var isFormData = function(obj) {
return toString.call(obj) === '[object FormData]';
}
var isBlob = function(obj) {
return toString.call(obj) === '[object Blob]';
}
this.defaultToJson = function(d) {
return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? angular.toJson(d) : d;
};
this.typeServiceProcessData = function(d){
return TypeService.processData(d);
};
this.typeServiceProcessJsData = function(d){
return TypeService.processJsData(d);
};
this.generateTransformRequestFn = function(mapKeyValues){
return function(data){
var object = {};
_.forEach(_.keys(mapKeyValues), function(key){
Utils.setAttributeValue(object, key, Utils.getAttributeValue(data, mapKeyValues[key]));
});
return object;
}
};
this.addedMethods = {};
// use of resource will be internal, to handle transformation of data
// and so on...
this.getResource = function(){
if(this.resource == null){
var baseResourceUrl = this.restUrl + this.baseName + '/';
var baseResourceMethods = {
'get': {method:'GET', transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+':id'},
'create': {method:'POST', url:baseResourceUrl, transformRequest:[this.typeServiceProcessJsData, this.defaultToJson]},
'update' : {method:'PUT', transformRequest:[this.typeServiceProcessJsData, this.defaultToJson]},
'search': {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+'search/(:search)/:offset/:limit/:order',
params: {offset:0, limit:50, order:"creationDate=asc"}
},
'custom_search': {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+':prefix/search/(:search)/:offset/:limit/:order',
params: {search:'pk=gt=0',offset:0, limit:50, order:"creationDate=asc"}
},
'list': {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+'search/(pk=gt=0)/0/50/creationDate=asc'
},
'delete': {method:'DELETE'}
};
_.forEach(_.keys(this.addedMethods), function(key){
baseResourceMethods[key] = this.addedMethods[key];
}, this)
this.resource = $resource(baseResourceUrl+':id',
{id:'#pk'}, baseResourceMethods
);
}
return this.resource;
};
this.get = function(id){
this.getResource().get({id:id});
};
this.create = function(data){
this.getResource().create(data);
};
this.update = function(data){
this.getResource().update(data);
};
this.search = function(searchQuery){
this.getResource().search({search:searchQuery});
};
this.searchPaginate = function(searchQuery, offset, limit){
this.getResource().search({search:searchQuery, offset:offset, limit:limit});
};
this['delete'] = function(id){
this.getResource()['delete']({id:id});
};
// Finishes the other injections
$injector.invoke(service, this);
}];
};
Some comments about this code :
The functions isFile/isObject,... are c/c from angular.js because I keep the defaultTransformResponse from angularJS, this function use internal function that are not in my scope so I had to cc it.
I define default methods create/update/...
I have a typeService where i declare all my type and fields, so i can automatically convert type : for instance my server's date are always timestamps, so i convret it automatically to Javascript Date in transformResponse and the opposite in transformRequest.
addedMethods is used to add others method.
restUrl is the entry point of all rest services, baseName must be set by the implementation, it's the entry point for the resource. BackEndPoint is the constant that define the contextPath of my application.
Example of usage :
.service('ArticleService',angular.baseResourceServiceMaker(['$http', function($http){
this.baseName = 'article';
var baseResourceUrl = this.restUrl + this.baseName + '/';
this.addedMethods.root ={
method:'GET', isArray:true,
transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+'root'
};
}]))
How can I define a dynamic factory in such a way that the URL is passed from the controller without using $rootScope?
.factory('getData', function ($resource,$rootScope) {
return $resource($rootScope.url, {id:'#id'}{
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET',
isArray: true,
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
}
});
})
Controller
var data = [];
$rootScope.url='/userDetails/userId?userId=userID'
getData.get({id:'123'}).$promise.then(function(data){
angular.forEach(data,function(dataVal){
//
},data)
You should create a function in your factory and then pass the URL and id as parameters to that function when you invoke it.
For your code the factory should look something like this:
.factory('getData', function($resource, $rootScope) {
return {
query: function(url, id){
return $resource(url, {userId: id}, {
'query': {
method: 'GET',
isArray:true
},
'get': {
method: 'GET',
isArray:true,
transformResponse: function(data) {
console.log(data);
data = angular.fromJson(data);
return data;
}
}
}).get();
}
}
})
And then you would invoke it like this getData.query(url,id) in the controller.
i' using AngularJS v1.4.2. i have 2 html page , they have 2 controller.both controller have save event. how to use use http post method
first controller i'm calling post method given below
var promisePost = crudService.post(Countries);
promisePost.then(function (pl) {
alert("Sucessfully Inserted")
getCountry();
$stateParams.country = "";
}, function (err) {
alert("NOt Inserted")
});
second controller i'm calling post method given below
var promisePost = crudService.post(Levels);
promisePost.then(function (pl) {
alert("Sucessfully Inserted")
getLevel();
}, function (err) {
alert("NOt Inserted")
});
my app.js
myapp.service('crudService', function ($http, RESOURCES) {
//Create new record
this.post = function (Country) {
var request = $http({
method: "post",
url: RESOURCES.baseUrl + "saveCountry",
data: Country
});
return request;
}
this.post = function (Level) {
var request = $http({
method: "post",
url: RESOURCES.baseUrl + "saveLevel",
data: Level
});
return request;
}
});
but this code only take last post method.How to selecet post method properly. Anyone can helpme?
User countryPost and levelPost as follows and call those accordingly.
myapp.service('crudService', function ($http, RESOURCES) {
//Create new record
this.countryPost= function (Country) {
var request = $http({
method: "post",
url: RESOURCES.baseUrl + "saveCountry",
data: Country
});
return request;
}
this.levelPost= function (Level) {
var request = $http({
method: "post",
url: RESOURCES.baseUrl + "saveLevel",
data: Level
});
return request;
}
});
The best practice for using services is to return an object from it
myapp.factory('crudService', function ($http, RESOURCES) {
return {
saveCountry : function(){
return $http({
method: "post",
url: RESOURCES.baseUrl + "saveCountry",
data: Country
});
},
saveLevel : function(){
return $http({
method: "post",
url: RESOURCES.baseUrl + "saveLevel",
data: Level
});
}
}
});
then inject it into your controller dependencies and use it like :
crudService.saveLevel().then(function(){
//do some code here
})
Create a single post method instead and receive the url to call in it as parameter along with the data. As shown below:
this.post = function (data, remainingUrl) {
var request = $http({
method: "post",
url: RESOURCES.baseUrl + remainingUrl,
data: data
});
return request;
}
i have
$http({
url: 'http://webapi.-----UA_WebApi/GetUserAccount',
method: 'POST',
params: {Username:Username, Password:Password},
headers: { 'Content-Type': 'application/json;charset=utf-8' },
})
and in my service i wrote this method :
PostLogin: function (apiName, params) {
var fullParams = getFullParams(apiName, params);
var promise = $resource(buildUrl(apiName), {}, POST).get(fullParams).$promise;
updateAllowedFilters(promise);
return promise;
}
if anyone could help me understand what i am doing (right and wrong) pls ?
i would also like an example in how to use the angular resource for post.
the PostLogin works
PostLogin: function (apiName, params) {
var fullParams = getFullParams(apiName, params);
var promise = $resource(buildUrl(apiName), {}, POST).get(fullParams).$promise;
updateAllowedFilters(promise);
return promise;
}
.then(function (results) {
if(results.data.TotalRows==1) {}
TotalRows is undefined when debugging. but there is TotalRows in the api
thanks
var actions = {
post: {
method: 'post',
transformResponse: function(data) {
// here is your chance to change received data
return new Model(angular.fromJson(data));
}
}
};
var url = "http://postSomeData/:id/somethingElse/:name";
var parameters = { id : 1, name : "test" }
var data = { name : "test", type : "some type" };
return $resource(url, parameters, actions).post(data).$promise;