This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I define a variable by the name tst in controller, and i give some value in a function by the name GetOrders, but when i get tst in another function GetTotalValueOfProducts it is undefined and does not have value,this is what i do:
<script>
app.controller('OrderController', function ($scope, $http) {
$scope.quantity = 1;
$scope.Orders = {};
var tst ;
GetOrders = function () {
$http.get('/Order/GetAllOrders').success(function (response) {
$scope.Orders = response;
//here i set tst, and tst has value, i checked it has value and it's not undefined
tst = response;
});
}
GetOrders();
GetTotalValueOfProducts = function () {
//but here 'tst' is undefined!!!
var p = tst;
}
GetTotalValueOfProducts();
});
</script>
What is the problem?
This is happening because js is asynchronous.
You are executing GetOrders() as it is making http request, which will take time but before that http request finishes you are calling GetTotalValueOfProducts which makes it undefined.
<script>
app.controller('OrderController', function ($scope, $http) {
$scope.quantity = 1;
$scope.Orders = {};
var tst ;
GetTotalValueOfProducts = function () {
//but here 'tst' is undefined!!!
var p = tst;
}
GetOrders = function () {
$http.get('/Order/GetAllOrders').success(function (response) {
$scope.Orders = response;
//here i set tst, and tst has value, i checked it has value and it's not undefined
tst = response;
GetTotalValueOfProducts();
});
}
GetOrders();
});
</script>
The above should give you expected result. Not sure when you want to call the function.
Hi There AngularJS is all async so GetTotalValueOfProducts(); is getting called before success callback return any value from http. You have to call your function once the callback is completed.
You can use $q of AngularJS to accomplish it. Here is the link $q
This is a classic JavaScript async problem. Your second function is executed before the promise in the first function is resolved.
You should call
GetTotalValueOfProducts();
in success callback of $http service. Change the code like this
<script>
app.controller('OrderController', function ($scope, $http) {
$scope.quantity = 1;
$scope.Orders = {};
GetOrders = function () {
$http.get('/Order/GetAllOrders').success(function (response) {
$scope.Orders = response;
GetTotalValueOfProducts(response)
});
}
GetOrders();
GetTotalValueOfProducts = function (tst) {
var p = tst;
}
});
</script>
Try attaching tst variable into your inner $scope object.
EDIT
It was sloppy not seeing that these function are async. You should fetch your results as:
Try attaching tst variable into your inner $scope object.
EDIT
It was sloppy not seeing that these function are async. You should fetch your results as.
app.controller('OrderController', function ($scope, $http) {
$scope.quantity = 1;
$scope.Orders = {};
$scope.tst;
GetOrders = function () {
$http.get('/Order/GetAllOrders').success(function (response) {
$scope.Orders = response;
$scope.tst = response;
GetTotalValueOfProducts();
});
}
GetOrders();
GetTotalValueOfProducts = function () {
//but here 'tst' is undefined!!!
var p = $scope.tst;
}
});
Or even try to use $q
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 am currently using Angular 1.3.14 and I would like to make an request using $http whenever a link is clicked. I am trying to place the $http request in a service so it's not bound to any one controller and can be accessed by multiple controllers. I've created two functions sayHello() and getData(). I want each one to console.log when clicked. Only the sayHello() function does, but not the getData() function.. Here's what I don't understand.
Why does the $http request fire on load, and not on click whereas the sayHello function works perfectly?
How can I modify my code so it works as intended?
<p>Console Log Our Data</p>
<p>
Console Log The Word "Hello"</p>
var app = angular.module('myApp', [])
.controller('mainCtrl', function($scope, dataService){
$scope.sayHello = dataService.sayHello;
$scope.getData = dataService.getData(function(response){
$scope.myData = response.data;
console.log($scope.myData);
});
})
.service('dataService', function($http){
this.getData = function(callback){
$http.get('http://jsonplaceholder.typicode.com/posts')
.then(callback);
}
this.sayHello = function(){
console.log("Hello!");
}
});
Codepen for reference http://codepen.io/benweiser/pen/remvvo/
That's because $scope.getData is equal to undefined when it should be a function
$scope.getData = function () {
dataService.getData(function(response) {
$scope.myData = response.data
console.log($scope.myData)
})
}
Update: You can send parameters either from the call to the method or from the method itself, suppose you have the following input
<input ng-model="name">
Then you can use the send the value and use it in your service as follows
<a ng-click="getData(name)">get data</a>
$scope.getData = function (name) {
dataService.getData(name)
.then(function (response) { ... })
}
Or using it directly in the controller
<a ng-click="getData()">get data</a>
$scope.getData = function () {
dataService.getData($scope.name)
.then(function (response) { ... })
}
Note that both assume that dataService.getData() returns a promise instead of passing the callback so you'd also need to do the following on your service if you want to code it like above
this.getData = function (name) {
// do something with name
return $http.get('http://jsonplaceholder.typicode.com/posts/' + name)
}
You should read more on Promises vs callbacks and how you can take advantage of them :)
When you do this
$scope.getData = dataService.getData(function(response){
$scope.myData = response.data;
console.log($scope.myData);
});
})
dataService.getData get executed immediately. As a result, $scope.getData is set to a promise instead of a function you intent to bind to ng-click
change this line $scope.getData = dataService.getData to the following which will actually set a function with callback to $scope.getData
$scope.getData = dataService.getData.bind(this, function(response) {
$scope.myData = response.data;
console.log($scope.myData);
});
I would try doing something like this. Should be easy to apply to your code above
//in your factory
app.factory('dataFactory',function($http){
factory = {};
//get data
factory.getData = function(obj){
return $http({
method: 'GET',
url: 'http://jsonplaceholder.typicode.com/postsn'
})
};
return factory
});
//in your controller
app.controller(someCtrl,function($scope,dataFactory){
dataFactory.getData().then(function(data){
console.log('This will be your data: ', data)
})
})
The Ajax query was being made when controller was first loading. Your bind with "getData" wasn't with a function, but with an ajax call, and that was why it was not triggered.
This refactor works on codepen:
'use strict';
var app = angular.module('myApp', [])
.controller('mainCtrl', function($scope, dataService){
$scope.sayHello = dataService.sayHello;
$scope.getData = function(){
dataService.getData().then(
function(response){
$scope.myData = response.data;
console.log($scope.myData);
}
);
}
})
.service('dataService', function($http){
this.getData = function(callback){
return $http.get('http://jsonplaceholder.typicode.com/posts');
}
this.sayHello = function(){
console.log("Hello!");
}
});
Main updates:
$scope.getData is binded to a function, not an Ajax call/promise
Promise is outside the service (better solutions are welcome :-) ).
dataService.getData is called as a function. (Did't work in other way).
Below added my controller code. I want to access value of $scope.FCGId ....... How can access this variable?
angular.module('hotelApp.controllers')
.controller('menuCtrl', ['$scope','menu'
function($scope,'menu') {
$scope.categories = [];
$scope.FCGId = 0
$scope.items = [];
$scope.getCategories = function() {
menu.getCategories().success(function(data) {
$scope.categories = data;
$scope.FCGId = data['rows'][0].GRPCOD;
});
}
$scope.getItems = function(gropuId) {
menu.getItems(gropuId).success(function(data) {
$scope.items = data;
console.log(data);
});
}
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories();
console.log($scope.FCGId);
$scope.getItems($scope.FCGId);
});
}
]);
From, above code returns 0 instead of value updated in getCategories() function.
Well
$scope.getCategories function is giving asynchronous call
in below event
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories();
console.log($scope.FCGId);
$scope.getItems($scope.FCGId);
});
when you call $scope.getCategories(), this asynchronous call is given.
But script is not waiting for completion of that call. And script access $scope.FCGId variable in console.log($scope.FCGId); without initialization because asynchronous cal is not completed.
Solution to this.
Either you call $scope.getCategories function at the start of controller as initialization part
or you should return promise from $scope.getCategories function
or use promise in another way as per your requirement.
EDIT CODE.
Defined $scope.getCategories as follow
inejct $q in your controller.
var defer = $q.defer();
$scope.getCategories = function() {
menu.getCategories().success(function(data) {
$scope.categories = data;
// $scope.FCGId = data['rows'][0].GRPCOD;
defer.resolve(data['rows'][0].GRPCOD);
return defer.promise;
});
}
and event handling in this way
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories().then(function(successData){
$scope.FCGId = successData
console.log($scope.FCGId);
});
$scope.getItems($scope.FCGId);
});
Solution -2.
Also there is no dependency while giving call to $scope.getCategories function
so you can call it at the starting of comptroller.
Same you can do for the call to $scope.getItems.
Your problem happens because javascript almost always runs faster than asynchronous call returns, so your $scope.getItems always calls before $scope.getCategories returns.
To strictly order the API calls you need a powerful construct called promise. There should be a lot of resources out there, just google "angular promise" and you're good =)
Edit: Actually making use of the success function is the most straight forward way to do this
$scope.getCategories = function() {
menu.getCategories().success(function(data) {
$scope.categories = data;
$scope.FCGId = data['rows'][0].GRPCOD;
$scope.getItems($scope.FCGId); // to here
});
}
$scope.getItems = function(gropuId) {
menu.getItems(gropuId).success(function(data) {
$scope.items = data;
console.log(data);
});
}
$scope.$on('$ionicView.afterEnter', function() {
$scope.getCategories();
console.log($scope.FCGId);
// $scope.getItems($scope.FCGId); // move this line
});
By this way you don't have to deal with all those $q and d's. And promise-antipatterns.
Seems like your menu.getCategories() is an asynchronous execution block, and because of deferred execution, you are getting 0 as the value of $scope.FCGId.
You can pass a function as the second parameter to the getCategories function, that will get executed and perform necessary assignments and further calls.
$scope.setFCGValue = function(data) {
$scope.categories = data;
$scope.FCGId = data['rows'][0].GRPCOD;
$scope.getItems($scope.FCGId);
};
$scope.$on('$ionicView.afterEnter', function() {
menu.getCategories().success($scope.FCGValue);
});
What we are doing is passing our custom function, that will be executed after the getCategories() part.
I've got a problem with filtering data from JSON file, which is an array of 20 objects.
in my factory I have these two functions.
function getData() {
return $http
.get('mock.json')
.success(_handleData)
.error(_handleError);
}
function _handleData(data) {
var filteredData = _filterData(data, "name", "XYZ");
console.log('filteredData', filteredData);
return filteredData;
}
and here console.log("filteredData") shows only filtered elements (i.e. 3 out of 20);
next - in a service I've got this one on ng-click:
var filterMe = function () {
DataFactory
.getData(_address)
.success(_handleServiceData );
}
where
var _handleServiceData = function (data) {
filtered = data;
};
the thing is - why the 'data' in _handleServiceData shows all of the elements instead of these previously filtered?
edit: here's the plunk - results are logged in console
Because the filteredData you return from _handleData function is not passed to the success callback you attach in filterMe function. That's because you attach that callback on the very same promise, since success function doesn't create new promise like the then method does. So to solve this modify your code like this:
function getData() {
return $http
.get('mock.json')
.then(_handleData, _handleError); //use "then" instead of "success"
}
Then in filterMe function:
var filterMe = function () {
DataFactory
.getData(_address)
.then(_handleServiceData );
}
Because promises are asynchronous, and you seem to return the value of filtered to your caller before it could be assigned.
You should be doing
function getData() {
return $http
.get('mock.json')
.then(_handleData); // then (for chaining), not success!
}
var filterMe = function () {
return DataFactory
// ^^^^^^ return a promise, not assign globals in async callbacks
.getData(_address)
.catch(_handleError); // I assume you want to deal with errors only in the end
}
I'm trying to do this:
app.service('productsService', ['$http', productsService]);
function productsService($http){
return {
getProducts: getProducts
}
var _products = [];
function getProducts(){
$http.get('http://localhost:4000')
.then(function(data){
_products = data;
});
}
}
But at the then callback _products is an undefined variable.
What is the correct way to set _products value from the then callback?
You need to set the variable before the return statement.
app.service('productsService', ['$http', productsService]);
function productsService($http){
var _products = [];
return {
getProducts: getProducts
}
//var _products = []; this will never run
function getProducts(){
$http.get('http://localhost:4000')
.then(function(data){
_products = data;
});
}
}