I have an array that I get from a service but in the controller I get an empty value in the forEach() function. This is the code.
Controller
Here, both 'products' and 'copyProducts' are empty. I need to work with the 'copyProducts' array into the forEach() function.
app.controller("controllerApp", function($scope, serviceApp){
var products = serviceApp.query();
$scope.copyProducts = products;
angular.forEach($scope.copyProducts, function(value, key){
console.log("object: "+value);
})
});
Service
app.factory("serviceApp", function($resource){
return $resource("products.json", {}, {
getAll: {
method: "GET",
isArray: true
}
})
})
Your code is wrong since .query() is asynchronous so it doesn't finish immediately and the result is not ready on the next line synchronously. So it needs a callback function to trigger once it's done with it's work.
serviceApp.query().$promise.then(function(res) {
$scope.products = res;
$scope.copyProducts = res;
angular.forEach($scope.copyProducts, function(item) {
console.log(item)
})
});
Alternative:
serviceApp.query({}, function(res, headers){
//etc
});
By the way, if you want to use the getAll method you have defined in your resource then you would not be using query()
serviceApp.getAll().$promise.then(function(res){}).....etc
Related
I've got an array of angularjs $http config objects:
var steps= [
{url:"http://api.com/test1", method: "GET"},
{url:"http://api.com/test2",method: "POST"},
{url:"http://api.com/test3",method: "GET"},
]
These are all API calls I need to execute in sequence. The number of this calls can vary.
I would like transform each of this object in a function executing the $http call (so then I can use that with map to obtain an array of functions).
Something like:
function transform(conf){
return $http(conf);
}
But this, obviously, executes the $http call.
You can use Array.reduce to chain the promises.
let promise = steps.reduce((promise, step) => promise.then(() => $http(step)), $q());
ES5
let promise = steps.reduce(function(promise, step){
return promise.then(function(){
return $http(step);
});
}, $q());
One option is to use the async/await pattern where you can await in the loop of request which would make them execute in a sequence.
Try it like this
app.controller('yourControllerController',
async function($scope, $http) {
var steps =
[
{ url:"http://api.com/test1", method: "GET" },
{ url:"http://api.com/test2", method: "POST" },
{ url:"http://api.com/test3", method: "GET" },
];
async function processHttpRequests(){
for(const step of steps){
var result = await $http({ method: step.method, url: step.url});
console.log('Url:' step.url + ' Result: ' + result);
}
};
await processHttpRequests();
});
I have a weird problem.
First I retrieve several calls at the same time. And save the returned data in a variable called "values"
function PrefsService($resource,PrefsResource,$q) {
var initialize = function() {
return $q
.all(
[PrefsResource.get({key:"TwentyFourHourTime"}),
PrefsResource.get({key:"DecimalTime"}),
PrefsResource.get({key:"startDayOfWeek"}),
PrefsResource.get({key:"RoundingIncrement"}),
PrefsResource.get({key:"RoundingOption"})
]
)
.then(function(values) {
return values
})
I use this piece of code in controller to see the returned value:
PrefsService
.initialize()
.then(function(values) {
console.log("values",values);
console.log("values[0]",values[0]);
console.log("values[0].result",values[0].result);
})
I want to use "values[0].result" get the result object. But it always gives me a value of "undefined".
Why?
Thx
This syntax looks weird:
return {
values
}
Its basically an object literal with a property name, but no value. In any case what you are tagging on to the initial all is unnecessary:
.then(function(values) {
return {
values
}
})
Just remove that part.
The returned values are promises, use them as promises like you are supposed to:
PrefsService
.initialize()
.then(function(values) {
values.map(function(valuePromise) {
valuePromise.then(function(value) {
console.log(value);
});
});
});
The most straightforward way would be to return the actual values, not the promises:
function PrefsService($resource,PrefsResource,$q) {
var initialize = function() {
return $q
.all([
PrefsResource.get({key:"TwentyFourHourTime"}),
PrefsResource.get({key:"DecimalTime"}),
PrefsResource.get({key:"startDayOfWeek"}),
PrefsResource.get({key:"RoundingIncrement"}),
PrefsResource.get({key:"RoundingOption"})
])
.then(function(values) {
var returnValues = [];
values.forEach(function(v) {
v.then(function(a) {
returnValues.push(a);
})
});
return returnValues;
})
};
return {
initialize:initalize;
}
}
PrefsService
.initialize()
.then(function(values) {
console.log(values); //an array of the actual values, not the promises
})
From the API I'm working on I need to take 2 different lists and I need to take in chunks of 20 items to avoid server timeouts.
What I built actually is this:
Items1.query().$promise
.then(function (data) {
$scope.items1 = data.list;
return Items2.query().$promise;
})
.then(function (data) {
$scope.items2 = data.list;
});
With this code I'm downloading the entire list of objects.
Both the query return:
{
list: [...],
next: true,
limit: 20,
last: 20
}
Basically it is a pagination system.
Both services are like this:
App.factory('Items1', ['$resource',
function($resource) {
return $resource('items1/:item1Id', { storeId: '#id'
}, {
query: {
method: 'GET',
isArray: false
},
update: {
method: 'PUT'
}
});
}
]);
I don't really know how to make recursive function with $resource in order to push those items in chunks of 20.
I wrote an example jsfiddle to show recursive promises. Converting it for your example, it would looks something like:
function getList(resource, list, last) {
return resource.query({last: last}).$promise.then(function(data){
list = list.concat(data.list);
if (data.next) {
return getList(resource, list, data.last);
}
return list;
});
}
getList(Items1, [], 0).$promise.then(function(list) {
$scope.items1 = list;
});
getList(Items2, [], 0).$promise.then(function(list) {
$scope.items2 = list;
});
You would need to modify your angular resources to allow you to pass the last parameter in the API call. I'm assuming that the API, if provided with that parameter, will return the next section starting from it.
I have the following service method:
ResourcesService.prototype.list = function ()
{
var deferred = q.defer();
var settings = fetchSettings();
restService.getAll(resourceName, settings)
.then(function (response) {
deferred.resolve(response.data, {
count: response.headers('cr_count'),
total: response.headers('cr_total'),
last: response.headers('cr_last')
});
}, function (error) {
deferred.reject(error.statusText);
});
return deferred.promise;
}
As you can see I am passing two values to deferred.resolve, which are response.data and a metadata object.
Up in the call stack I have:
//"scenes" is an object that inherits from ResourcesService
scenes
.withLanguage('en-us')
.sort('creation')
.size(2)
.list()
.then(function (firstPage, metadata) {
//firstPage is the "response.data" from previous method
//metadata is undefined, but should be an object with all the values from the headers
});
Why is metadata undefined? I debugged ResourcesService and the headers are being read just fine, but the object passed is as argument to deferred.resolve is not being delegated to my callback function.
Does deferred.resolve support only one argument to be passed to the callback? Do I have to put this metadata in the same object along with the response?
You can't pass more then one parameter into then callback, only the one is expected and considered. What you can do however is to resolve your promise with an object. For example:
ResourcesService.prototype.list = function () {
var settings = fetchSettings();
return restService.getAll(resourceName, settings).then(function (response) {
return {
data: response.data,
metadata: {
count: response.headers('cr_count'),
total: response.headers('cr_total'),
last: response.headers('cr_last')
}
};
}, function (error) {
throw new Error(error.statusText);
});
}
Note, that I also fixed deferred anti-pattern in your code, you don't need dummy deferred object, because you already have promise you can return.
Then you would use it like this:
scenes
.withLanguage('en-us')
.sort('creation')
.size(2)
.list()
.then(function (response) {
var firstPage = response.data,
metadata = response.metadata;
});
While #dsfq is right about not resolving with more than one argument, if you're using q, you could also wrap your resolved values in an array and use .spread() instead of .then() to split them across arguments.
Created:
.then(function (response) {
// Resolve with a single array
deferred.resolve([response.data, {
count: response.headers('cr_count'),
total: response.headers('cr_total'),
last: response.headers('cr_last')
}]);
}
Consumed:
scenes
.withLanguage('en-us')
.sort('creation')
.size(2)
.list()
// .spread() instead of .then()
.spread(function (firstPage, metadata) {
// Works as expected
});
I am trying to do a http get in angular like this:
$http
.get(some url, {
params: {
description: params.description,
from: params.from,
to: params.to
}
})
.success(function (data,status) {
$scope.info_show = data
});
here's the thing, the params object parameters are set based in user input, so if the user hasn't inputted anything for the from property (some input text fielt) it will be undefined.
my problem is that I can't just pass the params object, because it doesn't filter and if I pass undefined on even one of the properties then the web service by default will return everything, could someone show me a way of dynamically doing this?
You could use a function to filter the params object.
This one receives a list of params/properties you want to filter and the src object to filter from:
var filterParams = function(params, src) {
var result = {};
angular.forEach(params, function(p) {
if (angular.isDefined(src[p])) {
result[p] = src[p];
}
});
return result;
};
And then use it like this:
$http.get(some url, {
params: filterParams(['from', 'to', 'description'], params)
})
.success(function (data,status) {
$scope.info_show = data
});