I am newbie on angularjs, i have some problems about promise using with ng filling.
I want to access filled html data with javascript. But the data on the page can be change dynamically.
When click some button. It should be fill angular with changable data. Then will take the recent filled html source.
array.forEach( function (id) {
var promise = $http.post('http://postpagedomain.aspx?id=' + id).then(onComplete, onError);
promise.then(function () {
var html = $('#divframe').html();
}
);
});
var onComplete = function (response) {
$scope.Info = response.data;
}
Bu it's not taking recent data cause of synchronous problem. How can i handle it?
Instead of taking response of your http call in a variable, directly make http call. Something like:
$http.post('http://postpagedomain.aspx?id=' + id).then(onComplete, onError);
So, this will call the onComplete method when API returns success and write all your logic of actions to be performed once API call gets success in the function body of onComplete.
Also include a function of onError in your code to get any exceptions thrown by the API.
Your code can look something like:
array.forEach( function (id) {
$http.post('http://postpagedomain.aspx?id=' + id).then(onComplete, onError);
});
function onComplete(response) {
$scope.Info = response.data;}
Related
I am trying to build an AngularJS single page application with Java Spring as rest service.
Currently my controller allows me to save all the filled data to the back end which I have something like this in my code:
$scope.$on('$destroy', function () {
personalDataService.saveData.query({}, {
personalData: $scope.personalData
}, function (res) {
console.log(res);
});
});
In another page, I loaded the part of the data from the same table in order to do some validation, for example checking if user is female to determine she needs to fill another form.
The problem is while I am leaving this page, the promise for save is not return yet, the second page has been loaded and the data is not completed. If I refresh the second page again then everything is back to normal
Is there any way to make sure all the promise has been return before destroy/ leaving this page?
Once the promise is resolved, emit a event.
function saveFields() {
var deferred = $q.defer();
// in your save fields function, resolve the promise on success save or reject the promise on error.
onSaveCall(args, onSuccess, onFail);
var onSucess = function() {deferred.resolve()};
return deferred.promise;
}
$scope.$on('destroy', function () {
onSaveCall.then(function () {
// Here your save fields is resolved then write your code to emit or notify.
})
})
If you have more than one promise use promises.all https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Build an array of all your promises running (for example, by storing them in a service, in the $rootScope or via events) and wait for the completion by using $q.all():
$scope.$on('$destroy', function () {
$q.all($rootScope.promises).then(function(){
console.log('all promises finished');
});
}
In Angular I've made a service to retrieve data with a $http.get call from a json file (in my code: file4.json).
In the success call the data is passed into the array 'bmlService.items'. The console.log shows things went well.
However, this array is empty outside the $http.get function.
How can I pass the data outside this function?
This is my code:
app.service('bmlService', function($http){
var bmlService = {};
bmlService.items = [];
$http.get("file4.json")
.success(function(data){
bmlService.items = data;
console.log("inside succes call: ", bmlService.items);
})
.error(function(data, status){
alert("Something went wrong...");
});
console.log("outside http.get: ", bmlService.items);
return bmlService;
});
However, this array is empty outside the $http.get function. How can I pass the data outside this function?
You cannot because the AJAX call is asynchronous and this data is available only after the callback has executed - which can happen at a much later stage after firing the AJAX call. So if you want to pass it to the outside you could invoke some other function and pass the data as parameter to this function:
.success(function(data) {
bmlService.passData(data);
})
So basically you will have to redesign your code to work with callbacks instead of some sequential calls and variables that get assigned one after the other:
var bmlService = {
passData: function(data) {
// do something with the data here
}
};
I'm trying to take the response of an $http request and save it to a custom cache. I want to then use that cache to display data into the view. I thought the cache would be checked automatically on each request before fetching new data, but that doesn't seem to be working for me.
The problem I'm having is I can't seem to save the data. The following function needs to make 2 requests: articles and images.
getImages: function() {
var cache = $cacheFactory('articlesCache');
$http.get(posts)
.then(function (data) {
var articles = data;
angular.forEach(articles, function (article) {
var imageId = {id: article.image_id};
$http.post(images, imageId)
.then(function (response) {
article.image = response;
cache.put(article.url, article);
});
});
});
return cache;
}
This creates the custom cache, but there's no data in the returned object. I know now that I can't save the data this way, but I don't know why or how I would go about doing it.
Can anyone explain how storing response data works? Where, if at all, does using promises come in here?
Your return statement executes before the code in your then function does. If you want to return the cache you'll want to run everything through the $q service and then return the resolved promise.
This is probably not the best way to use $cacheFactory. Typically you'd expose your cache as a service at a higher level and then access the cache via the service where needed.
So on your main module you'd have something like this to create the cache.
.factory('cache', function ($cacheFactory) {
var results = $cacheFactory('articleCache');
return results;
})
Then where ever you need the cache you inject it into the controller and use cache.get to retrieve the data from it.
If you want to use $q to implement this, your code would look something like the code below. (Disclaimer: I've never used $q with $cacheFactory like this, so without all of your components, I can't really test it, but this should be close.)
var imageService = function ($http, $q,$cacheFactory) {
var imageFactory = {};
imageService.cache = $cacheFactory('articlesCache');
imageFactory.getImages = function () {
var images = $q.defer();
$http.get(posts)
.then(function (data) {
var articles = data;
angular.forEach(articles, function (article) {
var imageId = {id: article.image_id};
$http.post(images, imageId)
.then(function (response) {
article.image = response;
cache.put(article.url, article);
});
images.resolve(cache.get('articlesCache'))
});
});
return images.promise
app.factory('ImageService', ['$http', '$q', '$cacheFactory', imageService]);
});
I adapted the code from this answer: How to get data by service and $cacheFactory by one method
That answer is just doing a straight $http.get though. If I understand what you're doing, you already have the data, you are posting it to your server and you want to avoid making get call to retrieve the list, since you have it locally.
It seems that factory methods execution priority is the highest, so that callbacks has no data to deal with. What is the best way to make this work?
I got this kind of factory
app.factory('jsonService', function($http) {
return {
getDistricts: function(callback) {
$http.get('data/districts.json').success(callback);
},
getLocations: function(path,callback) {
$http.get('data/'+path+'.json').success(callback);
}
};
});
And controller
var app = angular.module('sandbox', []);
app.controller('sandboxCtrl',function ($scope,jsonService) {
//This one works
$scope.init1= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
})
});
};
$scope.init1();
//This one does not
$scope.init2= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
})
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
});
};
$scope.init2();
});
Here is working plunker
Angular has an implementation of promises named $q (documentation) that you should read up upon.
There is a race condition due to the async nature of http calls. Please review the updated code linked to below that shows an example of your code running (successfully) using promises to handle your two calls in succession.
So upon success of your first call it will call your second service method all without using callbacks thanks to the power of promises.
jsonService.getDistricts()
.success(function(data) {
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict)
.success(function(locationData) {
$scope.locations = locationData;
})
});
updated PLKR
Promise clarification:
The raw implementation of basic promises uses then to handle responses and promises returned from $http add additional methods (success, error) that will unpack your data from the response object that you would need to handle if your just use then.
init1() is the correct way of doing this. init2() does work because jsonService.getLocations() is getting invoked before jsonService.getDistritcs() completes. The angular $http service is asynchronous. Since jsonService.getLocations() depends on data from jsonServicd.getDistricts() you must wait until .getDistricts() completes before calling .getLocations(). One way to do that is to call .getLocations() within the .getDitricts() callback, just as you did in init1().
I am trying to call a service in angular.js through a controller on load and return a promise. I then expect the promise to be fulfilled and for the DOM to be updated. This is not what happens. To be clear, I am not getting an error. The code is as follows.
app.controller('TutorialController', function ($scope, tutorialService) {
init();
function init() {
$scope.tutorials = tutorialService.getTutorials();
}
});
<div data-ng-repeat="tutorial in tutorials | orderBy:'title'">
<div>{{tutorial.tutorialId}}+' - '+{{tutorial.title + ' - ' + tutorial.description}}</div>
</div>
var url = "http://localhost:8080/tutorial-service/tutorials";
app.service('tutorialService', function ($http, $q) {
this.getTutorials = function () {
var list;
var deffered = $q.defer();
$http({
url:url,
method:'GET'
})
.then(function(data){
list = data.data;
deffered.resolve(list);
console.log(list[0]);
console.log(list[1]);
console.log(list[2]);
});
return deffered.promise;
};
});
Inside of the ".then()" function in the service, I log the results and I am getting what I expected there, it just never updates the DOM. Any and all help would be appreciated.
getTutorials returns promise by itself. So you have to do then() again.
tutorialService.getTutorials().then(function(data){
$scope.tutorials = data;
});
Before that, $http returns a promise with success() and error().
Although you can also use then as well
Since the returned value of calling the $http function is a promise,
you can also use the then method to register callbacks, and these
callbacks will receive a single argument – an object representing the
response.
So you are correct with that.
What is your data coming from the http call look like? Your code works - I created a version of it here http://jsfiddle.net/Cq5sm/ using $timeout.
So if your list looks like:
[{ tutorialId: '1',
title : 'the title',
description: 'the description'
}]
it should work
In newer Angular versions (I think v 1.2 RC3+) you have to configure angular to get the unwrap feature working (DEMO):
var app = angular.module('myapp', []).config(function ($parseProvider) {
$parseProvider.unwrapPromises(true);
});
This allows you to directly assign the promise to the ng-repeat collection.
$scope.tutorials = tutorialService.getTutorials();
Beside that I personally prefer to do the wrapping manually:
tutorialService.getTutorials().then(function(tutorials){
$scope.tutorials = tutorials;
});
I don't know the exact reason why they removed that feature from the default config but it looks like the angular developers prefer the second option too.