I am fetching some data from my server and updating the DOM through Angular two way bindning. It is however not working as expected and I need to wrap it inside an ugly setTimeout function for the DOM to have time to update.
$http.post('myBackend.php', someData)
.then(function(res){
$scope.data = res.data;
doStuffWithDOMElements(); // Does not work
});
while this works:
$http.post('myBackend.php', someData)
.then(function(res){
$scope.someDataToPopulateDOMwith = res.data;
setTimeout(function(){ doStuffWithDOMElements();}, 50); // Yup, works
});
The line that gives error without the timeout "Can't read propertly of null" is this:
let y_0 = document.getElementById("l_0").offsetTop;
and in my index.html
<div id="l_{{$index}}" ng-repeat = "x in data"></div>
This is weird. Shouldn't every DOM element that is wrapped up in Angular "events" be updated automatically? $scope.$apply() does not work and shouldn't be necessary either. What's wrong here?
The need of $timeout comes every once in a while in angularjs.Most probably to init a jQuery plugin.
Your Error line:
let y_0 = document.getElementById("l_0").offsetTop;
is because your DOM has not yet been set and you are trying to get the element which yet has to be set or rather rendered in the DOM.
When you use $timeout,it should run after the DOM has been manipulated by Angular, and after the browser renders (which may cause flicker in some cases).That is why it is working in your case when you set the $timeout.
If you want to learn more about digest cycle. Your should know about $evalAsync as well.
If code is queued using $evalAsync from a directive, it should run after the DOM has been manipulated by Angular, but before the browser renders
If code is queued using $evalAsync from a controller, it should run before the DOM has been manipulated by Angular (and before the browser renders) -- rarely do you want this
If code is queued using $timeout, it should run after the DOM has been manipulated by Angular, and after the browser renders (which may cause flicker in some cases).
FurtherMore, Angularjs is a javascript framework. A browser has to do a number of things pretty much all at once, and just one of those is execute JavaScript. But one of the things JavaScript is very often used for is to ask the browser to build a display element. This is often assumed to be done synchronously (particularly as JavaScript is not executed in parallel) but there is no guarantee this is the case and JavaScript does not have a well-defined mechanism for waiting.
The solution is to "pause" the JavaScript execution to let the rendering threads catch up. And this is the effect that setTimeout() with a timeout of 0 does. It is like a thread/process yield in C. Although it seems to say "run this immediately" it actually gives the browser a chance to finish doing some non-JavaScript things that have been waiting to finish before attending to this new piece of JavaScript.
(In actuality, setTimeout() re-queues the new JavaScript at the end of the execution queue. See the comments for links to a longer explanation.)
IE6 just happens to be more prone to this error, but I have seen it occur on older versions of Mozilla and in Firefox.
Also, a lot has been written and explained about why the use of $timeout comes in handy time to time.
Links where you will find good explanation:
Why is setTimeout(fn, 0) sometimes useful?
AngularJS : $evalAsync vs $timeout
Breakup the Operations
One of the things to do is breakup the operations by chaining them.
$http.post('myBackend.php', someData)
.then (function onFulfilled (response) {
$scope.someDataToPopulateDOMwith = response.data;
return response;
}).then (function onFulfilled2 (response) {
doStuffWithDOMElements();
});
This allows the $q service to execute a digest cycle before invoking the second fulfillment handler. The AngularJS framework needs to do a digest cycle so the watch handlers for the ng-repeat directive have an opportunity update the DOM. The ng-repeat directive needs to finish updating the DOM before the doStuffWithDOMElements function can safely manipulate the DOM.
Use $timeout Service
Avoid using the raw browser setTimeout function, instead use the $timeout service. The AngularJS $q service then automatically does digest cycles.
Since the $timeout service returns promises, it can be used for chaining.
$http.post('myBackend.php', someData)
.then (function onFulfilled (response) {
$scope.someDataToPopulateDOMwith = response.data;
return response;
}).then (function onFulfilled2 (response) {
//return $timeout promise
return $timeout(function(){return response}, 1000);
}).then (function onFulfilled3 (response) {
doStuffWithDOMElements();
});
Because calling the then method of a promise returns a new derived promise, it is easily possible to create a chain of 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
Re-factor the Design
The AngularJS is a MVW* framework.
Avoid manipulating HTML DOM programmatically: Manipulating HTML DOM is a cornerstone of AJAX applications, but it's cumbersome and error-prone. By declaratively describing how the UI should change as your application state changes, you are freed from low-level DOM manipulation tasks. Most applications written with Angular never have to programmatically manipulate the DOM, although you can if you want to.2
Look into modeling what the doStuffWithDOMElements() function does and creating custom directives to watch that model and update the DOM. This fits better with the AngularJS framework and will avoid these timing issues.
in case of DOM manipulation in angular,it is well known.
see this SO link
Related
Problem
While the $digest-cycle in my app still runs quite fast, i noticed that some callbacks (which are bound in the templates for example via ng-if) are called way more often than i expected. This goes up to 100+ calls on a single UI-interaction (where I would generally expect something between 3 or 10 calls at most).
I would like to understand why the callbacks are called this often and possibly reduce the number of calls to prevent future performance Issues.
What I tried
From my understanding the described behaviour means that the $digest-cycle takes up to a few-hundred loops to remove all dirty-flags and make sure that all rendered nodes are up-to-date.
I simplified several callbacks to just return true - instead of evaluating some model-values - which had no effect on the number of $digest calls at all. I also checked the Performance-Tab in the Chrome-developer-Tools which only told me that the calls themselves are executed within a few ms.
For trouble-shooting i also removed several ng-repeat blocks and angular-filters throughout the application since those obviously apply several watches to be evaluated in the $digest loop. This had no impact on the number of calls to the callback-functions either.
Thus i guess i need a more sophisticated tool or method to debug the (number of) $digest calls throughout my application to even figure out where all those calls are coming from and how to reduce them.
Questions
Which tools and methods can I use to evaluate the performance of the $digest-loop (and especially the number of loops) in my angular-application?
How do I reduce the number of calls to callbacks which are bound in a template?
I think to answer the second question it would already be helpful to understand what can cause additional calls to foo() in a setup like this:
<div ng-if="ctrl.foo()">
<!--<span>content</span> -->
</div>
First thing what does actually digest cycle in angularJS?
1. Its process in which angular framework check for all two way binding variable changes by its own continuously.
2. When ever user interact and change two way binding variable then it get fire.
3. programmatically(in controller, service or factory) two way binding variable get changed
Above are reasons to fire digest cycle call...
Which entity are part of digest cycle?
1. $watch added on variables.
2. ngModel, ng-model iteself internally add $watch on varaible
Basically $watch function.
What we can do to avoid $digest/avoid call to $watch?
Think about variable using in UI that does this variable need to be two way binding?
If answer is NO then just go for one-way bind syntax
Avoid use of watch function from controller, service, factory
Then how can I watch it...
RX js is right now best library which can help to overcome this issue. Its just one option.
Use getter setter
How?
mymodule.controlle('ctrName', ctrClass);
ctrClass {
constructor($scope) {
this.myVar1 = null;
this.myVar2 = null;
}
set myVar1(value) {
// either code which i want in watcher
// or
// Some function which i want to execute after value get set
this.afterSet();
return this.myVar1 = value;
}
afterSet() {
}
}
Use controllerAs feature of angular
Create directives with isolated scopes
About tool:
To validate angular application Batarange is good tool.
I'm just curious why the following code is working:
<script>
var app = angular.module('VEL',[]);
app.controller('VELControl', function($scope, $http) {
$.getJSON("../json/dict.php?p="+$_GET['p'], function(response){
$scope.velData = response;
});
alert($scope.velData); // also works with alert($scope);
});
But when I remove the alert(...) it stops working.
Could anyone explain me why or refer to a documentation?
The main problem is that you're using jQuery's $.getJSON() method, which Angular doesn't know anything about. Angular is great at tracking when asynchronous events occur and updating the view when they complete, but you have to use Angular's services to take advantage of this.
Angular gives us the $http service for making AJAX calls. Since $http is tied into Angular's digest cycle (read up on this if you've never heard of it, it's critical to understanding how Angular works), Angular will update the view after $http returns data and its callbacks are run.
Rewrite your controller like this and it should work:
app.controller('VELControl', function($scope, $http) {
$http.get("../json/dict.php?p="+$_GET['p']).then(function(response){
$scope.velData = response.data;
});
});
As an aside, your alert() working with the old method was probably a fluke based on how fast your server returned the data. As I understand it, alert() runs asynchronously anyway, so when you called alert($scope.velData), this was probably pushed onto the end of the browser's execution queue, and before it was reached the AJAX call returned and set the value you needed. If your server had been slower, you probably would have alerted undefined and then received your data. If you have control over the server (which it looks like you do), this would probably be a fun experiment to try: put in a sleep(10) or some similar wait to force the server to take a long time; try different values and see what happens with that alert() statement.
I've got this problem that I couldn't find a solution for by googling.
I've got a library, that I'm using (and do not want to edit, unless it's really necessary) that allows the user to select an item, then calls my custom callback function to modify the item and then continues working with it.
I need to perform some asynchronous tasks on it, which may take some time. This creates a race condition as my async tasks have not yet finished when the callback function is finished and the library continues its work on the item.
library.onItemSelectionCallback = function (item) {
myService.modifyItem(item).then(
function (modifiedItemProperty) {
item.newProperty = modifiedItemProperty;
});
myService.anotherModifyItem(item).then(
function (modifiedItemProperty) {
item.existingProperty = modifiedItemProperty;
});
}
How do I wait for both of my async tasks to finish, before allowing this callback to finish?
Only thing I could think of is looping with while and sleep every hundred or so milliseconds until both of the promises have been resolved, but that doesn't seem to be a very good solution.
I understand that this makes async requests quite synchronous and might possibly be detrimental for UX, but do not really see another way out.
EDIT: I know that i'm risking with removing the generic nature of the question and thus making it too localized, I will say that I'm trying to use angular-file-upload module, specifically, trying to mount a custom imageService, that would resize the picture before it's upload. I'm mounting it on the onBeforeUploadItem callback. The idea is that creating the resized image may take a while and that is why I need to return a promise from my imageService, that needs to be resolved before upload.
If modifyItem and anotherModifyItem work independently (that is, one does not rely on the other), you can just pipe them both into $q.all, eg
library.onItemSelectionCallback = function(item) {
var promises = {
newProperty: myService.modifyItem(item),
existingProperty: myService.anotherModifyItem(item)
};
return $q.all(promises).then(function(values) {
return angular.extend(item, values);
});
}
This will return a promise that resolves with item.
For the first part of my question -- Yes, I guess the only way to really wait for those two promises to be resolved would be something with a while and sleep, making them synchronous, which would probably work and not even be that bad (except for the site pausing until the requests are fulfilled), but would make me feel very, very bad about myself as a person and how my actions affect this world.
It is not possible to correctly mix callbacks and promises without hacks afaik.
For the second part of my question -- as per comments of #georgeawg, figured that an AngularJS module that implements HTML5 API and callbacks instead of $http service and promises is not how a good AngularJS module should be implemented, and so I moved towards a different module ng-file-upload, which, even though one could argue is less stylish, does the job very well and in an Angular way (ng-file-upload provides a simple $upload service, that returns a promise. If you want to modify files before upload, suggested way is to simply $watch and catch the moment user drag-drops or selects a file.).
I'm wondering if there's a way to cause JavaScript to wait for some variable-length code execution to finish before continuing using events and loops. Before answering with using timeouts, callbacks or referencing this as a duplicate, hear me out.
I want to expose a large API to a web worker. I want this API to feel 'native' in the sense that you can access each member using a getter which gets the information from the other thread. My initial idea was to compile the API and rebuild the entire object on the worker. While this works (and was a really fun project), it's slow at startup and cannot show changes made to the API without it being sent to the worker again after modification. Observers would solve part of this, and web workers transferrable objects would solve all, but they aren't adopted widely yet.
Since worker round-trip calls happen in a matter of milliseconds, I think stalling the thread for a few milliseconds may be an alright solution. Of course I would think about terminating in cases where calls take too long, but I'm trying to create a proof of concept first.
Let's say I want to expose the api object to the worker. I would define a getter for self.api which would fetch the first layer of properties. Each property would then be another getter and the process would continue until the final object is found.
worker.js
self.addEventListener('message', function(event) {
self.dataRecieved = true;
self.data = event.data; // would actually build new getters here
});
Object.defineProperty(self, 'api', {
get: function() {
self.dataRecieved = false;
self.postMessage('request api first-layer properties');
while(!self.dataRecieved);
return self.data; // whatever properties were received from host
}
});
For experimentation, we'll do a simple round-trip with no data processing:
index.html (only JS part)
var worker = new Worker("worker.js");
worker.onmessage = function() {
worker.postMessage();
};
If onmessage would interrupt the loop, the script should theoretically work. Then the worker could access objects like window.document.body.style on the fly.
My question really boils down to: is there a way to guarantee that an event will interrupt an executing code block?
From my understanding of events in JavaScript, I thought they did interrupt the current thread. Does it not because it's executing a blank statement over and over? What if I generated code to be executed and kept doing that until the data returned?
is there a way to guarantee that an event will interrupt an executing code block
As #slebetman suggests in comments, no, not in Javascript running in a browser's web-worker (with one possible exception that I can think of, see suggestion 3. below).
My suggestions, in decreasing order of preference:
Give up the desire to feel "native" (or maybe "local" might be a better term). Something like the infinite while loop that you suggest also seems to be very much fighting agains the cooperative multitasking environment offered by Javascript, including when thinking about a single web worker.
Communication between workers in Javascript is asynchronous. Perhaps it can fail, take longer than just a few milliseconds. I'm not sure what your use case is, but my feeling is that when the project grows, you might want to use those milliseconds for something else.
You could change your defined property to return a promise, and then the caller would do a .then on the response to retrieve the value, just like any other asynchronous API.
Angular Protractor/Webdriver has an API that uses a control flow to simulate a synchronous environment using promises, by always passing promises about. Taking the code from https://stackoverflow.com/a/22697369/1319998
browser.get(url);
var title = browser.getTitle();
expect(title).toEqual('My Title');
By my understanding, each line above adds a promise to the control flow to execute asynchronously. title isn't actually the title, but a promise that resolves to the title for example. While it looks like synchronous code, the getting and testing all happens asynchronously later.
You could implement something similar in the web worker. However, I do wonder whether it will be worth the effort. There would be a lot of code to do this, and I can't help feeling that the main consequence would be that it would end up harder to write code using this, and not easier, as there would be a lot of hidden behaviour.
The only thing that I know of that can be made synchronous in Javascript, is XMLHttpRequest when setting the async parameter to false https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Parameters. I wonder if you could come up with some sort of way to request to the server that maintains a connection with the main thread and pass data along that way. I have to say, my instinct is that this is quite an awful idea, and would be much slower than just requesting data from the main thread.
For what I know, there is not something native in JS to do this but it is relatively easy to do something similar. I made one some time ago for myself: https://github.com/xpy/whener/blob/master/whener.js .
You use it like when( condition, callback ) where condition is a function that should return true when your condition is met, and callback is the function that you want to execute at that time.
having a small issue with AngularJS. See the following:
$http({method: 'GET', url: 'some/url/withJSON.json'})
.success(function(data)
{
$scope.data = data
$animations._load('arg1')
})
$animations is a registered service that does some stuff. Occasionally (maybe 20%) of the time the _load() method will run before the window has been painted/the digest loop completes, meaning the visualisation hasn't completed. By wrapping _load() with a setTimeout() of 10ms this has resolved it, but is there an implicit/explicit way in AngularJS to execute a callback once the digest loop has finished?
Without more information, I would suggest $timeout(fn) will be the best way to do this. It basically makes sure there's a break after angular completes processing for the browser to render. Roughly it is equivalent to setTimeout(fn, 0), so is what you have already suggested, but it is mockable for testing.
There is also $scope.$evalAsync(fn) that would normally render after the current digest cycle but before the DOM renders. I tend to prefer $evalAsync as it makes things easier for me to reason about, and won't allow other non-angular happenings to occur in the meantime, but it depends which works for you.
This answer has more detailed information about the two methods.