Setting the id on element created by #each helper - ember.js

How can I set the id for elements created by the Ember.js handelbars helper #each?
If you take a look at http://jsfiddle.net/7QL5z/ I tried to define an id on calling the #each helper.
The resulting HTML looks like this:
<div id="ember191" class="ember-view">
<ul>
<li id="ember349" class="ember-view"> Animal - Animals </li>
<li id="ember389" class="ember-view"> Android - Androids </li>
<li id="ember427" class="ember-view"> Human - Humans </li>
</ul>
</div>
Neither the div nor the ul (that would be the most usable way) get the specified id.
Is it possible at all? If not: how can I access these elements via Ember?

I tried setting 'elementId' instead of 'id' on the '#each' but it didn't seem to work either...
Otherwise you can create an animals list view
App.AnimalsListView = Ember.CollectionView.extend({
tagName: 'ul',
contentBinding: 'App.itemsController.content',
classNames: ['animals'],
elementId: 'animalsList',
itemViewClass: Ember.View.extend({
classNames: ['animal'],
templateName: 'ember/templates/animals/animal',
elementId:Ember.computed(function(){
'animal-'+this.getPath('content.name')
})
})
});
In your animal template:
{{ content.get('name') }} - {{ content.get('pluralized') }}
And finally in your main html file
<script type="text/x-handlebars">
{{ view App.AnimalsListView }}
</script>

You can just specify the id on a view using id="whatever", but it won't work on a {{#each}}
http://jsfiddle.net/tomwhatmore/TJvR6/
Might be a slightly more elegant solution but what I've put in that js fiddle seems to work.

Related

Is there itemController for new each syntax in Ember?

The new syntax
{{#each pages as |page| itemController="listedPage"}}
causes parse error. Is item Controller usable with the new "each" syntax?
If yes, how to specify it? If not, what is the preferred way to use computed properties with the items of "each"?
Controllers are being phased out - so I would recommend using components..
Component for Each Item
You could implement a 'listed-page' component which would look something like this:
Your template:
<ul>
{{#each pages as |page|}}
{{listed-page page=page}}
{{/each}}
</ul>
The component:
## components/listed-page.js
export default Ember.Component.extend({
tagName: 'li',
page: null,
pageNumberFancy: function() {
return '^'+this.get('page.number')+'^';
}.property('page.number')
})
## templates/components/listed-page.hbs
Number: {{page.number}} ({{pageNumberFancy}})
The output:
<div class='template-level'>
<ul class='template-level'>
<li class='listed-page-level'>Number: 1 (^1^)</li>
<li class='listed-page-level'>Number: 2 (^2^)</li>
</ul>
</div>
Component for the List
Or a listed-pages component - if 'pages' is an array computed property - or both:
Your template:
{{listed-pages pages=pages}}
The component:
## components/listed-pages.js
export default Ember.Component.extend({
tagName: 'ul',
pages: null,
pagesPlusOne: function() {
var pagesList = this.get('pages');
pagesList.addObject(specialPage);
return pagesList;
}.property('pages.[]')
})
## templates/components/listed-pages.hbs
{{#each pagesPlusOne as |page|}}
{{listed-page page=page}}
{{/each}}
The output:
<div class='template-level'>
<ul class='listed-pages-level'>
<li class='listed-page-level'>Number: 1 (^1^)</li>
<li class='listed-page-level'>Number: 2 (^2^)</li>
<li class='listed-page-level'>Number: +1 (^+1^)</li>
</ul>
</div>

Why didInsertElement hook trigger before elements rendered inside {{#each}} block?

I'm new to Ember, I want to add some query DOM manipulation code to the element in the {{#each}} block. So I google it up and found the solution from this guide:
views/products/index.js
import Spinner from 'appkit/utils/someJqueryCode';
Ember.View.reopen({
didInsertElement : function(){
this._super();
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
},
afterRenderEvent : function(){
// implement this hook in your own subclasses and run your jQuery logic there
}
});
export default Ember.View.extend({
afterRenderEvent: function() {
Spinner();
}
});
templates/products/index.hbs
<div class='panel panel-default products'>
<div class='panel-heading'>
<h2 class='panel-title'>Our Prodcuts</h2>
</div>
<div class='panel-body'>
<ul class='row'>
{{#each}}
<li class='col-md-4'>
<div class='thumbnail'>
<img {{bind-attr src=url alt=alt}} />
</div>
<div class='caption'>
<h3 class='name-me'>{{name}}</h3>
<p>{{description}}</p>
<div class='row no-gutter'>
<div class='col-xs-3'>
<button class='btn btn-primary'>Buy</button>
</div>
</div>
</div>
</li>
{{/each}}
</li>
</div>
</div>
But I seems after the point when afterRenderEvent() is triggered, all the elements in the {{#each}} block hasn't been rendered to the DOM yet, thus, the jQuery code return undefined
What's the right way to do it?
Your view's didInsertElement hook will fire as soon as the application route is rendered, which happens before the index route. You might think that putting it in the index.js file will work, but it's going to just extend the default application view behavior.
You need to create a more focused view that lives within your index.hbs file. One that is only concerned with your spinner jQuery doohickey. That, and an each/else conditional could work nicely here. For example:
{{#each}}
{{#view App.SpinnerDoohickeyView}}
<li class='col-md-4'>
<div class='thumbnail'>
<img {{bind-attr src=url alt=alt}} />
</div>
<div class='caption'>
<h3 class='name-me'>{{name}}</h3>
<p>{{description}}</p>
<div class='row no-gutter'>
<div class='col-xs-3'>
<button class='btn btn-primary'>Buy</button>
</div>
</div>
</div>
</li>
{{/view}}
{{else}}
<li>Empty collection!</li>
{{/each}}
Notice that I've wrapped each list item in its own view. You could wrap the whole ul if you wanted... this is just an example. The idea is that you are only creating views when you have a model.
And now you can define the view, and simply use the didInsertElement hook to work with jQuery:
App.SpinnerDoohickeyView = Ember.View.extend({
didInsertElement: function () {
this.$('li').css('background', 'blue');
}
});
If you have a model to render, jQuery should be able to safely access it this way. Good luck!
Here's some further reading and some code from the Ember folks that looks like what I've shown you here: http://emberjs.com/guides/views/handling-events/

Ember view with dynamic class names

Considering the following:
Parent template:
{{view App.SomeView id="42" panelClass="default"}}
View template:
<div class="col-md-3 col-sm-6">
<div class="panel panel-{{panelClass}}">
<div class="panel-heading">
<h3 class="panel-title">
{{name}}
</h3>
</div>
<div class="panel-body">
{{description}}
</div>
</div>
</div>
View JS:
App.SomeView = Ember.View.extend({
templateName: 'views/some-view'
});
How can I achieve output HTML where the panel class gets set properly? At the moment it doesn't work because it wants to bind, so it inserts the ember metamorph script tags, instead of just plain text for the panel class.
Also, the template is wrapped in an extra div. How would I modify it so that the ember-view wrapping div is actually the first div in the template (the one with col-md-3 col-sm-6)?
The bind-attr helper exists for that reason. Here's the guide entry.
<div {{bind-attr class=":panel panelClass"}}></div>
Also, not sure if you can use a prefix on panelClass in the template. If might be easier just to use a computed property to add the panel- beforehand.
I'm sorry, I didn't see your second question about the extra div. The guide explains here how to extend the element.
App.SomeView = Ember.View.extend({
classNames: ['col-md-3', 'col-sm-6']
});

Create a <li class="active"> around an active {{linkTo}}

What is the cleanest way to get <li class="active"> for the active page in the following Ember app?
index.html
<script type="text/x-handlebars">
<ul class="nav">
<li>{{#linkTo 'ping'}}Ping{{/linkTo}}</li>
<li>{{#linkTo 'pong'}}Pong{{/linkTo}}</li>
</ul>
</script>
app.js
App = Ember.Application.create()
App.Router.map(function() {
this.route("ping", { path: "/ping" });
this.route("pong", { path: "/pong" });
});
This workaround did it for me:
{{#linkTo 'menus.search' tagName='li' href=false}}
{{#linkTo 'menus.search'}}
<i class="icon-search"></i>
{{/linkTo}}
{{/linkTo}}
It creates an li element containing an anchor element. And both will be updated with the "active" class when the route is active.
replace in your template the li tags like so:
index.html
<script type="text/x-handlebars">
<ul class="nav">
{{#linkTo 'ping' tagName="li"}}Ping{{/linkTo}}
{{#linkTo 'pong' tagName="li"}}Pong{{/linkTo}}
</ul>
</script>
the {{linkTo}} with the tagName specified will apply a css class name of 'active' automatically when the application's current route matches the supplied route name.
example, when your app url is at /#/ping the resulting markup would be something like:
...
<li class="active">Ping</li>
...
Or you create a custom view
App.ItemView = Ember.View.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews.firstObject.active');
}.property()
});
and then use it like so
<script type="text/x-handlebars">
<ul class="nav">
{{#view App.ItemView}}
{{#linkTo 'ping'}}Ping{{/linkTo}}
{{/view}}
{{#view App.ItemView}}
{{#linkTo 'pong'}}Pong{{/linkTo}}
{{/view}}
</ul>
</script>
some css to see it actually working
li a {
color: #000;
}
li a.active {
color: #f00;
}
hope it helps
Just nest the {{link-to}} with a tagName on the outer one. This will set an active class on both the outer <li> and the inner <a> tag.
{{#link-to "ping" tagName="li"}}
{{#link-to "ping"}}ping{{/link-to}}
{{/link-to}}
{{#link-to "pong" tagName="li"}}
{{#link-to "pong"}}pong{{/link-to}}
{{/link-to}}

Ember.js binding array element to view

I have the following code in a ember.js template. userController is an ArrayController, with multipe "users" within.
{{#each CollaborativeEditor.userController}}
{{#view CollaborativeEditor.OnlineUserView userBinding="this"}}
<div class="avatar">
<div class="avatar_name">{{name}}</div>
<div class="avatar_status">{{status}}</div>
</div>
<div id="dropdown-1">
<ul>
<li><a href="#" {{action startChat target="onlineUser"}}>Talk to </a></li>
</ul>
</div>
{{/view}}
{{/each}}
This is the code of the respective view:
CollaborativeEditor.OnlineUserView = Ember.View.extend({
tagName: 'li',
startChat : function() {
console.log(this.get('user'));
}
});
Although, the name and status is set correctly for each user, the startChat action attached to the link always prints the first user of the array to the console.
What is wrong with the binding ?
Thanks a lot for your request, to put it in a jsfiddle !
While I was trying to reproduce the error there, I realized the problem, and it has nothing to do with ember.
The div with id="dropdown-1" was called from another link and it was always the same id, therefore always the same action with its user-binding.
Now I've bound the Id to the user-object and it works perfectly.