I have some code in an Angular directive that looks like the following:
$scope.populateResults = function (list_results) {
$scope.$apply(function() {
console.log("Applying", list_results);
if (list_results.attractions.length === 0) console.log("Empty list");
$scope.attractionsList.list_results = [];
$.each(list_results.attractions, function(index, attraction) {
console.log("executing for each");
if (attraction) {
attraction.addedWaypoint = false;
$scope.attractionsList.list_results.push(attraction);
}
});
console.log($scope.attractionsList.list_results);
});
};
The function is passed as a callback to an AJAX request that is handled in another file. The console.log statements all print everything as expected, but the view does not change. When I print the value of $scope.attractionsList.list_results in another function, it shows that the list is empty. Shouldn't the fact that the $scope variable is changed in an $apply function mean that the view would be updated? This only happens occasionally. I don't see any errors coming back from the AJAX request, and all the console.log's in the $.each loop print as expected. Where else should I look for this bug?
Related
I'd like to check an image to see if the resource is available, before displaying it. I found a good way to do that in AngularJS here: Angular js - isImage( ) - check if it's image by url
But every time I try to implement it, an infinite loop is triggered, even if I reduce the function to its simplest form in a codepen : https://codepen.io/anon/pen/mBwgbE
test image function (js)
$scope.testImage = function(src) {
console.log('function triggered');
Utils.isImage(src).then(function(result) {
return "result";
});
};
Usage (html)
<h3>Image link broken<h3>
<p>{{testImage('anylink')}}</p>
<h3>Image link OK<h3>
<p>{{testImage('http://lorempixel.com/400/200/sports/1/')}}</p>
Can anyone explain this behaviour to me, and help me fix it?
Angular runs the digest loop, and interprets your template. It sees {{testImage('anylink')}} and calls into it. This calls into Utils.isImage, which creates a promise. The promise is returned to testImage, but testImage itself doens't return anything, so the template shows nothing.
A little later, the promise resolves. Angular sees this, so it runs the digest loop and interprets your template. It sees {{testImage('anylink')}} and calls into it. This calls into Utils.isImages, which creates a prom... oh crap, we're in an loop. It's going to call isImage, which creates a promise, and then when that promise resolves, it interprets the template again and calls isImage, starting all over.
Instead, i would recommend that when your controller loads, you create the promises right then, and when they resolve, you stick whatever values you need from them onto the controller as concrete values. Something like this:
function myController($scope, Utils) {
$scope.anyLink = null;
$scope.sportsLink = null;
Utils.isImage('anyLink')
.then(function (result) { $scope.anyLink = result });
Utils.isImage('http://lorempixel.com/400/200/sports/1/')
.then(function (result) { $scope.sportsLink = result });
$scope.heading = "My Controller";
}
And then on your template, interact with $scope.anyLink or $scope.sportsLink
$scope.testImage is automatically watched by angular to see change of testImage.
As a result you can stop infinite loop by using $scope.cache variable.
$scope.testImage = function(src) {
console.log('function triggered');
if($scope.cache[src] == "result")
return "result";
Utils.isImage(src).then(function(result) {
$scope.cache[src] = "result";
return "result";
});
};
It was tested on your codepen.
I'm using a request header with a key and a value to read from the AngularJS controller. If that header, in this case, is AppHeaders is valid and has a custom value, I need to trigger a click from that controller. The code that I have, if something like this:
$scope.$applyAsync(function() {
if (appHeaders != null && appHeaders['header-name'] != null) {
if (appHeaders['header-name'] == "custom-value") {
$('.class-name').click();
}
}
});
What's wrong? I make a deep debug into this and the conditional works fine. I guess that the problem is because the element on the DOM doesn't exist when the click is fired.
Thanks for your help guys! The final solution results on apply the $broadcast event on the functions declared and use a counter to validate the last cycle of the calls to trigger the click on the element.
// Here we declare an empty array to store each request
var requests = [];
// Our first function
$scope.firstFunction = function() {
// Execute broadcast event with the name of the function
$scope.$broadcast('requestEnded',requests.push('firstFunction'));
};
// Our last function
$scope.secondFunction = function() {
// Execute broadcast event with the name of the function
$scope.$broadcast('requestEnded', requests.push('secondFunction'));
};
// This listener is executed each time that requestEnded is fired
$scope.$on('requestEnded', function(event, countRequests) {
// Here we validate that the count of requests is the desire
if (countRequests == 2) {
// Trigger Click
$('.class-selector').click();
}
});
I write a post with the entire research explaining this:
https://jbrizio.github.io/2017/10/20/Trigger-click-when-determinate-requests-finish-using-AngularJS.html
I have a an issue where it seems as though my controller is not waiting on the http.get '.then'. I am getting data properly back but it seems as though another function is processing before the data is retrieved. I've gone through many posts and have tried many of the things mentioned in those posts, but it doesn't seem to help. I am using PHP to retrieve the data.
I have a HTML file that calls two functions (I had tried with one, but when that didn't work, I tried splitting up the functionality).
HTML of the calls
<form editable-form name="editableForm" onaftersave="fetch();updateDetailsData()" >
Controller functions
$scope.fetch = function() {
$http.get("api/checkSave/"+ JSON.stringify($scope.programDetails))
.then(function(data) {
$scope.okToSave = data.data.save;
$scope.missFields = data.data.fields;
console.log($scope.okToSave); // line #194
console.log($scope.missFields); // line #195
});
}
$scope.updateDetailsData = function(){
console.log($scope.okToSave); // line #202
}
What displays in the console shows:
undefined // line 202
false // line 194 - correct data
Object // line 195 - correct data
As you can see, it appears to be processing the function updateDetailsData before the fetch function finishes. I thought the then should make processing wait until the get is finished - the promise returned.
I need to do some processing in the updateDetailsData function based on the values in the $scope variables but when it gets there they are undefined.
Can someone help? I'm sure it is something little that I am missing, but I think I have tried just about all solutions provided on these forums and still end up with the same results.
The problem comes from: onaftersave="fetch(); updateDetailsData()".
The update function executes as soon as fetch returns, not as soon as fetch is resolved.
Rework your function a bit:
function fetch () {
return $http.get("api/checkSave/"+ JSON.stringify($scope.programDetails))
.then(function(data) {
// ...
});
}
$scope.fetchAndUpdate = function () {
fetch().then(updateDetailsData);
}
In the template:
<form editable-form name="editableForm" onaftersave="fetchAndUpdate()">
You're using promises, so you have to wire into them, they aren't blocking:
<form editable-form name="editableForm" onaftersave="fetch().then(updateDetailsData)" >
Controller functions
$scope.fetch = function() {
return $http.get("api/checkSave/"+ JSON.stringify($scope.programDetails))
.then(function(data) {
$scope.okToSave = data.data.save;
$scope.missFields = data.data.fields;
console.log($scope.okToSave); // line #194
console.log($scope.missFields); // line #195
});
}
$scope.updateDetailsData = function(){
console.log($scope.okToSave); // line #202
}
Your fetch function is calling $http.get which makes an asynchronous call to your server. What that means is that the call will return right away, it won't block (i.e. stop the code from executing) while it waits for the server to respond. That is why you provide a callback using the .then function.
So taken the way you have currently written it, it is working as designed. If you want to have updateDetailsData function be executed after your then code then you have to either put it inside of the then or chain the functions together like Michael P. Bazos or Matthew Berg suggested.
I'm running this code in an Angular service, immediately upon loading the page. The controller $scope is passed as an argument to the function this extract belong to. The function is a $q promise.
I am not able to figure out how can I let the controller know that scope.req.rows has been updated. If I add scope.$apply() right after it, I run into a running digest phase. If I use the $q resolve function, it returns and no more loop results are returned. scope.$evalAsync() and $timeout seem to have no effect (at least without setting a timeout > 0). Same goes for scope.$watch.
How can I let the controller know that values were updated?
for (var page = 0; page < nbPages; page++) {
(function (pageNum) {
that.get(url,
where,
res.pageSize * page,
res.pageSize)
.then(function Success(data) {
$log.info('Result of page ' + pageNum + ' received');
for (row in data) {
scope.req.rows++;
}
}).catch(function chunkFail(err) {
reject(err);
});
})(page);
I build simple demo and it`s works. Correct me if i wrong.
Updated:
i mocking http request and delay it form 1000ms to 30000ms. and i steel have't any scope problems.
http://jsbin.com/wasoxe/4/edit?js,output
I am using a jQuery method $.getJSON to update data in some cascading drop down lists, in particular, a default value if there is nothing returned for the drop down, e.g. "NONE".
I just want some clarification to how my logic should go.
var hasItems = false;
$.getJSON('ajax/test.json', function(data) {
hasItems = true;
//Remove all items
//Fill drop down with data from JSON
});
if (!hasItems)
{
//Remove all items
//Fill drop down with default value
}
But I don't think this is right. So do I enter into the function whether or not I receive data? I guess I really want to check the data object contains something - to set my boolean hasItems.
You should handle the check right inside the callback function, check the example here.
var hasItems = false;
$.getJSON('ajax/test.json', function(data) {
hasItems = true;
//Remove all items
//Fill drop down with data from JSON
if (!hasItems)
{
//Remove all items
//Fill drop down with default value
}
});
You want to do all checking of returned data inside the callback, otherwise that condition will be called before the callback has been called, resulting in it always being the initial value assigned.
You're dealing with asynchrony, so you need to think of the code you're writing as a timeline:
+ Some code
+ Fire getJSON call
|
| server working
|
+ getJSON call returns and function runs
The code inside the function happens later than the code outside it.
Generally:
// Setup any data you need before the call
$.getJSON(..., function(r) { //or $.ajax() etc
// Handle the response from the server
});
// Code here happens before the getJSON call returns - technically you could also
// put your setup code here, although it would be weird, and probably upset other
// coders.