Binding class from an array in Ember.js - ember.js

I'm starting with Ember.js and I'm feeling a bit lost.
I have an object like this:
App.BRANDS = [
{
brand:'Audi'
},{
brand:'BMW'
},{
brand:'Skoda'
}];
So what I'm trying is to display all the elements in that object with the {{#each}} component, show the text inside and bind the same text as a className. So I code this in the route:
App.InsuranceAutoSelectBrandRoute = App.Route.extend({
model: function(){
return App.BRANDS;
}
});
And this in the template:
<article>
{{#each brand in model tagName='ul'}}
<li class='item-space'>
<span {{bind-attr class=':brand-auto classNameAuto'}}></span>
{{brand.brand}}
</li>
{{/each}}
</article>
Then the issue is that the name of each brand, before I bind it as a class attr I have to lowercase it...
App.InsuranceAutoSelectBrandController = Ember.Controller.extend({
classNameAuto: function() {
App.BRANDS.forEach(function(item, index){
return item.brand.toLowerCase();
});
}.property()
});
If in the same point where I'm returning the value I make a log, it works, but the class attr is not showing.
What I'd like:
<article>
<li class='item-space'>
<span class='brand-auto audi'}}></span>
Audi
</li>
<li class='item-space'>
<span class='brand-auto bmw'}}></span>
BMW
</li>
<li class='item-space'>
<span class='brand-auto skoda'}}></span>
Skoda
</li>
</article>
Forgive me for my English level and thank you

First, as #Kitler said, bind-attr is deprecated. If you are using 1.13.x or higher, you may just insert class as any other variable, {{brand.brand}}.
Second, your code is incorrect and will not work. The easiest way to do what you want is via helper. You need to create a helper, lower-case and use it in this way:
<article>
<ul>
{{#each model as |brand|}}
<li class='item-space'>
<span class='brand-auto {{lower-case brand.brand}}'>{{brand.brand}}</span>
</li>
{{/each}}
</ul>
</article>
How to create a helper you may learn from documentation (http://guides.emberjs.com/v1.13.0/templates/writing-helpers/). If you use ember-cli, following code should work:
//app/helpers/lower-case.js
import Ember from 'ember';
export default Ember.Handlebars.makeBoundHelper(function (str) {
return str.toLowerCase();
});

Related

How do I pass a value from one .hbs file to another

I am trying to pass the index value from posts.hbs to the delete-post.hbs and then from delete-post.hbs to my delete-post.js function.
posts.hbs
<br>
<h2> Blog Posts </h2>
<ul>
{{#each model as |onePost index|}}
<li id = {{index}}>{{onePost.title}} {{delete-post}}
</li><br>
{{/each}}
</ul>
{{add-new-post}}
delete-post.hbs
<a class="ui red button" {{action 'deletePost' parentNode.id `}}>Delete</a>`
delete-post.js
import Component from '#ember/component';
export default Component.extend
({
actions:
{
deletePost(input)
{
alert(input);
}
}
});
You can pass the parameter by = operator like {{component-name componentProperty=outerProperty. In your case:
{{#each model as |onePost index|}}
<li id = {{index}}>{{onePost.title}} {{delete-post parentNodeId=index}}
</li><br>
{{/each}}
Also, you should change parentNode.id to parentNodeId in your delete-post.hbs

How to bind a dynamic class to an element in Handlebars and Ember?

So I'm currently building a chat application and I need to bind a dynamic class to messages to define whether it is the senders message or the recipient. However, I'm not sure how to bind this class to an element with my current code:
Messages template
<ul class="content conversation" id="conversation">
{{#each message in messages}}
<li>
{{messageHighlight currentUser.id message.sender_id}}
<div class="avatar">
<img {{bind-attr src="message.thumb"}} />
</div>
<div class="chat-message">
<p>{{message.text}}</p>
<time>
{{message.sender.username}} • {{message.created_at}}
</time>
</div>
</li>
{{/each}}
</ul>
Highlight helper
Ember.Handlebars.helper('messageHighlight', function(currentUserId, senderId)
{
return (senderId == currentUserId) ? 'self' : 'other'
});
I tried to define my class like the following, but Handlebars threw an error:
<li class="{{messageHighlight currentUser.id message.sender_id}}">
So my question is, how do I bind a dynamic class to an attribute while passing in parameters?
Thanks in advance.
You need to return an escaped string from the helper.
Ember.Handlebars.helper('messageHighlight', function(currentUserId, senderId) {
var klass = (senderId == currentUserId) ? 'self' : 'other';
return new Ember.Handlebars.SafeString(klass);
});
In the template
<li class="{{messageHighlight currentUser.id message.sender_id}}" >The message</li>
Here is a working jsbin
FYI you need to be using Ember 1.11.0 or great to use bound attributes with out using {{bind-attr}}
Using Ember 1.10 or below you could use a component for the contents of the li like this.
<ul class="content conversation" id="conversation">
{{#each message in messages}}
{{chat-message message=message currentUser=currentUser}}
{{/each}}
</ul>
chat-message.js component:
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: [messageHighlight],
messageHighlight(){
return (message.sender.id == currentUser.Id) ? 'self' : 'other'
}
});
chat-message.hbs template:
<div class="avatar">
<img {{bind-attr src="message.thumb"}} />
</div>
<div class="chat-message">
<p>{{message.text}}</p>
<time>
{{message.sender.username}} • {{message.created_at}}
</time>
</div>

Ember components with itemController

I have 1 timeline on route posts with 2 types of post: from facebook and from twitter, both with same controller: post with 2 methods: isFacebook and isTwitter.
There's a component for each type of post and an 'post-index' with use this 2 methods to redener the correct component.
Route post with default ArrayController:
<ul class="timeline">
{{#each post in model itemController="posts.post"}}
<li> {{post-index post=post}} </li>
{{/each}}
</ul>
post-index
{{#if post.isFacebook}}
{{post-facebook post=post}}
{{/if}}
{{#if post.isTwitter}}
{{post-twitter post=post}}
{{/if}}
{{yield}}
The problem is when I try render a single post in route post.post with post-index template
route:
export default Ember.Route.extend({
setupController(controller, model) {
this._super(controller, model);
controller.set('model', model);
}
});
template:
<ul class="timeline">
{{#each post in model}}
<li> {{post-index post=post}} </li>
{{/each}}
</ul>
My route posts only works because it have itemController="posts.post"
but post.post don't. What I supposed to do ?
If I understand your question correctly, you are wondering how to pass the post itself to the post to the post property of post-index.
If so, you can modify your code as follows:
<ul class="timeline">
{{#each post in model}}
<li> {{post-index post=this}} </li>
{{/each}}
</ul>
The only change is to change post to this. Since you are in an each block this will pass the item for the current iteration.

Use {{yield}} inside {{each}} in Ember.Component

Is there a way to use {{yield}} inside a {{each}} helper in a Ember.Component?
So I can provide a repating structure with a component, and maybe a add/remove line functionality, but I can provive handlebars markup from outside and use the {{yield}} helper to access that.
Thats what I wanna do:
items:
[{
name: 'stack',
value: 5
}, {
name: 'overflow',
value: 8
}]
template:
{{#show-list value=items}}
{{name}} - {{value}}
{{/show-list}}
component:
<ul>
{{#each value}}
<li>{{yield}}</li>
{{/each}}
</ul>
And thats what I expect back:
<ul>
<li>stack - 5</li>
<li>overflow - 8</li>
</ul>
This is now possible in Ember using public APIs.
See this twitter post from Yehuda:
https://twitter.com/wycats/status/536723973745410048
And this demo:
http://jsbin.com/yawasisofu/4/edit?html,js,output
In your case, the component should look like:
<ul>
{{#each value as listItem}}
<li>{{yield listItem}}</li>
{{/each}}
</ul>

How to get current model for a route from a controller or a view?

I want to implement item-list/item-detail pattern in Ember, but the nuance is that the detail view must appear next to the selected item. E.g:
<ul>
<li><div>Post 1<div></li>
<li><div>Post 2<div></li>
<li><div>Post 3<div></li>
<li>
<div>Post 4<div>
<div>
<ul>
<li>Comment 1</li>
<li>Comment 2</li>
<li>Comment 3</li>
</ul>
</div>
</li>
<li><div>Post 5<div></li>
</ul>
The Handlebars template I tried is:
<script type='text/x-handlebars' data-template-name='posts'>
<ul>
{{#each model}}
{{#linkTo 'post' this}}
<div>{{title}}</div>
{{/linkTo}}
{{#if isSelected}} <!-- How to implement isSelected ? -->
<div>{{!-- render selected post's comments --}}</div>
{{/if}}
{{/each}}
</ul>
</script>
I tried this in controller:
App.PostController = Em.ObjectController.extend({
isSelected: function() {
return this.get('content.id') === /* what to put here? */;
}
});
What I'm stuck with is how to implement isSelected in 'Ember'-way? Am I going in right direction?
You are on the right track. The trick is to use a different controller to wrap products in the item-list vs. the item-detail. So start by specifying that the handlebars {{each}} helper should wrap each entry in a ListedProductController
{{#each model itemController="listedProduct"}}
Now define ListedProductController, adding the isSelected function you'd been writing. Now it can reference the singleton ProductController via the needs array, comparing the product that was set by the router to the listed product.
App.ProductController = Ember.ObjectController.extend({});
App.ListedProductController = Ember.ObjectController.extend({
needs: ['product'],
isSelected: function() {
return this.get('content.id') === this.get('controllers.product.id');
}.property('content.id', 'controllers.product.id')
});
I've posted a working example here: http://jsbin.com/odobat/2/edit