Using `afterRender` hook with nested views

对着背影说爱祢 提交于 2019-12-20 03:22:09

问题


Here is the jsFiddle if you like to see code first

What I want to do is simple: execute some (actually a lot) of view layer Javascript code from jQuery and jsPlumb libraries after ALL the views in the current route have finished rendering. After getting some help from this solution and finding that ember-latest has just been updated to include afterRender hook, I decided to upgrade. I now have the almighty afterRender hook.

All the views I am inserting into the dom have a block class associated with them. So the way I have been testing to see if all the views are in the dom is using $('.block').size() and seeing if it matches expected number.

However I am facing some issues when I try to use this hook in my application. I first tried to call the afterRender in the router, after the calls to connectOutlets. I always get only 1 block when I print out the number of blocks in dom while there should be 10.

If I put this code in didInsertElement of my local block view:

didInsertElement: () ->
    knob = this.$("##{@get 'knobId'}")
    name = this.$(".name")
    main = this.$(".main") #block html content will be injected into main
    knob.hide()

    Ember.run.scheduleOnce('afterRender', this, ()->
      console.log ">> block count: ", $(".block").size()
    )
    ...

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 only get 1 block in the dom and after that I get all 10. I am not sure why. But the main issue here is that if I put the hook inside didInsertElement, the code gets executed for an arbitrary number of views depending on data (in this example 10). However I only want to run this code once after all views have finished rendering.

Note that the view I am working with has nested data. I tried to reproduce this on jsFiddle, but I failed in the sense that it seems to work all find and dandy on the fiddle. Maybe because my views are big and complex it is causing some synchronization issues? In any event, I think we can use the fiddle as a way to discuss a solution and I can test it locally.

One hack I tried to do to get around the issue is to schedule my code to run after 500ms delay using Ember.run.later. That did solve the issue on my local machine. However, using a timer to do this is very sheep dung and does not work reliably since different browsers or machines may take longer to render the views.

This is a though one to reproduce and I have already spent much time trying to find a solution. I would appreciate any help in reproducing the problem here or finding a solution.

Edit (workaround): Thanks to look for helping me troubleshoot this problem, and by looking at this post about a similar problem, I came up with the following temporary workaround, which I put inside 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()

This outputs:

blockies number ...: 0
blockies number ...: 1
blockies number ...: 1
blockies number ...: 1
...
blockies number ...: 1
blockies number ...: 10
Yay!...we have 10

As Luke pointed out, the problem here is that my nested views are being rendered over several RunLoops. when I refresh the browser I get a different number of blockies number ...: 1 output everytime, anywhere 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 case. I feel that there is a need here for another hook that allows one to access DOM when it is guaranteed all the elements from views are accessible through jQuery selectors, but perhaps I am missing something here.


回答1:


I ran this question past Kris Selden, and he said "afterRender is after the render queue is totally flushed for that run loop; I'm guessing this is over more than one loop"

The most likely possibility is that you have a record array loading. Perhaps it starts with one item and then 9 more load in later. If you rule out data loading, perhaps you have an Ember.run.next or Ember.run.later somewhere in your code?



来源:https://stackoverflow.com/questions/13596733/using-afterrender-hook-with-nested-views

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!