Reputation: 3989
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:
Main Question: Why doesn't the last item (Matt) get highlighted? In other words, why, after a new item has been added, does childViews.length
still report the old value? Is there a better way to be doing this?
Secondary Question: Why does the observer on childViews
fire twice when we add the new item? (3 times total: 1 on initial insert, 2 from addition of 1 new child item)
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
Reputation: 454
You initial code looks a little tangled up. Here is a better way to achieve the same result:
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
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