AngularJS $rootScope loses value unless I use promise (trigger.io AJAX request) - angularjs

So this is quite weird. $rootScope is getting set correctly within a function, but then loses its value. I can only keep the value if I use a promise. (!?)
I am using a trigger.io (Forge) AJAX request, and upon success I update $rootScope.queries. Within the success block, $rootScope.queries is set correctly, as shown by the alert.
.run(function($rootScope, $state, $q) {
var retrieveHistory = function() {
forge.logging.log("Retrieving history");
// Retrieve the history
forge.request.ajax({
type: 'GET',
url: 'http://localhost:9000/json',
dataType: 'json',
success: function (data) {
$rootScope.queries = data["queries"];
alert("queries " + JSON.stringify($rootScope.queries));
},
error: function (error) {
}
});
}
retrieveHistory();
At this point though, after retrieveHistory() has been called, $rootScope.queries is now empty. The view doesn't update and inspecting using the Chrome console shows that it is empty.
Let's say I add a promise to the function, but don't really use the promise for anything.
.run(function($rootScope, $state, $q) {
var retrieveHistory = function() {
var deferred = $q.defer();
forge.logging.log("Retrieving history");
// Retrieve the history
forge.request.ajax({
type: 'GET',
url: 'http://localhost:9000/json',
dataType: 'json',
success: function (data) {
$rootScope.queries = data["queries"];
alert("queries " + JSON.stringify($rootScope.queries));
},
error: function (error) {
deferred.reject("error " + JSON.stringify(error));
}
});
return deferred.promise;
}
var promise = retrieveHistory();
promise.then();
With this promise, $rootScope.queries keeps its value. The view updates and Chrome inspector shows the value is set correctly.
Why is this? I simply don't understand this behavior. Why can't I preserve the value of $rootScope.queries in the original code? Why does the promise keep the value?

ok, 2 questions.
why are you using trigger.ajax over $http pr resource which are angular native services hence are fully aware of angulars diges cycle not the case of external libraries.
why are you using rootscope you really shouldn't do that unles you are broadcasrting an event inside a service i know no good reason to polute your rootscope.
How ever your problem is likely to be doe to forge.ajax being outside angular digest so you need to execute.
$rootScope.$apply()
to let rootscope know there are somechangins he needs to be aware of

Related

Angular JS 1.X Access Resolved Promise Objects values

I have read a lot of posts on promises,resolving promises, and accessing the data however I cannot seem to. Following some posts on Stack Overflow has just caused errors, so I am not sure what exactly I am doing wrong.
I have a function like so:
function getJsonl() {
var deferred = $q.defer();
$http({
url: 'urlNotShownForSecurity',
dataType:"json",
method: 'GET',
data:{"requestId":"123"},
headers:{"Content-Type":"application/json","requestId":"123"},
}).success(function(data) {
deferred.resolve(data);
console.log(data)
}).error(function(error,status,headers,config) {
deferred.reject(error);
});
return Promise.resolve(deferred.promise);
}
Here I return a json promise that has been resolved resulting in a json object I believe.
Printing to console I get the following:
Inside data is the information I need, it looks like this:
data:Array[8]
0:Object
description:"My description paragraph"
I have tried things with the returned object in my controller like:
vm.result = data.data[0].description;
vm.result = data[0].description
I have tried many different approaches in the view as well to access but I get 2 blank li tags and that is it.
I would like to be able to access the data so I populate a table. So if I can use it with ng repeat that would be great, as well as being able to access without because some data is used in more than just the table.
Update
#DanKing, following your implementation I get the following output in console:
Now I am back with a promise object.
It looks to me as though you're fundamentally misunderstanding the nature of promises.
$http() is an asynchronous function - that means it doesn't complete straight away, which is why it returns a promise.
It looks to me as though you're trying to call $http() and then get the result back and return it from your getJson1() method, before $http() has finished executing.
You can't do that. Your getJson1() method should just return the promise, so your calling method can chain onto it - like this:
getJson1().then(function(data) {
// do some other stuff with the data
});
The whole point of promise chains is that they don't execute straightaway - instead you provide callback functions that will be executed at some indeterminate point in the future, when the previous operation completes.
Your getJson1() function just needs to do this:
return $http({
url: 'urlNotShownForSecurity',
dataType:"json",
method: 'GET',
data:{"requestId":"123"},
headers:{"Content-Type":"application/json","requestId":"123"},
});
getJsonl().then(function(data){
console.log(data);
},function(err){
console.log(err);
})
should work. Where is your $http request and where is your call to getJsonl() will also make a difference. So choose that carefully when implementation. If you are using this in a service then you will have to return the function result say
this.somefunction = function (){
return getJonl();
}
and in your controller inject the service and do the following
service.somefunction().then(function(data){
console.log(data);
},function(err){
console.log(err);
})
Ok, rewrote the answer as a complete component to show the moving parts.
$http returns a promise so your original getJsonl call can be simplified. Using your original $http parameters this should dump your API response to the screen if you use the <json-thing> tag:
angular.module('yourModuleName')
.component('jsonThing', {
template: '<pre>{{$ctrl.thing|json}}</pre>',
controller: function ($http) {
var $ctrl = this;
getJsonl()
.then(function (response) {
console.log(response); // we have the $http result here
$ctrl.thing = response.data; // allow the template access
}, function (error) {
console.log(error); // api call failed
});
// call backend server and return a promise of incoming data
function getJsonl() {
return $http({
url: 'urlNotShownForSecurity',
dataType: 'json',
method: 'GET',
data: { requestId: '123'},
headers: { 'Content-Type': 'application/json', requestId: '123'}
});
}
}
});

How to access ajax response data outside ajax function in angularjs

I am fetching some data via http get ajax.
$.ajax({
type: "GET",
url: "/userdata/",
success: function(response){
$scope.steps = response.no_of_steps;
$scope.calories = response.calories;
},
error: function(){
alert("error");
}
});
If i print it on console from inside the success function, it prints the value like:
success: function(response){
var $scope.steps = response.no_of_steps;
var $scope.calories = response.calories;
console.log($scope.steps, $scope.calories);
},
But if i print it on colsole outside my ajax request, it print undefined. how is that so?
$.ajax({
...
});
console.log($scope.steps, $scope.calories);
this is continuation of my comment on your post above
you could do that by using $scope.$emit("custom-event-name"); of angular,
here you are actually watching $scope.steps and $scope.calories for change once your ajax call completes signify this by using $emit of angular
and then listen for that event by $scope.$on("custom-event-name", function(){})
and log it there, Please do make sure your "custom-event-name" is same in $emit and $on
in success callback : initialize the value and use $emit to emit an event named initialized as :-
$scope.steps = response.no_of_steps;
$scope.calories = response.calories;
$scope.$emit("initialized");
and then listen for that event initialized
$scope.$on('initialized', function () {
console.log($scope.steps + ' and ' + $scope.calories);
});
Couple of mistakes here.... use $http instead of $ajax, secondly you are defining your variables inside your success function so the scope is limited once it comes out of it... define your function globally in your controller and then access it inside your success function.
var $scope.steps => wrong way
$scope.steps = "something"; => right way.
You don't need to append var keyword with $scope in angularjs.
$scope.steps;
$scope.calories;
$.ajax({
type: "GET",
url: "/userdata/",
success: function(response){
$scope.steps = response.no_of_steps;
$scope.calories = response.calories;
},
error: function(){
alert("error");
}
});
Use $scope variables if you want to bind to the view and var does not and is local to the function it was declared in.
This works for me, i want to recommend you not to make use of $.ajax in angular js its a jquery function; i heard $http is faster than $.ajax function.
Hope this works for you if didnot work please inform me
//$scope.tablerows defined outside the function
$scope.tablerows;
module='user';
url=siteurl+"/admin/"+module+"/list";
BlockUi();
// $http is ajax call in angular js which is equivalent to $.ajax();
$http({
url : url,
method : "GET",
data : "",
headers : { 'Content-Type':undefined,
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods' : 'POST, GET, OPTIONS, PUT',
}
//responseText is the output of success call
}).then(function(responseText){
// assigning the response text on the variable
$scope.totaltablerows=responseText.data.response_data;
$scope.tablerows=$scope.totaltablerows;
UnblockUi();
},function(error){
//alert(JSON.stringify(error));
UnblockUi();
UnknownError(JSON.stringify(error));
});

How to use $http promise response outside success handler

$scope.tempObject = {};
$http({
method: 'GET',
url: '/myRestUrl'
}).then(function successCallback(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
}, function errorCallback(response) {
});
console.log("Temp Object outside $http ", $scope.tempObject);
I am getting response in successCallback but
not getting $scope.tempObject outside $http. its showing undefined.
How to access response or $scope.tempObject after $http
But if I want to use $scope.tempObject after callback so how can I use it. ?
You need to chain from the httpPromise. Save the httpPromise and return the value to the onFullfilled handler function.
//save httpPromise for chaining
var httpPromise = $http({
method: 'GET',
url: '/myRestUrl'
}).then(function onFulfilledHandler(response) {
$scope.tempObject = response
console.log("Temp Object in successCallback ", $scope.tempObject);
//return object for chaining
return $scope.tempObject;
});
Then outside you chain from the httpPromise.
httpPromise.then (function (tempObject) {
console.log("Temp Object outside $http ", tempObject);
});
For more information, see AngularJS $q Service API Reference -- chaining promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
Explaination of Promise-Based Asynchronous Operations
console.log("Part1");
console.log("Part2");
var promise = $http.get(url);
promise.then(function successHandler(response){
console.log("Part3");
});
console.log("Part4");
The console log for "Part4" doesn't have to wait for the data to come back from the server. It executes immediately after the XHR starts. The console log for "Part3" is inside a success handler function that is held by the $q service and invoked after data has arrived from the server and the XHR completes.
Demo
console.log("Part 1");
console.log("Part 2");
var promise = new Promise(r=>r());
promise.then(function() {
console.log("Part 3");
});
console.log("Part *4*");
Additional Resources
Angular execution order with $q
What is the explicit promise construction antipattern and how do I avoid it?
Why are angular $http success/error methods deprecated? Removed from v1.6?
How is javascript asynchronous AND single threaded?
Ninja Squad -- Traps, anti-patterns and tips about AngularJS promisesGood theory but needs to be updated to use .then and .catch methods.
You're Missing the Point of Promises
$http call is async call. The callback function executes when it has returned a response. Meanwhile the rest of the function keeps executing and logs $scope.tempObject as {}.
When the $http is resolved then only $scope.tempObject is set.
Angular will bind the changed value automatically using two way binding.
{{tempObject}} in the view will update itself.
if you want to use tempObject after callback then do this
then(function(data){
onSuccess(data);
},function(){
});
function onSuccess(data){
// do something
}
Try to use a $scope.$apply before to finish the successCallback function. An other solution is to change successCallback -> function so:
$http({ method: 'GET', url: '/myRestUrl' }).then(function(success) { $scope.tempObject = success; console.log("Temp Object in successCallback ", $scope.tempObject); }, function(error) { });

how to make synchronous http request in angular js

How to make blocking http request in AngularJS so that i can use the $http response on very next line?
In the following example, $http object doesn't return the result to the next line so that I can pass this result to fullcalender(), a JavaScript library, because $scope.data returns blank value.
This is the sample code:
$http.get('URL').success(function(data){
$scope.data = data;
});
$.fullCalender({
data: $scope.data
});
You can use promises for that.
here is an example:
$scope.myXhr = function(){
var deferred = $q.defer();
$http({
url: 'ajax.php',
method: 'POST',
data:postData,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
//if request is successful
.success(function(data,status,headers,config){
//resolve the promise
deferred.resolve('request successful');
})
//if request is not successful
.error(function(data,status,headers,config){
//reject the promise
deferred.reject('ERROR');
});
//return the promise
return deferred.promise;
}
$scope.callXhrAsynchronous = function(){
var myPromise = $scope.myXhr();
// wait until the promise return resolve or eject
//"then" has 2 functions (resolveFunction, rejectFunction)
myPromise.then(function(resolve){
alert(resolve);
}, function(reject){
alert(reject)
});
}
You can't, you'll need deal with it through promises, but you could try do it like this:
$http.get('URL').success(function(data){
angular.copy(data, $scope.data);
});
$.fullCalender({
data: $scope.data
});
but most people would just do
$http.get('URL').success(function(data){
$.fullCalender({
data: data
});
});
If whatever your fullCalender object is doesn't work with async data, you might need to wrap it in something like ng-if or force it to redraw when the data has been supplied. You can also force the controller to not load until the data is loaded by using the route resolve.
Here is a practical answer, courtesy of user Kirill Slatin who posted the answer as a comment. Practical use example at the bottom of the answer.
If, like me, you need to use that response object as a scope variable, this should work:
$http.get('URL').success(function(data){
$scope.data = data;
$.fullCalender = $scope.data;
$scope.$apply()
});
$scope.$apply() is what will persist the response object so you can use that data.
-
Why would you need to do this?
I'd been trying to create an "edit" page for my recipes app.
I needed to populate my form with the selected recipe's data.
After making my GET request, and passing the response data to the $scope.form, I got nothing... $scope.$apply() and Kirill Slatin helped big time. Cheers mate!
Here's the example from my editRecipeController:
$http.get('api/recipe/' + currentRecipeId).then(
function (data) {
$scope.recipe = data.data;
$scope.form = $scope.recipe;
$scope.$apply()
}
);
Hope that helps!

angular js getting data from laravel and print it

Hi I'am trying a simple example of using a controller and a factory to get some data back to the view but for some reason I can't print it.
I managed to get the ajax call to work.
If I type the
$scope.sampleStyles = [{ sample: 'text here', text : 'dasdas'}
and don't use the ajax call it works
UPDATE: if I add an alert before assigning to my scope it works (ajax has time to do his thing)
anyone know how to overcome that?
CODE:
var packageApp = angular.module("packageApp", []);
packageApp.controller("MyController", function($scope, myFactory){
$scope.sampleStyles = [];
function init(){
$scope.sampleStyles = myFactory.getSampleStyles();
}
init();
});
packageApp.factory('myFactory', function($http, $log){
var factory = {};
var sampleStyles = [];
var tempData = {};
factory.update = function(){
$.ajax({
type: 'POST',
url: '/account/fetch-sample-styles',
data: {
source: 'ajax'
},
success: function(data, textStatus, XMLHttpRequest){
tempData = data;
}
});
alert(tempData);
sampleStyles = tempData;
}
factory.getSampleStyles = function(){
factory.update();
return sampleStyles;
};
return factory;
});
Are you using the AngularJs $http service? If so it will return a promise which you then operate on. Here is more on promises from the AngularJs docs.
My guess is, you are using an ajax.get(...) with a success callback defined inside. The problem is probably due to the success callback not belonging to the "AngularJs world."
To fix this, you need to tell AngularJs that its scope has changed. Use the $[Root]scope.$apply() function, and have the scope injected into your service as a dependency.
Something like this inside the factory:
$.ajax({
url: "/api/some/end/:point",
...
success: function(data) {
$scope.$apply(function() {
$scope.sampleStyles = data; // etc
});
}
});
I strongly recommend that you look into the $http service, it makes the above code much nicer, and is designed to play nice with the $scope.
$http.get("/api/end/point").then(function(response) {
// response.data points at the page data sent back, assuming that your
// api endpoint sends back JSON of the likes of
// { status: "SUCCESS", styles: [...] }
$scope.sampleStyles = response.data.styles;
});
EDIT:
Now that you posted some code, it seems like the root of your issue is based on the fact that the ajax get is an async call. Why are you even messing with using a temporary variable? Why not the following?
factory.update = function(){
$.ajax({
type: 'POST',
url: '/account/fetch-sample-styles',
data: {
source: 'ajax'
},
success: function(data, textStatus, XMLHttpRequest){
sampleStyles = data;
}
});
}
If you really wanted to make the $.ajax call blocking, you can set async: false in the $.ajax properties.
EDIT 2:
Fixed some broken links, sorry I am a SO newb :(

Resources