How to assign promise to scope - angularjs

How to assign $promise to $scope.advertiserName? In the below example, Console.log($scope.title) returns "undefined".
Restangular.all('advertiser').one("?id=" + 1).getList().then(function(data) {
$scope.advertiserName = data.name;
$scope.advertiserId = data.id;
});
$scope.title = $scope.advertiserName;
$scope.id = $scope.advertiserId;

We can use callback function to execute lines of code after getting response of ajax call.You can visit this blog for awesome explanation of call back function http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them/. Lets understand it through code.
$scope.setNameId = function (title,id,callBackFunction) {
Restangular.all('advertiser').one("?id=" + 1).getList().then(function(data) {
// $scope.advertiserName = data.name;
$scope.title= data.name;
// $scope.advertiserId = data.id;
$scope.id=data.id;
callBackFunction();
});
}
$scope.setNameId($scope.title,$scope.id,executeAfterResponse);
var executeAfterResponse=function(){
//code that you want to execute when value of $scope.title and $scope.id has changed
};
We can also do it by this approach
$scope.setNameId(executeAfterResponse);
Without passing $scope.title and $scope.id variable in argument of $scope.setNameId function as $scope variables can be accessed directly inside same file.
Commented lines of code are not required as we are assigning value to $scope.name and $scope.id directly.

If I am getting you correct, this is happening because of asynchronous call.
Asynchronous means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example,
$.ajax returns immediately and the next statement, return result;, is
executed before the function you passed as success callback was even
called.
You should do it like
$scope.someFunction = function () {
Restangular.all('advertiser').one("?id=" + 1).getList().then(function(data) {
return $scope.advertiserName = data.name;
});
}
$scope.title = $scope.someFunction(); //It will give you output
Edit 1:
I read many articles for the same and what I noticed that the response of asynchronous call will be taken out from normal execution flow. Either you use restangule or $http call, both are asynchronous call. So compiler will not wait for your response. You need to tell the compiler to wait for the ajax response. What I did in one of my projects. Here is a simple example which may illustrate more.
First I declared a controller function. Like below
$scope.asynchronousCallee = function (){
$http.get('/url').
success(function(data, status, headers, config) {
$scope.myAjaData = data;
});
}
$scope.asynchronousCallee(); //Call above function just after the declaration
Simply this function will receive data from server with a get call and assign response in variable but please note this success function will be called asynchronously. So what I did, I called asynchronousCallee function just after the declaration of it. Now compiler will wait for the response of this function and after executing the function, compiler will proceed further. I hope it may help you brother :-)

In your example below you are expecting the memory reference of advertiserName and title and advertiserId and id to be maintained. However, when pulling properties off of the scope it is retrieved by value not by reference. If you wanted to make your code work you would have to do one of two things:
Restangular.all('advertiser').one("?id=" + 1).getList().then(function(data) {
$scope.advertiserName = data.name;
$scope.advertiserId = data.id;
});
$scope.title = $scope.advertiserName;
$scope.id = $scope.advertiserId;
Initialize the correct property on the scope:
Restangular.all('advertiser').one("?id=" + 1).getList().then(function(data) {
$scope.title = data.name;
$scope.id = data.id;
});
Make it a by reference update instead:
var advertiser = {
id: $scope.advertiser,
title: $scope.advertiser
}
$scope.advertiser = advertiser;
Restangular.all('advertiser').one("?id=" + 1).getList().then(function(data) {
advertiser.title = data.name;
advertiser.id = data.id;
});
Since promises through the use of angular already trigger a digest cycle the view will then be updated

Restangular.all('advertiser').one("?id=" + 1).getList().then(function(data) {
$scope.advertiserName = data.name;
$scope.advertiserId = data.id;
return { name : data.name, id : data.id };
}, function(er){
//Handle error
})
.then(function(response){
$scope.title = response.name;
$scope.id = response.id;
}, function(er){
//Handle error
});

Related

Angular Promises returning q.all( ) with empty array

What am i trying to achieve is as such:
Invoking my service to retrieve all appointments in appointment types (number of types not fixed) tied to a doctor
If there are 3 appointment types, then there will be 3 async calls made
return a single promise with $q.all() after all 3 promises have been resolved
appointmentService
this.getAllDoctorAppointments = function (doctor, appointmentTypeArray) {
var promises = [];
angular.forEach(appointmentTypeArray, function (appointmentType) {
var defer = $q.defer();
$http.get('/appointments/?doctorName=' + doctor + '&apptType=' + appointmentType)
.success(function (listOfAppointments) {
defer.resolve(listOfAppointments);
promises.push(defer.promise);
});
});
return $q.all(promises);
};
In my console log, the appointmentType returns [ ].
This happens because the empty 'promises' array is returned even before the 3 async calls are made. I am still very new to the concept of promises, what is the best approach to work this out? Thanks!
$scope.getAllDoctorAppointments = function (doctor, appointmentTypeArray) {
appointmentService.getAllDoctorAppointments(doctor, appointmentTypeArray)
.then(function (appointmentType) {
//could take in any number. hardcoded 3 just for testing.
console.log(appointmentType)
angular.forEach(appointmentType[0], function (xRay) {
$scope.xRayAppts.events.push(xRay);
});
angular.forEach(appointmentType[1], function (ctScan) {
$scope.ctScanAppts.events.push(ctScan);
});
angular.forEach(appointmentType[2], function (mri) {
$scope.mriAppts.events.push(mri);
});
});
};
this.getAllDoctorAppointments = function (doctor, appointmentTypeArray) {
var promises = [];
angular.forEach(appointmentTypeArray, function (appointmentType) {
promises.push($http.get('/appointments/?doctorName=' + doctor + '&apptType=' + appointmentType)
.success(function (listOfAppointments) {
return listOfAppointments;
});
);
});
return $q.all(promises);
};
$http.get returns the promises that you wants to collect, there is no need for a new defer in this case.
the promise is not being added to the array because the code that adds it to the array, promises.push(defer.promise);, is inside of the result code of the thing you are trying to defer. So the promise wouldn't get added to the list of promises to execute until after it executed!
so you can either move that push line outside of the success call looking something like this:
angular.forEach(appointmentTypeArray, function (appointmentType) {
var defer = $q.defer();
$http.get('/appointments/?doctorName=' + doctor + '&apptType=' + appointmentType)
.success(function (listOfAppointments) {
defer.resolve(listOfAppointments);
});
promises.push(defer.promise);
});
or, you can do as lcycool suggests and just add the $http.get(...).success(...) calls to the array directly.

AngularJS - $scope undefined within same controller

I have a controller where I am trying to store information in $scope.weather and then use it's contents to pass to a function. When I log the result of $scope.weather[0].latitude when I use it one function but when I call it another function within the same controller the result is coming back undefined. Shouldn't the $scope be usable within the same controller? This is also within the same function.
angular.module('CityCtrl', []).controller('CityController', ['$scope', '$http', 'City', function($scope, $http, City){
$scope.update = function (zip) {
City.get({zip : zip}).success(function(response){
$scope.weather = response
}).then(function(response){
$scope.weather = response.data;
// This is returning the expected result
console.log($scope.weather[0].latitude;
})
if(zip.length === 5){
// This is coming back undefined
console.log($scope.weather[0].latitude);
var box = getBoundingBox([$scope.weather[0].latitude, $scope.weather[0].longitude], 50);
City.matches(box[1], box[3], box[0], box[2]).success(function(response){
$scope.matches = response
}).then(function(response){
$scope.matches = response.data;
console.log($scope.matches);
})
}
}
}]);
This is an order of operations issue.
When you console.log($scope.weather[0].latitude) in the if statement $scope.weather has not actually been set yet because the City.get() call is asynchronous. This means $scope.weather will not be set until a successful response is returned from the City service, which in your case will execute after the code block in the if statement.
In order for your if statement code to have access to $scope.weather you would need to include it in the .then(function() { // if logic here } portion of your code.
Also it is worth noting that City.get().success() and City.get().then(successFunction) are pretty much doing the same thing. You should use one or the other, but you shouldn't need to use both.

AngularJS how do I execute code only after a promise is resolved? (with Restangular)

This might be a nooby question but I still haven't been able to get my head around promises and specifically how to write code with them. (I've read several articles but most of them are abstract and I simply haven't written enough to have a clear picture)
I've got an AngujlarJS application that gets data through a http request to another server which sends a promise at first. I've been able to retrieve the response from the promise and use it in my app. However because my code is poorly written. It executes other code before the promise is resolved leading to problems. It starts loading the page before it has the data.
what i have is:
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object
//code that does someting with $scope.data
What i need is (I think)
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object.
beforethisresolves(function{
show fancy loading icon or something })
.whenthis resolves(function{
//code that does someting with $scope.data
}
however I can't get the syntax correct.
This is what it looks like in general:
var promise = $http.post('/url');
console.log('Request started');
promise.then(function(result) {
console.log('Success');
console.log(result);
}, function() {
console.log('Failure');
});
In fact, $q AngularJS documentation helped me a good deal to get my head around promises concept.
Hope this helps!
var successCallback = function(){//...};
var errorCallback = function(){//...};
$http
.post('url', data)
.success(successCallback)
.error(errorCallback);
//OR
$http
.post('url', data)
.then(successCallback, errorCallback);
Assuming that you're using Bootstrap modal you can do the following:
function openModalWithProgressIndicator(deferred) {
const progressModal = $uibModal.open({
templateUrl: 'templates/modals/progress.html',
backdrop: 'static'
});
return deferred.then(function (value) {
return value;
}).finally(function () {
progressModal.close();
});
}
The deferred argument passed to this function is a promise. That said you can now do the following:
const deferred = $http.post('http://somewhere/data', {foo: 'bar'});
openModalWithProgressIndicator(deferred)
.then(function (httpResponse) {
// do sth with httpResponse.data
}).catch(function (error) {
// do sth with error
});
So the main point to note is the finally callback that's always executed.

How to use an angular scope variable outside the scope it is defined in

I have a service like below, which fetches the id of a student from a RESTful (Laravel) API and returns it.
.factory('Student', function($http)
{
return {
getId: function(adm_no) {
return $http.post('/api/student/getId',{adm_no:adm_no})
.then(function(response)
{
return response.data;
},
function(httpError)
{
Notifier.error(httpError.data.error.message,'Error ' + httpError.status + " Encountered");
});
}
}
}
)
Then i use it as follows in a controller.
$scope.adm_no = 98;
Student.getId($scope.adm_no)
.then(function(response)
{
$scope.id = response;
});
// probably i want to use the `$scope.id` when a particular event takes place (or even in another query to the server alltogether), but outside the above function scope e.g.
$scope.showId = function()
{
alert($scope.id);
};
Now, the question is how I can use the a scope variable declared in a 'local scope' outside the scope, for the usage above shows that $scope.id is undefined?
Your $scope.id is undefined in function $scope.showId() because when you call an alert function, your post request hasn't finished yet and so $scope.id hasn't been initialized (it is beeing executed asynchronously). Try this:
$scope.showId = function() {
if ($scope.id) {
alert($scope.id);
}
};
Anyway you don't have to use $rootScope in this case. Your property id from $scope is accesible from your whole controller. You have to wait for the ajax post request and than it is initialized.
In place of $scope you have to use $routescope variable to get id in other place as-
Student.getId($scope.adm_no)
.then(function(response)
{
$routescope.id = response;
});

How to use data from $http in other controller

How can I use the totalResults outside of the function that Im setting it? I just cant wrap my head around how to do it, I need to use the totalResults that I gather from my database and use in another function to calculate the amount of pages. I do this so I dont load all the data to the client but I still need to know the total count of rows in the database table.
My json looks like:
Object {total: 778, animals: Array[20]}
Angular:
var app = angular.module('app', []);
app.controller('AnimalController', ['$scope', 'animalSrc', function($scope, animalSrc)
{
$scope.animals = [];
var skip = 0;
var take = 20;
var totalResults = null;
//$scope.totalResults = null;
$scope.list = function()
{
animalSrc.getAll(skip, take, function(data) {
$scope.animals = $scope.animals.concat(data.animals);
// I need to be able to use this outside of function ($scope.list)
totalResults = data.total;
//$scope.totalResults = data.total;
});
};
$scope.showMore = function()
{
skip += 20;
$scope.list();
};
$scope.hasMore = function()
{
//
};
// Outputs null, should be the total rows from the $http request
console.log(totalResults);
}]);
app.factory('animalSrc', ['$http', function($http)
{
// Private //
return {
getAll: function(skip, take, callback)
{
$http({
method: 'GET',
url: 'url' + skip + '/' + take
}).
success(function(data) {
callback(data);
}).
error(function(data) {
console.log('error: ' + data);
});
}
};
}]);
You need to start thinking asynchronously. Your console.log is called before the $http has returned and totalResults has been set. Therefore, totalResults will always be null.
You need to find some way to delay the call to console.log so that the $http call can finish before you run console.log. One way to do this would be to put the console.log call inside your callback function so that it is definitely called after $http's success.
A more elegant way to do this is to use promises. angular.js implements $q, which is similar to Q, a promise library.
http://docs.angularjs.org/api/ng.$q
Instead of creating a callback function in getAll, you return a promise. Inside $http success, you resolve the promise with the data. Then, in your controller, you have a function that is called when the promise is resolved. Promises are nice because they can be passed around and they allow you to control the flow of your asynchronous code without blocking.
Here's a boilerplate I was just working on for myself for similar setup where data is an object that needs to be split into more than one scope item. Issue you weren't grasping is storing the data within the service, not just using service to retrieve data. Then the data items are available across multple controllers and directives by injecting service
app.run(function(MyDataService){
MyDataService.init();
})
app.factory('MyDataService',function($http,$q){
var myData = {
deferreds:{},
mainDataSchema:['count','items'],
init:function(){
angular.forEach(myData.mainDataSchema,function(val,idx){
/* create deferreds and promises*/
myData.deferreds[val]=$q.defer();
myData[val]= myData.deferreds[val].promise
});
/* load the data*/
myData.loadData();
},
loadData:function(){
$http.get('data.json').success(function(response){
/* create resolves for promises*/
angular.forEach(myData.mainDataSchema,function(val,idx){
myData.deferreds[val].resolve(response[val]);
});
/* TODO -create rejects*/
})
}
}
return myData;
})
app.controller('Ctrl_1', function($scope,MyDataService ) {
$scope.count = MyDataService.count;
$scope.items =MyDataService.items;
});
app.controller('Ctrl_2', function($scope,MyDataService ) {
$scope.items =MyDataService.items;
$scope.count = MyDataService.count;
});
Plunker demo

Resources