Here is jsFiddle if you like to see the code first
What I want to do is simple: do some (actually a lot) of the Javascript level from jQuery and jsPlumb libraries after ALL the views in the current route have completed rendering. Having received some help from this solution and finding out that the latest update had just been updated before afterRender , I decided to update. Now I have an almighty afterRender hook.
All the views that I insert in dom have the block class associated with them. So I tested to see if all the views in dom use $('.block').size() and see if it matches the expected number.
However, I have to face some problems when I try to use this hook in my application. First I tried to call afterRender in the router after connectOutlets calls. I always get only 1 block when I print the number of blocks in dom, while there should be 10.
If I put this code in didInsertElement my local block view:
didInsertElement: () -> knob = this.$("##{@get 'knobId'}") name = this.$(".name") main = this.$(".main")
Then I get the following output:
>> block count: 1 >> block count: 10 >> block count: 10 >> block count: 10 >> block count: 10 >> block count: 10 >> block count: 10 >> block count: 10 >> block count: 10 >> block count: 10
For some reason, the first iteration, I get only 1 block in dom, and after that I get all 10. I'm not sure why. But the main problem here is that if I put the hook inside didInsertElement , the code will be executed for an arbitrary number of views depending on the data (in this example 10). However, I want to run this code only after all the views have finished rendering.
Please note that the view I'm working with has nested data. I tried to reproduce this on jsFiddle, but I failed in the sense that everyone seems to find and dandy on the violin. Maybe because my views are large and complex, does this cause some synchronization problems? In any case, I think we can use the violin as a way to discuss the solution, and I can check it locally.
One hack I tried to solve the problem was to schedule my code to run after a Ember.run.later delay using Ember.run.later . This solved the problem on my local machine. However, using a timer for this is a very sheep dung and does not work reliably, as different browsers or machines may take longer to render views.
This is what needs to be reproduced, and I have been trying for a long time to find a solution. I would appreciate any help in reproducing the problem here or finding a solution.
Edit (workaround):. Thank you for helping me fix this problem and, having looked at this article about a similar problem , I came up with the following temporary workaround that I entered in the router code:
# Keep trying until there are two or more blocks in DOM blocksLoaded = -> console.log "blocks number ...: ", $('.block').size() if $('.block').size() > 1 console.log "Yay!...we have ", $('.block').size() startTraversingBlocks() else Ember.run.next(this, ()-> blocksLoaded() ) blocksLoaded()
It is output:
blockies number ...: 0 blockies number ...: 1 blockies number ...: 1 blockies number ...: 1 ... blockies number ...: 1 blockies number ...: 10 Yay!...we have 10
As Luke noted, the problem is that my subviews are handled by several RunLoops. when I update the browser, I get a different number of blockies number ...: 1 output each time, somewhere between 4 and 10 times during my tests.
In my opinion, this is not a very good solution, but it seems to work for my use. I feel that there is a need for another hook that allows you to access the DOM when it is guaranteed that all elements from the views are accessible through the jQuery selector, but maybe I am missing something.