I need to assign data to ng-csv from api call.
I tried this. but didn't worked
script:
$scope.getArray = function () {
var temp;
$http.get(Config.serviceUrlBase + '/abc/ExportToCSV').success(function(data, status, headers, config) {
temp = data;
})
return temp;
};
//here data is string separated by comma and new line for csv format.
aspx file:
<Button ID="ExportToCSV" ng-csv="getArray" filename="test.csv" lazy-load="true"
>ExportToCSV</Button>
Am I missing anything?
The temp variable is assigned to asynchronously, which means that return temp; actually returns undefined.
The ng-csv attribute accepts promises, so you should use $q:
$scope.getArray = function () {
var deferred = $q.defer();
$http
.get(Config.serviceUrlBase + '/abc/ExportToCSV')
.success(function(data, status, headers, config) {
deferred.resolve(data);
});
return deferred.promise;
};
Note: $q is a service that you can inject in your controller or wherever it is you can inject things.
You are missing '()'.
ng-csv="getArray()"
I have the same issue and it's solved by the following:
$scope.getArray = function () {
var temp = $http.get(Config.serviceUrlBase + '/abc/ExportToCSV').success(function(data, status, headers, config) {
return data;
})
return temp;
};
The issue was because temp variable by default is undefined. So, You should wait till the variable has returned value from promise.
To solve this you need to use a promise with your lazy load, but you must also include the resolved response as an array as your data is in JSON format.
Leaving the response in JSON format led to 0 being loaded only under the first column when we had a collection being returned.
In the below AngularJS example we had a collection we wanted to store in the array (i.e. rows of data):
$scope.getArray = function() {
var deferme = $q.defer();
$http.get(url).success(function(data) {
var myArr = [];
for(var i=0; i < data.length; i++) {
myArr.push([data[i].value1, data[i].value2, etc...]);
}
deferme.resolve(myArr);
});
return deferme.promise;
}
the value of the ng-csv directive should be an expression, a value or a promise.
So you can assign $scope.getArray
myapp.controller('myctrl', function ($scope) {
$http.get(url)
.success(function (data) {
$scope.getArray = data;
});
});
AngularJS is watching for scope variable to be updated.
Related
Am looping over an array and making async calls to an api the returned data need to me merged with a different array the problem am facing when the merge occurs some of the promises have not being resolved yet so the resulting array after the merge is missing data . any ideas how to go about this . (am new to Angular).
the array am looping through has at least 200 elements (I don't know the size of the array in advance) i get the id from each element and i call this service:
for (i = 0; i < list.length; i++) {
service.getRawHtml(list[i].service_object_id)
.then(function(res){
temp=htmlToArray(res);
htmlDataList.push(temp);
}, function(err){
// error
})
}
service.getRawHtml=function (id){
var defer = $q.defer();
$http({
method: 'GET',
url: 'http://wlhost:50000/'+id
}).success(function (response) {
defer.resolve(response);
}).error(function (data, status) {
console.log("Failure");
defer.reject(err);
});
return defer.promise;
}
Thanks in advance.
Use $q.all - From the documentation:
Combines multiple promises into a single promise that is resolved when
all of the input promises are resolved.
Example usage:
$q.all([
getPromise(),
getPromise(),
getPromise()
]).then(function(value) {
$scope.result1 = value[0];
$scope.result2 = value[1];
$scope.result3 = value[2];
}, function(reason) {
$scope.result = reason;
});
As #ndoes suggests, this can be done with $q.all(), which takes an array of promises and resolves when all of them are resolved, or rejects if any of them is rejected.
You're calling the asynchronous function inside the for loop which will execute, but as soon as all of the function calls have been made, the code will continue synchronously. There wont be any magical awaiting for all the promises to resolve.
What you instead should do is to push all of the returning promises to an array, then use $q.all() to await them all.
//Declare an array to which we push the promises
var promises = [];
for (i = 0; i < list.length; i++) {
//Call the service and push the returned promise to the array
promises.push(service.getRawHtml(list[i].service_object_id));
}
//Await all promises
$q.all(promises)
.then(function(arrayOfResults){
//Use your result which will be an array of each response after
//it's been handled by the htmlToArray function
})
.catch(function(err){
//Handle errors
});
I took the opportunity to refactor your code at the same time, as it is implementing the deferred anti-pattern. Since $http already returns a promise, there's no need to create and return a new one. Simply return it right away like
service.getRawHtml = function (id){
return $http({
method: 'GET',
url: 'http://wlhost:50000/'+id
}).success(function (response) {
return htmlToArray(response);
}).error(function (data, status) {
console.log("Failure");
$q.reject(error);
});
}
just to clerify #nodes solution:
var promisesToWaitFor = [];
for (i = 0; i < list.length; i++) {
var promis = service.getRawHtml(list[i].service_object_id)
promisesToWaitFor.push(promis)
}
$q.all(promisesToWaitFor).then(function(resultsArray) {
var flatten = [].concat.apply([],resultsArray);
temp=htmlToArray(flatten);
htmlDataList.push(temp);
}, function(err) {
// error
});
I have a factory where I'm getting data from a server using the $http methods:
.factory('$factory', function ($q, $http, $timeout, $state, $ionicHistory, $localstorage) {
var obj = [];
var functions = {
getData: function () {
var dfd = $q.defer();
if(!obj){
$http({
url: remoteUrl+'/getdata',
method: 'POST',
data: {}
}).then(function(response) {
$timeout(function(){
obj = response.data;
dfd.resolve(response.data);
}, 2000)
}, function(response) {
}
}else{
return obj;
}
return dfd.promise;
}
}
}
So this gets the data and puts it in an object. Correct me if I'm wrong, but this method of using a factory for this type of action is so it's not tied to a controller, and can be used anywhere in my application.
With this in mind, I wish to make it so I can get the data anywhere in my app without having to query the server each time. i.e. once it's queried the server the factory saves the response to an object (like I'm doing now). But I'm having trouble accessing the data afterwards in another controller for example.
I've started to make what I think it should look like by using the if(!obj) line, but in the else statement I cant seem to just return the obj object. It throws errors as it's not returning a promise like it's expected too.
I'm not sure if I'm even along the right lines with this.
Thanks
You're returning a promise so you need to always return a promise even on the cached response, you can do it relatively easy at this level by wrapping it in a $q.when (https://docs.angularjs.org/api/ng/service/$q) which will return a promise immediately resolved.
return $q.when(obj);
Although $http service has built in caching, so you may want to take a look under the Cache section.
https://docs.angularjs.org/api/ng/service/$http
https://www.ng-book.com/p/Caching/
This should work:
Insteada assigning obj = [] assign as a null. Usually I prefer callback. You can try this code:
.factory('$factory', function($q, $http, $timeout, $state, $ionicHistory, $localstorage) {
var obj = [];
var functions = {
getData: function(cb) {
// instead of checking !obj you have to check for length or you have to set obj as null
if (obj && obj.length == 0) {
$http({
url: remoteUrl + '/getdata',
method: 'POST',
data: {}
}).then(function(response) {
obj = response.data;
cb(response.data)
}, function(response) {
}
}
else {
cb(obj)
}
}
}
}
})
// You can use callback by following code
$factory.getData(function(response){
// response will come here
})
I'm trying to create an AngularJS service, which returns data based on several HTTP requests. But i seem to just not get it to work.
The REST call works as follow:
get /index which returns an array of urls
call each of the url's, and add the result to an array
I expect that at the end of the call of the service function, that i receive a data structure containing all the data from the url's.
My current, somewhat working code uses callbacks, but even though it works in one controller, it does not in another. I want to correctly use promises, but i'm already confused with success vs then.
My service:
// Get a image
obj.getByUrl = function (imageUrl, callback) {
$http.get('https://localhost:9000' + imageUrl).success(function (data) {
callback(data);
});
}
// Get all images
obj.getAll = function(callback) {
$http.get('https://localhost:9000/1.0/images').success(function (data) {
if (data.status != "Success") {
console.log("Err");
}
var images = [];
for(var n=0; n < data.metadata.length; n++) {
var c = data.metadata[n];
obj.getByUrl(c, function(data2) {
images.push(data2.metadata);
});
}
callback(images);
});
}
i'd like to use the service in a controller resolve like this:
resolve : {
images: function(ImagesServices, $route) {
return ImagesServices.getState($route.current.params.containerName)
},
I came as far as this, but it does only return the data of the index call, not the aggregated data:
obj.getAll3 = function() {
var images = [];
var promises = [];
//var httpPromise = $http.get('https://localhost:9000/1.0/images');
var httpPromise = $http({
url: 'https://localhost:9000/1.0/images',
method: 'GET',
});
return httpPromise.success(function(data) {
var data2 = data.metadata[0];
// angular.forEach(data.metadata, function(data2) {
console.log("D11: " + JSON.stringify(data2));
//var inPromise = $http.get('https://localhost:9000' + data2)
var inPromise = $http({
url: 'https://localhost:9000' + data2,
method: 'GET',
})
.success(function (data2) {
console.log("D2: " + JSON.stringify(data2));
images.push(data2);
});
promises.push(inPromise);
// });
return $q.all(promises).then(function() {
return images;
});
});
}
Maybe someone can point me into the right direction?
This is the typical case where chaining promises, and using $q.all(), is adequate:
/**
* returns a promise of array of images
*/
obj.getAll = function() {
// start by executing the first request
return $http.get('https://localhost:9000/1.0/images').then(function(response) {
// transform the response into a promise of images
// if that's not possible, return a rejected promise
if (data.status != "Success") {
return $q.reject("Error");
}
// otherwise, transform the metadata array into
// an array of promises of image
var promises = data.metadata.map(function(imageUrl) {
return $http.get('https://localhost:9000' + imageUrl).then(function(resp) {
return resp.data;
});
});
// and transform this array of promises into a promise
// of array of images
return $q.all(promises);
});
}
This avoid the callback antipattern, and uses chaining. It's a bit long to explain here, but I wrote a blog post that should, hopefully, make the above code clear: http://blog.ninja-squad.com/2015/05/28/angularjs-promises/
I have the following scenario, I need data from a particular url. I have written a function which takes parameter 'url'. Inside the function I have the $http.get method which makes a call to the url. The data is to be returned to the calling function
var getData = function (url) {
var data = "";
$http.get(url)
.success( function(response, status, headers, config) {
data = response;
})
.error(function(errResp) {
console.log("error fetching url");
});
return data;
}
The problem is as follows, $http.get is asynchronous, before the response is fetched, the function returns. Therefore the calling function gets the data as empty string. How do I force the function not to return until the data has been fetched from the url?
Take a look at promises to overcome such issues, because they are used all over the place, in angular world.
You need to use $q
var getData = function (url) {
var data = "";
var deferred = $q.defer();
$http.get(url)
.success( function(response, status, headers, config) {
deferred.resolve(response);
})
.error(function(errResp) {
deferred.reject({ message: "Really bad" });
});
return deferred.promise;
}
Here's a nice article on promises and $q
UPDATE:
FYI, $http service itself returns a promise, so $q is not necessarily required in this scenario(and hence an anti-pattern).
But do not let this be the reason to skip reading about $q and promises.
So the above code is equivalent to following:
var getData = function (url) {
var data = "";
return $http.get(url);
}
You can use $q.all() method also to solve this problem
var requestPromise = [];
var getData = function (url) {
var data = "";
var httpPromise = $http.get(url)
.success( function(response, status, headers, config) {
data = response;
})
.error(function(errResp) {
console.log("error fetching url");
});
requestPromise.push(httpPromise);
}
in the calling function
$q.all(requestPromise).then(function(data) {
//this is entered only after http.get is successful
});
make sure to inject $q as a dependency. Hope it helps
You function seems redundant. Just use $http.get(url), since you aren't really doing anything else before you use it anyway.
var url = 'foo/bar';
$http
.get(url)
.success( function(response, status, headers, config) {
$scope.data = response;
})
.error(function(errResp) {
console.log("error fetching url");
});
Or if you need to access the promise later just assign it to variable;
var promise = $http.get(url);
// some other code..
promise.then(function(data){
//.. do something with data
});
A typical way to do what you want is like so:
var getData = function(url, callback) {
$http.get(url).success(function(response) {
callback && callback(response);
});
};
Used like:
getData('/endpoint', function(data) {
console.log(data);
});
I'm using a service in order to share data between controllers. However, the service is returning a promise with cached data even when making new requests. Depending on where the defer instance is created either live data is returned but two-way binding breaks or the two-way binding works but cached data is returned.
How can one prevent the return of a promise with cached data and keep two-way binding?
I've put up a plunker to illustrate the case: http://plnkr.co/edit/SyBvUu?p=preview and for sake of completeness, here is the troublemaking service:
app.service('myService', function($http, $q) {
// When instancing deferred here two way binding works but cached data is returned
var deferred = $q.defer();
this.get = function(userId) {
// When instancing deferred here two way binding breaks but live data is returned
//var deferred = $q.defer();
console.log('Fetch data again using id ', userId);
var url = userId + '.json';
$http.get(url, {timeout: 30000, cache: false})
.success(function(data, status, headers, config) {
deferred.resolve(data, status, headers, config);
})
.error(function(data, status, headers, config) {
deferred.reject(data, status, headers, config);
});
return deferred.promise;
};
});
UPDATE: The problem wasn't that data was cached, it was that I hadn't understood how a data was to be shared and that the shared data can not be a primitive. See my own answer below.
Since $http returns a deferred object what you are doing here is actually overkill. When I changed your service to the following it seems to work fine.
Plunker
app.service('myService', function($http, $q) {
this.get = function(userId) {
console.log('Fetch data again using id ', userId);
var url = userId + '.json';
return $http.get(url, {timeout: 30000, cache: false});
};
});
Edit
To get your controller SecondCtrl to update, the easiest thing to do, while keeping the structure of your code the same, is to broadcast the new data in an event defined in FirstCtrl using $rootScope.$broadcast and capture the broadcasted event in your other controller using $scope.$on. I've updated the Plunker and now your data is in sync.
Modified loadUserFromMyService function in FirstCtrl:
$scope.loadUserFromMyService = function(userId) {
var promise = myService.get(userId);
promise.then(
function(data) {
console.log('UserData', data);
$scope.data = data;
$rootScope.$broadcast('newData', data);
},
function(reason) { console.log('Error: ' + reason); }
);
};
Added in SecondCtrl:
$scope.$on('newData', function (evt, args) {
console.log('dataChanged', args);
$scope.data = args;
});
I came up with simplified solution to share data with the help of Luke Kende. Here is a plunk: http://plnkr.co/edit/JPg1XE?p=preview. See code below.
One important thing is that the shared object isn't a primitive. When I tried different solutions I started with declaring the shared object and assign it null, which is a no-no. Using an empty object makes it work.
var app = angular.module('plunker', []);
// Service
app.service('myService', function($http, $q) {
//object that will be shared between controllers
var serviceData = {
items: []
};
return {
data: serviceData, //pass through reference to object - do not use primitives else data won't update
get: function(url, overwrite) {
if (serviceData.items.length === 0 || overwrite){
$http.get(url, {timeout: 30000})
.success(function(data, status, headers, config) {
//could extend instead of ovewritting
serviceData.items = data;
})
.error(function(data, status, headers, config) {
serviceData.items = {status: status};
});
}
return serviceData;
},
empty: function(){
serviceData.items = [];
},
more: function(){
//do some other operations on the data
}
};
});
// Controller 1
app.controller('FirstCtrl', function( myService,$scope) {
//myService.data is not initialized from server yet
//this way don't have to always use .then() statements
$scope.data = myService.data;
$scope.getTest = function(id){
myService.get('test' + id + '.json',true);
};
$scope.addItem = function() {
$scope.data.items.push({'title': 'Test ' + $scope.data.items.length});
};
$scope.delItem = function() {
$scope.data.items.splice(0,1);
};
});
// Controller 2
app.controller('SecondCtrl', function( myService,$scope) {
//just attach myService.data and go
//calling myService.get() results in same thing
$scope.data = myService.data;
//update the the data from second url -
$scope.getTest = function(id){
myService.get('test' + id + '.json',true);
};
$scope.empty = function(){
myService.empty();
};
});