3

I am creating an Ember Component, and I need to bind the scroll event to a div that is dynamically created.

My handlebars code looks like this:

<div class="searchbarContainer" tabindex="0" bubbles=false>
    {{input type="search" name="searchFor" class="searchTextField" placeholder="Search..." value=searchKey}}

    {{#if searchKeyNotNull}}
    <div class="searchResultsContainer box-shadow" {{bind-attr id="searchBarID"}}>
        {{!-- BINDS ID TO searchBarID for searchResultsContainer--}}
            {{#if noResults}} {{!-- Then say : "No Results. " --}}
                      <div class="applicantsName noResults">
                        No Results found.
                      </div>      
            {{else}}
                {{!-- For each Row, include visual elements. --}} 
                {{#each toSearch }}
                    <div> ... </div>
                {{/each}}
            <div class='endOfResults'>
                End of Results
            </div> 
            {{/if}}
    </div>
    {{/if}}
</div>

The logic is set so that according to what is entered in the input part of this, searchKeyNotNull is updated to 'true' or 'false', as is noResults.

As it functions now, the div searchResultsContainer becomes populated with divs that contain the results, and there is a max height. I have also enabled overflow, so the user can scroll through the results.

I need to bind a scroll event to this div so that when it reaches the end, an action is fired.

Now, here's the event code I started with:

        $('#searchBarID').bind('scroll',function(){
        if($(this).scrollTop() + $(this).innerHeight()>=$(this)[0].scrollHeight)
        {
          alert('end reached');
        }
      });

Which works. Unfortunately, when this snippet is run, the div has not been created yet, so this event is never bound. I also tried to use the jQuery.on function, but it seems that scroll doesn't bubble, so

        $('body').on('scroll','#searchBarID', function(){
        if($(this).scrollTop() + $(this).innerHeight()>=$(this)[0].scrollHeight)
        {
          alert('end reached');
        }
      });

does not work.

I am at my wit's end trying to find a workaround for this; I need to either: a) find a way to bind scroll to a dynamically added element, or b) find out when Ember creates elements so I can insert this bind code right then.

A solution that does either of these two would help me greatly!

6
  • Check out the didInsertElement hook: stackoverflow.com/questions/8881040/… Commented Jul 25, 2014 at 18:40
  • What causes the element with the id searchBarID to be added to the DOM? I'm not familiar with Ember, but given a similar problem, I would try to attach a callback to whatever creates searchBarID so that the event binding is sure to follow the creation of the DOM node. Commented Jul 25, 2014 at 18:45
  • I definitely thought of going that route: unfortunately I couldn't find good docs on when and how DOM elements are created. Thanks! Commented Jul 25, 2014 at 20:25
  • Oliver, I did check out the hook and it definitely helps clear up other questions I hadn't asked yet, but for this particular case, the didInsertElement only fires once, and DOM elements are re-created multiple times after it. So unfortunately it doesn't work for this case. Commented Jul 25, 2014 at 20:26
  • @Darshan the didInsertElement hook is definitely what you are looking for. You just need to modify your template a bit. Commented Jul 25, 2014 at 23:54

3 Answers 3

1

I countered this issue a couple of times. I find it easiest hiding the element instead of placing it an a if block.

You could do so by using expanding your bind-attr

<div class="searchResultsContainer box-shadow" {{bind-attr id="searchBarID" class="searchKeyNotNull::hidden"}}>

Where is hidden is a class that will set the display to none. This will cause the element to renderd only once, will allows you to use the didInsertElement hook like so:

onDidInsert: function () {
    var element = $('#searchBarID', this.$());
    element.on('scroll', function () {
        // your impl.
    })
}.on('didInsertElement')

in your component.

The reason I use the bind-attr to hide the element (instead of not rendering it!) is because ember will create a new element every time the searchKeyNotNull changes. The didInsertElement hook will only trigger once however.

Sign up to request clarification or add additional context in comments.

1 Comment

Hm. Definitely a solid suggestion: something seems to be breaking when I have it created first, then tampered with. I'll update this comment as soon as I can confirm it's working.
0

scroll, load, error events not bubble up(see: https://developer.mozilla.org/en-US/docs/Web/Events/scroll), therefore you must update handlers on adding scrollable element.

1 Comment

I am aware that it doesn't bubble; that's why I'm asking if, potentially, there is a way to bind scroll when the element is created and for that I would need to know when and how Ember creates DOM elements. I wonder if someone could provide me with an answer to this.
0

Most of the time there is a way to do something you normally do in jQuery with Ember view hooks, actions, and events.

An alternate approach would be to wrap that DOM node in a {{#view}} and then make use of the didInsertElement hook. For example, let's wrap it in {{#view App.SearchResultsView}}:

<div class="searchbarContainer" tabindex="0" bubbles=false>
    {{input type="search" name="searchFor" class="searchTextField" placeholder="Search..." value=searchKey}}

    {{#if searchKeyNotNull}}
      {{#view App.SearchResultsView}}
        <div class="searchResultsContainer box-shadow">
          <!-- MORE HBS HERE -->
        </div>
      {{/view}}
    {{/if}}
</div>

Now, let's define App.SearchResultsView:

App.SearchResultsView = Ember.View.extend({
  didInsertElement: function () {
    this.$('.searchResultsContainer').on('scroll', function (e) {
      // scrolling...
    });
  }
});

Hope that helps! Let me know if I can further explain anything.

7 Comments

whoa whoa whoa wait: I'm relatively new to Ember but are you telling me that I can actually associate a component with a view? I mean if so - whoa - every time the dom element changes, the view is recreated, and every time the view is recreated, the didInsertElement is fired, and therefore every time I need the scroll bound, it gets bound right up there. I have got to try this route asap. Will update soon after.
@Darshan That is precisely what I am telling you. :D Pretty easy pattern to follow, right? Any time a view is rendered, it receives a unique id. Thus, no worries about the bindings.
ahhh- i've run into an issue. It seems that once it is inside the view, the view can no longer access methods defined in SearchBarComponent. So although the scroll binding works, since I cannot access the logic part of component, I cannot call the function I need. Unless I'm missing something here...
Yeah, you are missing some things. :) You can access the current controller from both the component and view... this.get('controller') and you can use the Ember.Evented as a mixin with that controller to communicate as needed. You can have the view scroll event call a controller method that fires something in the view. emberjs.com/api/classes/Ember.Evented.html Let your controller do the "thinking" and just have your views send it messages. Hope that makes sense.
@Darshan So in your component, you could do something like this.get('controller').on('searchTriggerFired', this.doSomethingThatViewToldMeToDoThroughTheController);
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.