AngularJS Redraw issue? Chrome only - javascript

Super new to AngularJS so please be patient with me :)
We're using it to poll our API for updates while we process a repository (and after). On one particular page the data is loaded but not drawn to the screen. Highlighting the content, or resizing the browser causes a redraw and shows all angular values that weren't there a moment ago! Latest Chrome!
Just look: Everything starts at "0" or "-" but highlighting the page reveals "Optimized Images" and "Filesize Savings" content changes.
Live example:
MAY REQUIRE YOU HIT REFRESH TO HAVE THE ANGULAR DRAW FAIL
REQUIRES CHROME ~ Version 31.0.1650.63 m
It works on Firefox!?!
http://crusher.io/repo/alhertz/didthesaintswin/63f49d36e709dea172fe7e4bbacbcfd834f9a642
This appears to be very similar to this question, but there is no nested controller issue I can detect: Update page contents after GET request in AngularJS
When I try to add a $scope.$apply() I get this error: http://docs.angularjs.org/error/$rootScope:inprog?p0=$apply
This is the relevant code in the angular controller (coffeescript):
do poll = ->
$http.get("#{$location.absUrl()}/poll.json").success((data, status, headers, config) ->
if $scope.continuePolling
#console.log("still polling #{$location.absUrl()}")
$scope.data = data
$scope.TotalOptimizedImage = $scope.CalcTotalOptimizedImage(data.images)
$scope.TotalImgSize = $scope.CalcTotalImgSize(data.images)
$scope.SavedImgSize = $scope.CalcSavedImgSize(data.images)
$scope.TotalSavings = ($scope.TotalImgSize - $scope.SavedImgSize + 0)
$timeout(poll, 10000)
)
Really not sure how to break this apart for fixing. Thoughts?

It looks like you need to call $scope.apply inside the callback to the http.get. The reason is that the callback will happen outside the controller digest. Sorry I'm not adept at coffee script but something like this:
$http.get("#{$location.absUrl()}/poll.json").success((data, status, headers, config)
if $scope.continuePolling
$scope.$apply(function() { // wrap the stuff you want to update in the $scope.$apply
#console.log("still polling #{$location.absUrl()}")
$scope.data = data
$scope.TotalOptimizedImage = $scope.CalcTotalOptimizedImage(data.images)
$scope.TotalImgSize = $scope.CalcTotalImgSize(data.images)
$scope.SavedImgSize = $scope.CalcSavedImgSize(data.images)
$scope.TotalSavings = ($scope.TotalImgSize - $scope.SavedImgSize + 0)
});
$timeout(poll, 10000)
)
I would suggest that you use a safeApply function and there is a great timeFunctions service that might help you with the polling that I've used quite successfully in a couple projects. You can find it in this answer.

Related

firefox times out after 2 minutes on ajax-call

I'm trying to make an Ajax call to a page that takes too long to load. I want to wait for the data to load, but firefox times out after two minutes. On the web you'll find the settings that are in the screenshot, but it doesn't help (and I've restarted firefox). How can I make sure firefox (or any other browser) waits for the call to load?
Of course this is only a temporary solution, but I want it to work for now.
use the timeout, parameter while making ajax call
Vikram was right that it was not the browser. It was in Angular. I don't know Angular so I have to hack. I'll show you my hack but don't use it. Consider it as a hint to find a better solution. Also, you probably don't need all the below code changes to have a hack that works.
angular.js
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
// TODO(vojta): fix the signature
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
// DO NOT COMMIT :) (added line)
timeout = 5000001; // added line
$browser.$$incOutstandingRequestCount();
url = url || $browser.url();
....
function timeoutRequest() {
jsonpDone && jsonpDone();
xhr;// && xhr.abort();
}
app.js:
angular.module('MyApp', [])
  .config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.timeout = 5000;
}]);
But in addition, this proxy to my vagrant machine was causing a timeout:
https://github.com/drewzboto/grunt-connect-proxy
web-incoming.js
function timeout(req, res, options) {
// DO NOT COMMIT
req.socket.setTimeout(600000);
//if(options.timeout) {
// req.socket.setTimeout(options.timeout);
//}
},

$http.success callback not being called on IE11

I have an AngularJS application that loads some data using $http service, then based on the revised data it updates corresponding html elements and updates the UI. Here is the code:
app.controller("RankingCtrl", function ($scope, PostModel, DeserializePosts, $http, $cookies){
var mainContainer = $('#mainContainer');
$scope.error = null;
function loadTop(){
$http({
type: 'GET',
url: '/ranking',
headers: {
'X-Requested-With': 'XMLHTTPRequest',
}
}).success(function(topPosts){
var mostLikedPosts = DeserializePosts(topPosts.mostLiked);
var mostViewedPosts = DeserializePosts(topPosts.mostViewed);
var mostCommentedPosts = DeserializePosts(topPosts.mostCommented);
$scope.topLiked = mostLikedPosts;
$scope.topViewed = mostViewedPosts;
$scope.topCommented = mostCommentedPosts;
}).error(function(error){
errorHandler(error.statusCode, error);
}).finally(function () {
$('#mainContainerSpinner').hide();
mainContainer.fadeIn();
});
}
loadTop();
});
My problem is, apparently the .success function is not being called, since the .finally function does get executed but the UI is not updates with any of the downloaded elements. If I ctrl-R the IE browser window, then the function works properly and the UI shows the expected elements. This issue does not happen on Chrome or FireFox.
Interestingly, when I open the F12 dev tools from IE to debug this JS code, the issue does not happen. Looks more like a bug on IE11 but I was wondering if anyone else has seen this behavior and if there are workarounds to try. Currently, my users need to refresh the page when they visit it, as only a page refresh updated the UI as expected on this AngularJS controller.
UPDATE: the UI is not getting updated because the .success function has a forEach instruction that is throwing an exception. This is happening because the browser is sending the $http request without the 'X-Requested-With' header that is specified in the code above, so the response it receives is html instead of json. Forcing a browser refresh does make the browser add this header to the http request.

Tab-to-tab communication using BreezeJS and localStorage

I'm using BreezeJS and storing/restoring data in local storage. That's working great. The problem occurs when the user opens multiple tabs. Changes in each tab clobber each other. Changes should be synchronised between tabs.
NB: BreezeJS will take care of merging changes, I just need to deal with race conditions between tabs.
var stashName = 'stash_everything';
window.setInterval(function () {
var exportData = manager.exportEntities();
window.localStorage.setItem(stashName, exportData);
}, 5000);
addEvent(window, 'storage', function (event) {
if (event.key == stashName) {
var importData = window.localStorage.getItem(stashName);
manager.importEntities(importData);
}
});
I've tried listening to the 'storage' event, but I haven't been able to get it working successfully. I either still clobber changes, or get into an infinite loop.
The crux of the issue is that I'm just saving on a timer; if I only saved after user interaction, then I'd avoid (most) race conditions. There's no 'has the user changed anything since last time I asked you' call in breeze, though, as far as I can tell.
Does anyone have advice on how to approach this?
Hmm this doesn't seem like it is impervious to having problems for many reasons but the main one would be that you still won't prevent concurrent saves from each tab with different data sets. That being said, if you are comfortable with the fact the two caches could be out of sync just use some unique identifier -
Somewhere in your app on load -
var stashName = 'stash-everything-' + new Date().getTime();
window.setInterval(function () {
var exportData = manager.exportEntities();
window.localStorage.setItem(stashName, exportData);
}, 5000);
Now each tab would have a unique set of data to work with.

Why this polling stops working after idling for some time?

I am using AngularJS to constantly poll for new data through HTTP POST. An alert will be sent when new data is received. The code which is inside a controller looks something like this;
var poll = function() {
$http.get('phones.json').success(
function(data)
{
new_val = data.val;
if ( (new_val!== old_val) )
{
$window.alert("AlertEvent");
}
old_data = new_val;
$timeout(poll, 500);
}
);
};
poll();
This code works when the html page is refreshed. Working means when phones.json is changed, an alert will appear. However, if I leave the page on for, say 30 minutes, and come back later, it stops working. I have to refresh the page to get it working again.
What else did I miss out? What did I do wrong? Could it due to some caching mechanism?
Thank you very much.
EDIT: I found the cause. It is indeed due to the browser reading from cache. I can see this using Chrome Developer tools. How can this caching be disabled for this html page only?
You may be able to bust the cache by doing something like this:
$http.get('phones.json?v=' + Date.now())
Depending on how your back-end is set-up you may need to adjust it to accept that.

Is it possible to define a layer which will retry to load, e.g. with exponential back-off?

I am using OpenLayers to connect to a home-grown server, and unlike professional grade servers like Google or Cloudmade that box will actually take a while to calculate the result for a specific tile. And as it is a mathematical function I am plotting, there is no big chance to accelerate the server or even pre-render the tiles.
My initial trials with Leaflet quickly came to the conclusion that Leaflet actually leaves all of the reloading and load-error handling to the browser, while OpenLayers at least has an event that is fired when the tile server does return with an error code.
The idea I am following was to basically start rendering a tile when it was requested and fire an HTTP 503 immediately, relying on the client to try again.
To try again, I implemented a simple layer like this:
var myLayer = new OpenLayers.Layer.OSM.MYLayer("mine", {
'transparent':"true",
'format':"image/png",
'isBaseLayer':false});
myLayer.events.register("tileerror", myLayer, function (param) {
// Try again:
var targetURL = param.tile.layer.getURL(param.tile.bounds);
var tile = param.tile;
tile.timeout = tile.hasOwnProperty("timeout") ? tile.timeout * 2 : 1000;
setTimeout(function (tileToLoad, url) {
if (tileToLoad.url === url) {
tileToLoad.clear();
tileToLoad.url = url;
tileToLoad.initImage();
}
}.bind(undefined, tile, targetURL), tile.timeout);
});
I figured out the code required to reload a tile from the source of OpenLayers, but maybe there is a cleaner way to accomplish this.
My problem is: The tiles themselves are reused, as are the divs in the DOM, so the reload procedure might actually try to reload a tile into a DIV that long as been successfully reused, e.g. because the user scrolled to someplace else where the server was able to provide data quickly.
The question I guess boils down to - is there an official way to use the tileerror event to simply try to reloading, or at least a simpler way in the API to trigger a reload? I spent quite a while in the source of OpenLayers itself but couldn't shed light on why it is still going wrong (the test for tileToLoad.url == url didn't really do it).
Thanks for your help!
Ok, after some more trial and error I found that I could actually add an eventListener to my Layer class, which will do what I want - try to reload the tile again after a certain wait. The trick was the consecutive call of setImgSrc() for cleanup and to draw with the true parameter, which effectively is an (undocumented) force flag. Thanks to the code!
OpenLayers.Layer.OSM.MyLayer= OpenLayers.Class(OpenLayers.Layer.OSM, {
initialize:function (name, options) {
var url = [
"xxxx"
];
options = OpenLayers.Util.extend({
"tileOptions":{
eventListeners:{
'loaderror':function (evt) {
// Later reload
window.setTimeout(function () {
console.log("Drawing ", this);
this.setImgSrc();
this.draw(true);
}.bind(this), 3000); // e.g. after 3 seconds
}
}
}
}, options);
var newArguments = [name, url, options];
OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
},
CLASS_NAME:"OpenLayers.Layer.OSM.MyLayer"
});
You should have a look at the following resources:
http://dev.openlayers.org/docs/files/OpenLayers/Util-js.html#Util.IMAGE_RELOAD_ATTEMPTS
http://dev.openlayers.org/apidocs/files/OpenLayers/Tile-js.html
http://dev.openlayers.org/docs/files/OpenLayers/Tile/Image-js.html

Categories

Resources