3

I am writing an Ember.js application, and I would like to run the following Javascript code on a div with a class of navbar right after the view renders:

$('.navbar').affix({offset: -1000});

I am not aware of any way to do this easily in Ember, as the standard JQuery $(document).ready() doesn't work with Ember apps. There has to be some easy way of doing this, but all of the other answers to this question seem to be like complex workarounds and are based on outdated versions of Ember.js.

Template in Question:

<script type="text/x-handlebars">
  <div class="navbar" data-spy="affix">
    <div class="navbar-inner">
      <div class="container">
        <a class="btn btn-navbar">
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </a>
      </div>
    </div>
  </div>

  {{outlet}}
</script>
Community
  • 1
  • 1
Brad Ross
  • 451
  • 8
  • 26

2 Answers2

5

I assume that you mean the template of your ApplicationView when you speak of the 'index template':

App.ApplicationView = Ember.View.extend({
    didInsertElement : function(){
        var that = this;
        Ember.run.next(function(){
            that.$('.navbar').affix({offset: -1000});
        });
    }
});

What are the ingredients of this solution?

  1. The didInsertElement of a View is the right place to put jQuery and jQuery plugin initialization logic.
  2. The didInsertElement hook is called when the DOM-Element of your view has been inserted, but the inner elements have not yet been inserted. Therefore i wrapped your logic in a call to Ember.run.next(). This call makes sure that the logic is run after your view has rendered completely, because it is run at the end of the Ember Run Loop, which is responsible for synchronizing necessary changes to the DOM.

Update: Even better solution proposed by Thomas to decrease delay between rendering and the JS Logic being run:

App.ApplicationView = Ember.View.extend({
    didInsertElement : function(){
        var that = this;
        Ember.run.schedule('afterRender',function(){
            that.$('.navbar').affix({offset: -1000});
        });
    }
});

Problem with the first solution according to OP:

is there any way to decrease the lag between when the element renders and when the JQuery is run, or is that simply not possible? It looks funny when elements change live after the template renders.

According to Thomas:

I've used both next and schedule to run jquery in my own app and the delay Brad mentions is significantly less to non existent with schedule

Further Reading for the ones interested in the great concept of the Ember Run Loop: http://alexmatchneer.com/blog/2013/01/12/everything-you-never-wanted-to-know-about-the-ember-run-loop/

mavilein
  • 11,648
  • 4
  • 43
  • 48
  • is there any way to decrease the lag between when the element renders and when the JQuery is run, or is that simply not possible? It looks funny when elements change live after the template renders. – Brad Ross Apr 03 '13 at 17:33
  • Hmm, that would mean to run it right after the view has been rendered. But the RunLoop does render all the stuff from its 'render pipeline' and the part from next is run. Long story short, i don't know a way to do so, but i guess it **could** be possible if one digs deeper into the runloop. – mavilein Apr 03 '13 at 17:40
  • But i guess this sort of problems are normally solved by showing a loading screen or something like that until the UI has been fully loaded. – mavilein Apr 03 '13 at 17:40
  • 1
    You could try `Ember.run.schedule('afterRender',function(){})` – Thomas Apr 03 '13 at 17:54
  • But this will likely happen, after all the Views in render queue have been rendered, right? – mavilein Apr 03 '13 at 17:56
  • 1
    I've used both `next` and `schedule` to run jquery in my own app and the delay Brad mentions is significantly less to non existent with `schedule` – Thomas Apr 03 '13 at 18:01
  • Will update my post accordingly, since this a very valueable information. – mavilein Apr 03 '13 at 18:10
0

My assumptions:

  • That code is the only thing in your HTML page (along with your JavaScript includes).
  • This is the only code in your JavaScript file:

    App = Ember.Application.create();

If those two assumptions are correct then you're not working with the index template, you're actually working with the application template. Every view has an init function. The simplest thing for you to do is override that function like so:

App.ApplicationView = Ember.View.extend({
    init: function(){
        this._super();
        alert('do something here');
    }
});

Here's a Fiddle for you to review.

commadelimited
  • 5,656
  • 6
  • 41
  • 77
  • This will not work, since the init function is called, when the Ember object is created. This is long before it is rendered to the DOM. And the fiddle is not even rendering, because you did not call this._super in the init method and you are missing important initialization stuff this way. – mavilein Apr 03 '13 at 17:28
  • It might not be the best option but the fiddle is indeed rendering. I wouldn't have posted it if it wasn't. I've noticed in the past month or so that Chrome refuses to "run" Fiddles containing references to Github hosted code. This is because Github returns JS files as text/plain. Anyway, I had to use Firefox to write the Fiddle and it does indeed execute. – commadelimited Apr 03 '13 at 17:32
  • I am not seeing the navbar div in Chrome and Firefox in the result View. I could fix by adding the aforementioned call to super. – mavilein Apr 03 '13 at 17:36
  • I see what you meant. Agreed. I've updated the fiddle and my answer. – commadelimited Apr 03 '13 at 19:08