I have an app.run() block that:
uses an Angular service that uses a $http.get to get some values and sets them in the $rootScope
calls another Angular component that uses the values from the $http.get
The issue is that the code in the second step runs before the HTTP call has replied with the values in the first step. I don't want the code in the second step to call the service directly.
How can I force the second step to wait for the first step to complete?
service.js
function getSomeConfigs() {
var promise = $http.get('/blah');
$rootScope.promise = promise
.then(function () {
$rootScope.someVariable = data.someVariable;
});
}
helper.js
$rootScope.someVariable
is undefined
You must chain the two steps:
$http.get('restAddress')
.then(function(result) {
$scope.myResult = result.data;
})
.then(function() {
MyFunctionIndirectlyDependingOnPreviousResult();
});
You can even place the call to MyFunctionIndirectlyDependingOnPreviousResult() inside the first then() callback:
$http.get('restAddress')
.then(function(result) {
$scope.myResult = result.data;
MyFunctionIndirectlyDependingOnPreviousResult();
});
$HTTP service returns a promise.
So just set the return from your $http.get to a var and you can use the .then method.
e.g.
//Controller
....
//appSvc.get is a call to your .factory or .service that returns the promise
var inputUrl = "myUrl/REST/";
var promise = appSvc.get(inputUrl);
promise.then(function (data) {
//DO SOMETHING WITH Returned DATA
}
...
//SERVICE
'use strict';
module.exports = function (ngModule) {
ngModule.factory('appSvc', ['$http', function ($http) {
function epicUniverse (inputUrl) {
return $http.get(inputUrl);
}
return {
get:epicUniverse
};
}]);
};
Related
I'm trying to make a method that returns an array of objects after getting the objects from an API. The problem is that the return from the factory happens before all the calls are finished. I've tried to use $q.defer but it still sends the return before it's ready to ship.
This is what I've come up with so far.
angular.module('watchList').factory('storageService', ['$http', '$q', function ($http, $q) {
storage = {};
storage.getMovies = function () {
var movies = localStorage.getItem('movies');
var movieArray = angular.fromJson(movies);
var newArray = [];
var defer = $q.defer();
angular.forEach(movieArray, function (id) {
newArray.push($http.get(api + id));
});
$q.all(newArray).then(function (response) {
defer.resolve(response);
});
return defer.promise;
}
This is the controller that I'm trying to make the call from
angular.module('watchList').controller('watchListController', ['$scope', 'storageService', function ($scope, storageService) {
$scope.movies = storageService.getMovies();
I want the loop to finish everything before it returns the array.
You don't need to create a promise, you can just return the promise returned by the $q.all(newArray) call.
The thing is that you cannot expect to get a result synchronously when it only becomes available asynchronously. So you need to keep with using then:
storage.getMovies = function () {
var movies = localStorage.getItem('movies');
var movieArray = angular.fromJson(movies);
var newArray = movieArray.map(function (id) {
return $http.get(api + id);
});
return $q.all(newArray);
}
storageService.getMovies().then(function(movies) {
$scope.movies = movies;
// ... other code working with $scope.movies
});
Side note: the map method does what you do with forEach, but immediately returns the array, which is quite practical.
getMovies will return immediately with a promise. You need to use "then" to wait on that promise.
$scope.movies = storageService.getMovies().then((response) => ...)
I have a function in a service returns a promise. I call this function from a different service where I want to alter the result of the promise and make this new altered value available to my controllers.
angular.module('test').service("service1", function($q) {
function doSomething(something) {
//parses some data
return deferred.promise;
}
return {
doSomething: doSomething
}
});
angular.module('test').service("service2", ['service1', $q, function(service1, $q) {
var deferred = $q.defer();
var myResult = deferred.promise;
service1.doSomething("test").then(function (result) {
var alteredResult = result + "Altered"; //result I want to be accessible by my controllers
deferred.resolve(alteredResult)
});
return {
myResult: myResult
}
}]);
angular.module('test').controller('testController', ['$scope', 'service2', function ($scope, service2) {
$scope.test = service2.myResult;
}]);
It obviously didn’t work when I was initializing myResult outside of the .then() and updating it inside because myResult was passed to the controller before it was altered. This lead me to try creating another promise inside of service2.
After all of this, I get a promise in $scope.test but it ends up being a blank {} javascript object.
How can I properly store the altered result of service2 so that I can access it from my controllers?
Thanks you very much for your time. Let me know if you need additional information or if I am being unclear.
You are assigning promise to the scope variable. You should resolve the promise (promise chain) and assign the resolved value instead.
service2.myResult.then(function(result) {
$scope.test = result;
});
I am working on making my existing code a bit better and I would like to have two objects for interacting with a RESTful API.
Previously, I used $http to get get the data and return the promise. I would then .then some actions to it. Then I would requery to get additional data and repeat it. So there were several nested $http queries. I would like to avoid that if possible.
I would like to have a service (or a factory) that I can use to configure various query parameters and another object for massaging and outputting the response.
Here is what I have so far:
var solr1 = angular.module('solr', ['ngResource']);
solr1.run(function(){
console.log("Module Loaded");
});
solr1.service('solrser', function($resource,solrresult) {
console.log("In Solrser")
this.serverurl = 'url';
this.res = $resource(this.serverurl);
this.query = function (p) {
this.res.get(p).$promise.then(function(d) {
return solrresult(d);
});
}
});
solr1.factory('solrresult',function() {
return function(a) {
this.data = a;
this.ready = 0;
console.log("In Factory")
this.getdocs = function() {
console.log("Getting Data")
console.log(this.data);
return this.data.docs; //this is line 9
}
return this;
}});
Controller looks like this:
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
console.log("Start");
var res = solrser.query({'q':'potato'});
console.log(res)
console.log(res.getdocs())
}]);
The output looks like this:
Module Loaded solr_module.js:5
In Solrser solr_module.js:9
Start main_controller.js:6
undefined main_controller.js:9
TypeError: Cannot read property 'getdocs' of undefined
at new <anonymous> (.........../main_controller.js:10:21)
at Object.e [as invoke] (https://code.angularjs.org/1.3.1/angular.min.js:36:365)
at F.instance (https://code.angularjs.org/1.3.1/angular.min.js:75:91)
at https://code.angularjs.org/1.3.1/angular.min.js:58:287
at s (https://code.angularjs.org/1.3.1/angular.min.js:7:408)
at G (https://code.angularjs.org/1.3.1/angular.min.js:58:270)
at g (https://code.angularjs.org/1.3.1/angular.min.js:51:172)
at g (https://code.angularjs.org/1.3.1/angular.min.js:51:189)
at https://code.angularjs.org/1.3.1/angular.min.js:50:280
at https://code.angularjs.org/1.3.1/angular.min.js:18:8 angular.js:11339
In Factory solr_module.js:25
So the factory gets created after the controller resumes execution. What am I doing wrong here?
You have a timing issue.
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
console.log("Start");
var res = solrser.query({'q':'potato'});
console.log(res)
console.log(res.getdocs())
}]);
When you call solrser.query() it runs async to get the data. So the res is not populated with any real value until after the promise returns inside the "solrser" service. You would need to do something more like:
solrser.query({'q':'potato'},function(res) {
console.log(res);
console.log(res.getdocs());
}); // could be done as a promise as well....
Separately, you also are doing something really strange when the response is called inside the solrser service.
this.query = function (p) {
this.res.get(p).$promise.then(function(d) {
return solrresult(d);
});
}
Calling return from within the promise handler is not going to do you very much. You need to either call a callback or resolve a deferred. Something like this would work better:
this.query = function (p,callback) {
this.res.get(p).$promise.then(function(d) {
callback(solrresult(d));
});
}
Or as a promise
this.query = function (p) {
var defer = $q.defer();
this.res.get(p).$promise.then(function(d) {
defer.resolve(solrresult(d));
});
return defer.promise;
}
Or you could just pass the get(p).$promise through, etc.
UPDATE:
Since this is an async action, there will always be a promise or callback. You have to do one of:
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
var res = solrser.query({'q':'potato'},function() {
res.getDocs();
});
}]);
Or just straight callback
app.controller('app1cont', ['$scope', 'solrser', 'solrresult', function($scope,solrser,solrresult){
solrser.query({'q':'potato'},function(res) {
res.getDocs();
});
}]);
I see missing semicolon
console.log("In Solrser")
shouldbe
console.log("In Solrser");
also missing semicolon here
console.log("Getting Data")
console.log("In Factory")
in controller you have missing semicolons too.
I think this is a reason why your code don't work.
To avoid bug like missing semicolon you can use JSHint plugin in your IDE
I'm an ionic/Angular n00b and I having trouble wrapping my head around how to do this.
I have a factory defined as such:
angular.module('starter.services', [])
.factory('Calendars', function () {
var calendars;
var success = function(message) {
calendars = message;
return calendars;
};
var error = function(message) {alert("Error: " + message)};
window.plugins.calendar.listCalendars(success,error);
return {
all: function() {
return calendars;
},
get: function(calendarId) {
return calendars[calendarId];
}
}
});
And I'm trying to retrieve the calendars within my controller like this:
.controller('CalendarsCtrl', function($scope,Calendars) {
$scope.calendars = Calendars.all();
})
The factory method is being called but the results are not available until the 'success' callback is invoked so the CalendarsCtrl is always undefined.
How to solve this?
Edit - I've corrected the call within the controller. The same issue remains though, that the function does not return results until the success callback.
You will have to use a promise.
First add the dependency $q
.factory('Calendars', function ($q) {
then in all() you do this
all: function() {
var deferred = $q.defer();
window.plugins.calendar.listCalendars(function(data) {
deferred.resolve(data);
}
,function(error) {
deferred.reject(error); // something went wrong here
});
return deferred.promise;
now this will make the return after the data has been resolved (no more undefined).
One last thing, now when you get the data back at your controller you do this
var promise = Calendars.all();
promise.then(function(data) {
console.log('Success: you can use your calendar data now');
}, function(error) {
console.log('Failed for some reason:' + error);
});
You can read some more about promises here: https://docs.angularjs.org/api/ng/service/$q
I know it's hard to grasp the first time.
Angular factories are returning an object, in order to call their methods you must call them with Calendar.all() which will invoke the inner function.
My controller cannot pick up the data that processed from $http.get() in requirejs environment. Any idea how can I fix it?
UserFactory.js
define(['app'], function (app) {
app.module1.factory('UserFactory', ['$http', function($http) {
//your minsafe controller
var factory = {};
factory.getUsers = function(){
$http.get('api.php').success(function(data) {
// here the data from the api is assigned to a variable named users
return data; // [{"id":"1","name":"Big Kev","email":"bigkev#example.com"},{"id":"2","name":"Judy","email":"punk#example.net"},{"id":"3","name":"Suzy","email":"suzy#example.com"},{"id":"4","name":"Joey","email":"sheena#example.org"},{"id":"5","name":"DeeD","email":"deedee#example.net"}]
});
};
return factory;
}]);
});
view.js,
define(['app'], function (app) {
app.module1.controller('View1Ctrl', ['$scope','UserFactory', function($scope,userFactory) {
//your minsafe controller
$scope.message = "Message from View1Ctrl";
$scope.users = userFactory.getUsers();
console.log($scope.users); // undefined
}]);
});
as you can see, I get undefined for $scope.users. It seems that console.log($scope.users); is executed before $http.get('api.php') in the factory. How can I fix this then?
This has nothing to do with Require; it is a very common misconception about how asynchronous code runs (especially promises).
You see factory.getUsers() makes an asynchronous call. The response will be available at a later time and success will be called with the response data at that later time. On the other hand, $scope.users = userFactory.getUsers() is synchronous. It is almost guaranteed that the async call wll NOT have returned by the time the next statement runs. And even if it has returned, the callback will NOT have run because Javascript is single threaded in the browser.
Moreover you have another serious fault: the success callback is "procedural" in the sense that it receives the response and has to do something with it. Its return value is disregarded!
To use promises (or at least $http) correctly in this case, change factory.getUsers to return the $http promise:
factory.getUsers = function(){
return $http.get('api.php');
};
And change the controller to use the promise or the success/error callbacks:
// option (1) with promise
userFactory.getUsers().then(function(response) {
$scope.users = response.data;
});
// option (2) callback
userFactory.getUsers().success(function(data) {
$scope.users = data;
});