I have recently built a feature on our web application that uses AngularJS and I am having some issues with IE 11 not properly $apply()ing data changes to the DOM. For some reason this only occurs sometimes and never occurs when I try to debug the problem which makes it seem like a timing issue.
Here is the function that gets called when the problem occurs.
$scope.createThrottling = function (sources) {
MYAPP.modals.Throttling('New', sources, API, function () {
$scope.isLoading = true;
$scope.$apply();
API.Migrations.getThrottles({ id: jQuery.getUrlVar('id') }, function (data) {
$scope.Throttles = data.Throttles;
$scope.isLoading = false;
// THE PROBLEM IS RIGHT HERE
});
});
}
The comment above shows where the problem seems to be stemming from. At this point in the execution of the code, Angular should automatically be checking for a change in $scope.Throttling and then make a change to the DOM accordingly, however, for some reason in IE 11, on the first visit to the page the binding is not occurring.
Subsequent refreshes of the page cause the binding to work however which seems very strange. It is as if $scope.$apply() is needed after API.Migrations.getThrottles is finished, but I cannot do that because Angular throws a JS error saying that it is already digesting.
Some things to note:
This only happens in IE
This only happens on the first visit to a page per load of the browser (I can hit F5 and try the same exact thing and it will work)
Could this be occurring because my API.Migrations.getThrottles call is inside a callback function for the MYAPP.modals.Throttling module which is outside of Angular completely?
When I try to debug the JS function above, everything works just fine which makes it seem like a timing issue
Any help to finding out what is causing this bug would be much appreciated!
Thanks
for the reference, found the solution here: http://www.oodlestechnologies.com/blogs/AngularJS-caching-issue-for-Internet-Explorer
$httpProvider.defaults.cache = false;
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
}
$httpProvider.defaults.headers.get['If-Modified-Since'] = 'Thu, 01 Jan 1970 00:00:00 GMT';
I think I have found my problem! I finally tried printing the data that is coming back from the server in the DOM without using the debugger and I realized that the API response is NOT giving me back new data! It is sending back cached data that is no longer valid which is why the objects are not showing up in the DOM. This also explains why using the debugger works because it forces each API call to not be cached!
I was able to fix this problem in my $resource by adding { _: Date.now() } in my params object in my $resource. This appends _=1234567890 to all GET calls for this $resource which forces IE to not cache
Related
I wrote the following code for debugging puposes:
(function () {
"use strict";
// The initialize function is run each time the page is loaded.
Office.initialize = function (reason) {
$(document).ready(function () {
// Use this to check whether the API is supported in the Word client.
if (Office.context.requirements.isSetSupported('WordApi', 1.1)) {
// Do something that is only available via the new APIs
Office.context.document.addHandlerAsync(Office.EventType.DocumentSelectionChanged, onSelectionChanged);
}
else {
// Just letting you know that this code will not work with your version of Word.
$('#status').html('This code requires WordApi 1.1 or greater.');
}
});
};
var c = 1;
function onSelectionChanged(eventArgs) {
c++;
$('#status').html('onSelectionChanged() call '+c+);
}
})();
This code only sometimes reacts to changes. Sometimes reeaaly slow. Sometimes (I guess, if it is too slow and there have been multiple changes in between, it does not recognize them und prints onSelectionChanged() call 4 after a while, even though, there have been many more changes.
Other times, if I close Word, and open it again, it just works as a charm. Then I close it and open it again, and again, it fails - It is completely inconsistant. Thereby this feature is effectively not usable.
I tested this on different machines, different versions of Windows and it occures independend of the utilization of the system.
Any ideas?
Unfortunately I was not able to repro your issue. The event works quite consistently.
Its not related but is there a specific reason why you are checking the 1.1 requirement set? This event was shipped on the first release of the API so that's not needed.
If you can provide your build number and a sample document and video of whats going on we could investigate in more detail.
thanks!
I have code like this:
someRequestToServer().then(response => {
console.log('phase before nav = ' + $rootScope.$$phase);
$location.path('/somepath');
console.log('phase post nav = ' + $rootScope.$$phase);
return response;
});
both of the print lines return with '$digest' which implies I am in a digest cycle ... yet the $location.path call is simply not working -- specifically browser URL browser doesn't change. In fact this is the only part that does work.
Also, after the call, location.path() returns the proper URL, what is failing is its ability to propagate to the browser and automatically route the page. While I am able to force the routing with a $route.reload() (as suggested in comments), I still can't get the browser URL to change which puts the page in bad place where the URL and the state of the page don't match.
I was sort of able to work around this by using window.location.href directly, but for various reasons this is not a great solution.
there is a lot about $location.path not working because it is called outside of an angular scope (therefore you have to force it with an apply), but any ideas when it seems like it is in digest cycle?
I've come across some weird behavior in IE11 when it comes to overriding functions in javascript.
On a webpage i have the following code:
(function ()
{
var tempSetItem = window.localStorage.setItem;
window.localStorage.setItem = function (key, data)
{
var newdata = JSON.stringify(data);
newdata = JSON.parse(newdata);
newdata.CachedDate = new Date();
tempSetItem.call(window.localStorage, key, JSON.stringify(newdata));
};
})();
This should simply override the localStorage.setItem() function.
However, i ran into some trouble with the code and decided to comment it out.
When i refreshed the page (cleared cache ofcourse) the same problem was still there.
I soon realised that IE had somehow cached my override.
I searched through all my files to make sure i didn't override it anywhere else and i even tried to set it to null manually through the console (In other words, it should not be null after a refresh) and after a refresh, it was now null.
It seems to be cached per domain or per URL in some way and i don't know how to clear this cache or if it's even possible.
I even tried repairing IE but localStorage.setItem is still null on my webpage.
I still don't know why the overriding gets cached but i found a way to reset it.
Calling localStorage.clear() resets the entire localStorage-object, including the setItem-function.
To avoid the rest of the problem i simply made a normal wrapper-function instead of overriding the original and called that from all the places where i would otherwise call localStorage.setItem().
I'm pretty new to Angular and I'm using firebase as my backend. I was hoping someone could debug this issue. When I first go to my page www.mywebsite.com/#defaultHash the data doesn't load into the DOM, it does after visiting another hash link and coming back though.
My controller is like this:
/* initialize data */
var fb = new Firebase('https://asdf.firebaseio.com/');
/* set data to automatically update on change */
fb.on('value', function(snapshot) {
var data = snapshot.val();
$scope.propertyConfiguration = data.products;
console.log($scope.propertyConfiguration);
console.log("Data retrieved");
});
/* save data on button submit */
$scope.saveConfigs = function(){
var setFBref = new Firebase('https://asdf.firebaseio.com/products');
setFBref.update($scope.propertyConfiguration);
console.log("configurations saved!");
};
I have 3 hash routes say "Shared", "Registration", and "Home" with otherwise.redirectTo set to "Shared".(They all use this controller) Here's the error that occurs: (all "links" are href="#hashWhereever")
1) Go to website.com/#Shared or just refresh. Console logs $scope.propertyConfiguration and "Data Retrieved". DOM shows nothing.
2) Click to website.com/#Registration, console logs $scope data properly, DOM is loaded correctly.
3) Click back to website.com/#Shared, console logs $scope data properly yet this time DOM loads correctly.
4) Refresh currently correctly loaded website.com/#Shared. DOM elements disappear.
Since $scope.data is correct in all the cases here, shouldn't Angular make sure the DOM reflects the model properly? Why is it that the DOM loads correctly only when I am clicking to the page from another link.
I can "fix" it by adding window.location.hash = "Shared" but it throws a huge amount of errors in the console.
FIXED:(sorta)
The function $scope.$apply() forces the view to sync with the model. I'd answer this question myself and close it but I'm still wondering why the view doesn't load correctly when I correctly assign a value to $scope. If Angular's "dirty checking" checks whenever there is a possibility the model has changed, doesn't assigning a value to $scope overqualify?
Angular has no way to know you've assigned a value to $scope.variable. There's no magic here. When you run a directive (ng-click/ng-submit) or Angular internal functions, they all call $apply() and trigger a digest (a check of the dirty flags and update routine).
A possibly safer approach than $apply would be to use $timeout. Currently, if you call a write op in Firebase, it could synchronously trigger an event listener (child_added, child_changed, value, etc). This could cause you to call $apply while still within a $apply scope. If you do this, an Error is thrown. $timeout bypasses this.
See this SO Question for a bit more on the topic of digest and $timeout.
This doc in the Angular Developer Guide covers how compile works; very great background read for any serious Angular dev.
Also, you can save yourself a good deal of energy by using the official Firebase bindings for Angular, which already take all of these implementation details into account.
Vaguely Related Note: In the not-too-distant future, Angular will be able to take advantage of Object.observe magic to handle these updates.
I'm working with Solr 4.3.0 and implementing Ajax-Solr for the interface. However, Ajax-Solr does not save state automatically. There is a ParameterStore and ParameterHashStore method but they don't work with legacy browsers. I used my google-fu and found the following but it doesn't work as intended:
https://github.com/evolvingweb/ajax-solr/pull/23
...with a few more resources I came up with this:
<script>
var Manager;
(function ($) {
Manager.setStore(new AjaxSolr.ParameterHashStore());
Manager.store.exposed = [ 'fq', 'q', 'start' ];
Manager.init();
// Establish Variables
var History = window.History; // Note: We are using a capital H instead of a lower h
if ( !History.enabled ) {
// History.js is disabled for this browser.
// This is because we can optionally choose to support HTML4 browsers or not.
return false;
}
State = History.getState(),
// Bind to State Change
History.Adapter.bind(window,'statechange',function(){ // Note: We are using
statechange instead of popstate
// Log the State
var State = History.getState(); // Note: We are using History.getState() instead
of event.state
History.log('statechange:', State.data, State.title, State.url);
});
// Log Initial State
History.log('initial:', State.data, State.title, State.url);
})(jQuery);
</script>
But it doesn't work. The Forward and Back buttons are broken in all browsers and nothing gets logged to the console.
What am I missing or is v4.3.0 inherently borked right now and needs a patch?
Would greatly appreciate any help. Thank you!
I know nothing about AJAX-Solr. But I think discussing some few things with you might help.
If AJAX-Solr works the same way the normal AJAX do, then AJAX-Solr will execute some server-side function and outputs it in client-side (of course without refreshing the actual page). As consequence, you should put the function triggering the ajax call inside your History.Adapter.bind(window,'statechange',function().
About State = History.getState(); it is responsible of returning the state data of a pushed state in history stack (url, title, ajax parameters). please to read the doc about hisrory.js on github. Note also that you are using a comma instead of semicolon in your code. In addition, you are calling this function twice. Call it only one time inside your Bind function to get the state parameters and use them in your ajax call (in order to refresh the part of your page while navigating over brower back-forward buttons).
I advise you to read also Back-Forward buttons of browser are showing weird behaviour. History.js, maybe it will help you understand positionning of ajax regarding Bind.
Good luck.