In the controller i would like to put the http request of a json file into a function. Only when its outside of a function it works. Why?
I would like to display the contents in the view.
Controller:
$http.get('components/json/list.json').success(function (data) {
main.listNoFunction = data;
});
main.listFunction = function () {
var result = null;
$http.get('components/json/list.json').success(function (data) {
result = data;
});
return result;
}
View:
// This works:
{{ main.listNoFunction }}
// This works NOT:
{{ main.listFunction }}
Thanks for your help!
Vin
you need to call the function, not just reference its object name.
example:
{{ main.listFunction() }}
Also, $http is asynchronous, meaning that the result of the function will be returned before the $http call is finished.
I suggest you read up on promises at https://docs.angularjs.org/api/ng/service/$q to achieve the desired effect.
There is no reason why the second example would work. Here is the explanation of the both snippets.
Snippet #1.
$http.get('components/json/list.json').success(function (data) {
main.listNoFunction = data;
});
This code put in the controller issues GET request. When response is available (remember that this is asynchronous operation, so $http.get returns immediately), execution enters into success callback. Inside of it controllerAs (main) property listNoFunction is populated. Then $http.get kicks off new digest loop, which applies scope objects and refreshes the view. You see data rendered properly.
Snippet #2.
main.listFunction = function () {
var result = null;
$http.get('components/json/list.json').success(function (data) {
result = data;
});
return result;
}
First of all, you didn't call the function main.listFunction. But even if you did, result would still be the same. Once again, $http.get doesn't wait until server responds, so entire main.listFunction returns undefined. When later data comes into success callback and result is set - it's too late because it will not be reflected in the view anyway, since result was passed by value not reference.
Related
var final;
final = $http.get('http://localhost:9000/therapist_data',config)
.success(function(response) {
console.log("I got the data I requested");
var resdata = response;
console.log(resdata);
return resdata;
});
console.log(final);
I am trying to return response data and store it into final variable instead I am getting the promise object.
How do I return actual data?
I'll try to develop Cyril answer based on your code :
var final;
final = $http.get('http://localhost:9000/therapist_data',config)
.success(function(response) {
console.log("I got the data I requested");
var resdata = response;
console.log(resdata);
return resdata;
});
console.log(final);
Here is the order of execution :
var final
$http.get('http://localhost:9000/therapist_data',config)
.success(); : this will trigger the request and register the function in success as a callback when server will have respond to your request
console.log(final); -> so still undefined. It does NOT WAIT for the response.
some times later ... your function in the success is called.
This is the very basis of callbacks and asynchonous processing, you don't know when it will be executed, or at least, it will often be executed after all the other code. In angularJS there is no way of doing a synchronous request. You must move you code in the success function.
As long as you are making a network call, your data will return back asynchronously, it is the nature of it, you can't fight it.
var wrongFinal; // <-- nope, final will never get into that scope
$http.get('http://localhost:9000/therapist_data',config)
.success(function(response) {
console.log("I got the data I requested");
var goodFinal = reponse; // <-- yes, here, the data lived
// do something with the data here
});
console.log(wrongFinal); // nop, wrong scope, no sense, data doesn't live here
Soooooo, the answer is a question:
What do you want to do with your data?
It depends on the destination. Are you going to make anoher network call? Do you want to update the view? Do you want to call a 3rd party library?
You need to understand and to embrace the nature of asynchronous in JavaScript.
$http.get will always return a promise.
If you'd like to get the promise value you should do it inside the success callback, like this:
var final;
$http.get('someUrl').success(function(response) {
final = response;
});
No need for resData, will only cause a promise chain, something you don't require in this case.
I'm running into a bit of a problem with an Angular (1.4) service. The code is roughly as follows :
service.retrieveStuffFromServer = function() {
return httpCallFromServer().then(
function(data) {
if (Array.isArray(data)) {
return data;
}
return [];
}
);
};
I call this function in two distinct controllers. Most of the times, it works as intended, but I'm having problem in those conditions :
The HTTP call takes time to return the data
Controller A calls the service.
Controller B calls the service.
The service returns data to controller A
The call in the controller B is cancelled. The logic after it never executes
My first guess would be to slightly alter the service, to inform either of the controllers if the service is already busy so I can retry later, but I'm not sure if this is the best solution, so I'm looking for some advice.
Hard to say why it doesn't just work, but presumably something in httpCall() is preventing the same call from being made again before the 1st one completes, and it rejects if that happens. But if you want controller B call to share the response from an active previous call, you could cache the promise:
function myFunction() {
if (!myFunction.promise) {
myFunction.promise = httpCall()
.then(function(result) {
myFunction.promise = undefined;
return ...
}, function(err) {
myFunction.promise = undefined;
throw err;
});
}
return myFunction.promise;
}
This will cause the same promise from a prior call to be returned as long as the prior call is still unresolved.
Using a property of the function itself as a cache is a convenient way to keep state associated logically with the function itself. You could just use any variable defined outside the scope of myFunction though.
I'm creating a factory to take a userId from one page, make a call to a REST API, and return the results on the following view. My initial attempts were largely taken from this answer but - unsurprisingly - I keep getting caught in a situation where the doesn't respond in time and the get() method returns an empty array.
Here's the factory itself
app.factory('GetMessages', function() {
var messages = []
function set(userId) {
Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
messages = docs
})
}
function get() {
return messages;
}
return {
set: set,
get: get
}
});
For what it's worth I'm having no trouble getting the userId into the factory as it's just passed in on a function like this
view:
<a ng-click='passToFactory(message.user.id)' href='/home/inbox/reply'>Reply</a>
controller:
$scope.passToFactory = function(id) {
GetMessages.set(id);
};
and the controller for the following view is just
$scope.messages = GetMessages.get()
The issue I'm having is that after the factory returns the empty set no further changes from the factory are recognized (even though after time elapses it does get the proper response from the API) and $scope.messages remains empty.
I've attempted to move the API call to the get method (this hasn't worked as the get method often does not get the userId in time) and I can't find a way to use a promise to force get() to wait on set() completing.
I'd prefer to keep using Restangular in the eventual solution but this is a small thing that has taken too much time so any fix works.
I'm fairly new to Angular so I'm sure there's something totally obvious but right now I'm just lost. Thanks.
The race condition that you have is that the function inside the .then method is executed asynchronously after the call to the set function. If the get function executes before the $q service fulfills the promise, the get function returns an empty array.
The solution is to save the promise and chain from the promise.
app.factory('GetMessages', function() {
var promise;
function set(userId) {
promise = Restangular.all('/api/messages/').getList({'_id': userId});
}
function get() {
return promise;
}
return {
set: set,
get: get
}
});
In your controller, chain from the promise.
GetMessages.get.then( function (docs) {
$scope.messages = docs;
}) .catch ( function (error) {
//log error
};
For more information on chaining promises, see the AngularJS $q Service API Reference -- chaining promises.
You are breaking the reference to the original messages array when you reassign it.
Try:
Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
messages.concat(docs) ; // keep same array reference
});
Simple example to explain why it isn't working
var arr = [];
var x = arr;
arr = [1,2,3]; // is now a different array reference
console.log(x); // is still empty array. x !== arr now
cherlietfl is right.
The problem is that you break the reference to the messages array since you assign a new array to messages inside your get function. But concat is doing this as well.
Try this:
Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
messages.splice(0, messages.length); // clear the array
messages.push.apply(messages, docs); //add the new content
});
Try assigning you function to the scope. Then call that function in the model. Like so:
// controller
$scope.getMessages = GetMessages.get;
View:
<div ng-repeat="message in getMessages()"></div>
This way when the request call finishes and the digest cycle goes through the watchers again, the get function will be called and you will get your messages.
I have a promise that runs without a problem when it runs during application start, e.g.
myPromise
.success( function(data) { $scope.myvariable = data })
.error( function(msg, code) { console.log("msg: " + msg + "\nCode" + code) });
However if I try to run the promise dynamically, let's say when a button is clicked, (1) the promise executes successfully but none of my variables are updated.
running apply or digest only produces the following error: $digest already in progress
$scope.getContent = function() {
myPromise
.success( function(data) {
$scope.myVariable = data; //THIS WORKS
console.log(data); //THIS WORKS
})
}
//Running the below code afterwards still produces a null value
console.log($scope.myVariable);
This is what we called as async world.
When you are waiting for the callback of your promise the statement console.log($scope.myVariable); already executed but still you don't have any value in it.
In that case you can use $watch if you want to get it value outside.
$scope.$watch('myVariable',funciton(newVal){
console.log(newVal);
});
Little Detail:-
myPromise is invoked and waiting for the response in the meanwhile the statement console.log($scope.myVariable); after it executed which obviously doen't have any value for $scope.myVariable inside it (nobody gave it :-P). So when response came back it call the success or error method and initialize the value to your variable $scope.myVariable and print it.
Here your console.log($scope.myVariable); statement executes before success callback so here you need to do .then() chaining or apply watch on scope variable.
The reason your console.log, which comes after your promise logs null is because it is executing before your promise returns. Even though the function using the promise has run, the success part of the code has not fired yet. The code hits the promise, makes the calls, creates the promise object, and moves on. When the promise returns, it fills in the empty promise object with the returned data. This is happening after your
console.log($scope.myVariable);
A good way maybe to handle it is to store the returned value in a variable. Inside the success set the variable to the returned data, then use a function like
$scope.myVariable = undefined;
$scope.getContent = function() {
myPromise
.success( function(data) {
$scope.myVariable = data; //THIS WORKS
console.log(data); //THIS WORKS
})
}
function checker() {
if($scope.myVariable) {
console.log($scope.myVariable);
}
}
Then you can call that function as needed.
Depending on what you are doing and when your getContent function needs to run, you may want to use this with ui-router, and use resolve, which will run these functions before the page loads, thereby ensuring you have data to work with when the DOM loads.
AngularJS UI-Router: preload $http data before app loads
I think promise object makes call only once,when controller is initialized
you have to reinitialize controller,to get new updated values from server
A. Call sequence will be as follows when DOM ready:
1. $scope.getContent will get initialized.
2. then execution : console.log($scope.myVariable);
B. Async call:
1. If success then below statement will get executed.
$scope.myVariable = data; //THIS WORKS
Above point A and B are independent. Execution is asyc here.
Hope this will help you understand. Enjoy.
Maybe this can help :
// a service providing data with async call
app.service('myService', function(elasticQuery) {
this.getData = function (a,b,c) {
// don't forget the 2 return
return elasticQuery.search({
// make ajax call
}).then(function (response) {
return response
});
};
});
// in my controller
// scope.getData is lunch with ng-click in the view
$scope.getData = function(a,b,c){
myService.getData( a,b,c ).then(function (data) {
$scope.myVariable = data;
});
};
I'm aware that Angular can handle promises from within controllers. For example:
function MyCtrl($scope) {
$scope.myvar = getDeferredPromise();
}
The main angular digest loop handles this gracefully, assigning whatever value the deferred function finally returns later to myvar.
However, although the $http.get() method returns a promise, I cannot get it to work in this way. For example:
function MyCtrl($scope, $http) {
$scope.myvar = $http.get('/url');
}
The 'promise' the get method returns has a success method which takes a function that is assigned the data that one would wish to assign to myvar.
However, it also has a then method - but that is given the entire response object - not just that data part! This is what seems to end up getting assigned to myvar!
This fiddle may help: http://jsfiddle.net/QKnNC/1/
Am I doing something wrong here? Or is this somehow 'by design'?
ng.$http
The $http service is a function which takes a single argument — a
configuration object — that is used to generate an HTTP request and
returns a promise with two $http specific methods: success and error.
$http returns a promise, so you need to chain then to get the data.
IPService.getV1().then(function (response) {
console.log(response)
$scope.value1 = response.data;
});
then is the general promise function, that takes a success and error callback and you get the resolved value, whatever it may be. success and error are $http specific, and are aliases for then with one exception: they set a bunch of useful arguments rather than just the data. See the source.
It is by design. Your getV2() method is what you want. Since you are using GET, you could save the result of your promise and return that on subsequent calls to getV2():
var v2promise, v2data;
return {
getV2: function() {
if(!v2promise) {
v2promise = $http.get('http://ip.jsontest.com/').then(
function(response) {
v2data = response.data;
return v2data;
});
}
return v2promise;
}
}