KOGI
KOGI

Reputation: 3989

Proper way to observe ChildViews in Ember?

Here's a fiddle for this question (make sure you have your console open):

Basically, I have a handlebars block view as a parent, then loop through an array of items and create child block views for each item. Later, those children are modified in some way, and I want to detect and handle that event.

Summary Code:

{{#view App.outerView}}
    {{#each item in items}}
        <h6>Person:</h6>
        {{#view App.innerView}}
            <dl>
                <dt>First Name</dt><dd>{{item.first}}</dd>
                <dt>Last Name</dt><dd>{{item.last}}</dd>
            </dl>
        {{/view}}
    {{/each}}
{{/view}}

At some later point:

controller.get( 'items' ).pushObject( 'new item' );

In the jsFiddle, I am trying to keep the last child object always highlighted (active = true). When the outerView is initially inserted, we apply the highlight to the last item and it works as expected. Later, when we add a new item, our highlight method fires, but it doesn't see the new item.

2 Questions about this:


Edit: I've adjusted the fiddle a little to better illustrate my needs. Notice the mixed content of the outerView (HTML + innerViews). Basically, my outerView needs to accept ANY content as children, not just an array/collection of views/data.

Edit 2: Further clarification: Manipulating those child views after the fact is a different story and can be accomplished either by working with the contents of childViews (ember views) or using jQuery to manipulate HTML elements that may not be a formal ember view object, or any other means. The real goal here is simply to capture the event when ANY of the outerView content changes (with a current copy of childViews).

Edit 3: Here's an Issue on Github

Upvotes: 1

Views: 1113

Answers (2)

Sebastian Seilund
Sebastian Seilund

Reputation: 454

You initial code looks a little tangled up. Here is a better way to achieve the same result:

http://jsfiddle.net/XaN8T/1/

You should wrap each item in a controller, which is easily done with the {{each}} helper:

<script data-template-name="application" type="text/x-handlebars">
    This is a list:
    <ul>
        {{each controller itemController="item" itemViewClass="App.ItemView"}}
    </ul>
</script>

And create a separate template for your items:

<script data-template-name="item" type="text/x-handlebars">
    <h6>Person:</h6>
    <div {{bindAttr class=":content active"}}>
        <dl>
            <dt>First Name</dt><dd>{{first}}</dd>
            <dt>Last Name</dt><dd>{{last}}</dd>
        </dl>
    </div>
</script>

Then you can take advantage of needs and computed properties in Ember with the active property:

App.ItemController = Ember.ObjectController.extend({
    needs: ['application'], //This way each ItemController can access the ApplicationController's content (i.e. the list of all items)
    active: function() {
        return this.get('controllers.application.lastObject') === this.get('content');
    }.property('controllers.application.lastObject')
});

The App.ItemView then binds its css class to the controller's active property:

App.ItemView = Ember.View.extend({
    tagName: 'li',
    templateName: 'item',
    classNameBindings: ['controller.active']
});

Voila! It works :)

Upvotes: 1

Mudassir Ali
Mudassir Ali

Reputation: 8041

A better way of doing what you have accomplished in the fiddle is by the use of Ember.CollectionView

and then adding a computed property which determines whether the child is the last one or not on the itemViewClass for the active class

active: function () {
  return this.get("elementId") === this.get("parentView.childViews.lastObject.elementId");
}.property('parentView.childViews.length'),

Here is a Working Fiddle

Note Whenever there is a need to deal with collection of views either use CollectionView or Ember.ContainerView(This gives much more control over the child views manipulation) as per your requirements, but for most of the cases Ember.CollectionView would suffice

Upvotes: 0

Related Questions