Use {{yield}} inside {{each}} in Ember.Component - ember.js

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>

Related

How to sort the model array before supply to template

Here is my model, how can i sort it before I send to template?
route.js
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return {
"fruits":[
{fruit:"mango",color:"yello"},
{fruit:"banana",color:"muddy yellow"},
{fruit:"apple",color:"red"}],
"prices":[9, 10,5 ]
}
}
});
Twiddle Demo
Any one please update my twiddle? looking for ember approch.
here is my hbs:
<h1>Fruit Prices Sorted here</h1>
<ul>
{{#each sortedSongs as |price|}}
<li> {{price.fruit}}</li>
{{/each}}
</ul>
<h1>Fruit Prices Sorted here</h1>
<ul>
{{#each sortedPrice as |price|}}
<li> {{price.price}}</li>
{{/each}}
</ul>
<h1>Fruit Details Sorted by Alphabetically </h1>
<ul>
{{#each model.fruits as |fruitObject|}}
<li> {{fruitObject.fruit}}</li>
{{/each}}
</ul>
<br>
<h1>Fruit Color Sorted by Alphabetically </h1>
<ul>
{{#each model.fruits as |fruitObject|}}
<li> {{fruitObject.color}}</li>
{{/each}}
</ul>
Take a look into Ember.computed.sort or
In your twiddle you should create a computed property in the controller, which would sort model.fruits and return the result, e.g.:
nameSort: ['name'],
sortedByName: Ember.computed.sort('model.fruits', 'nameSort')
The second argument has to be a name of a property which holds an array of properties you want to sort by. Or you can provide a function if you need some custom sorting logic.
I've edited your twiddle. Also I think the price attribute was supposed to go inside each fruit object, not in a separate list, so I moved it.
https://ember-twiddle.com/78e0b3e9ff873a8909221efc87e3ef43
Next time do some research though, Ember.computed.sort documentation isn't hard to find.

Binding class from an array in 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();
});

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

How to only add to controller if commit is successful? Ember.js

In the controller
this.get('store').createRecord(Emb.Painting, {name: n});
this.get('store').commit();
In the template
<ul>
{{#each controller}}
<li>
{{name}}
</li>
{{/each}}
Although you can place a
didCreate: function() {
}
in your model. I could not find any direct callback. I decided to add validation at my view layer.

How to render hasMany associations each with their own controller

So my models are set up like this :
App.Post = DS.Model.extend
comments: DS.hasMany 'App.Comment'
App.Comment = DS.Model.extend
post: DS.belongsTo 'App.Post'
I'm trying to create a view that has all posts and all comments display, but I need to decorate the comment objects.
This is what I'd like to do but, to no avail :
<ul>
{{#each post in controller}}
<li>{{post.title}}</li>
<ol>
{{#each comment in post.comments itemController="comment"}}
<li>{{comment.body}}</li>
{{/each}}
</ol>
{{/each}}
</ul>
Properties defined in a App.CommentController are simply not found by the template.
I suspect that Ember.OrderedSet does not implement the itemController param - is there a workaround for this?
You need to use the new expiremental control tag. This will load the view and controller for the specified type:
<ul>
{{#each post in controller}}
<li>{{post.title}}</li>
<ol>
{{#each comment in post.comments}}
{{ control "comment" comment }}
{{/each}}
</ol>
{{/each}}
</ul>
You will need to enable this expiremental feature first. Put this before ember is loaded:
<script type='application/javascript'>
ENV = {
EXPERIMENTAL_CONTROL_HELPER: true
};
</script>
Also, you will need to specify that the controller for comments should not be a singleton, otherwise there will only be one controller instantiated for all comment views:
// this is needed to use control handlebars template properly per
// https://github.com/emberjs/ember.js/issues/1990
App.register('controller:comment', App.CommentController, {singleton: false });