Ember components with itemController - ember.js

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.

Related

How to link to each nested routes in ember?

I've got an ember app where my routing looks like this:
App.Router.map(function() {
this.resource('works', {path: '/works'}, function(){
this.route('work', {path:':work_id'})
});
});
I want to be able to link each of my work in my index template, for my naviguation. I tried this but it didn't work:
<script type="text/x-handlebars" data-template-name='application'>
<header id="header">
<nav id="main-nav" role="navigation">
<div class="item-container">
{{#link-to 'works' tagName='div' classNames="navItem work" }}
<p>Works</p>
{{/link-to}}
<ul>
{{#link-to 'work' works work}}{{work.title}}{{/link-to}}
</ul>
</div>
</nav>
</header>
<main id="main">
{{outlet}}
</main>
</script>
If you need more info just let me know.
Your index route definitions should look like this (assuming you are using Ember Data):
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('work');
}
});
Then in your index template go through {{each}} of the App.Work records and link to the works.work route:
<ul>
{{#each}}
<li>{{#link-to 'works.work' this}}{{title}}{{/link-to}}</li>
{{/each}}
</ul>
When linking to a route, you do resource.route
{{#link-to 'works.work' work}}{{work.title}}{{/link-to}}
In this case I'm assuming work is a property that is a model, if not, you'll need to show your router.

ember.js: conditional positioning of nested resource outlet

starting from the bloggr-client of the ember guide i would like to include the outlet of 'post' inside the 'posts' each loop, resulting in some kind of accordeon-like behaviour.
e.g., the router looks like this:
App.Router.map(function() {
this.resource('posts', function() {
this.resource('post', { path: ':post_id' });
});
});
the template (cleared of html tags for legibility) should do something like:
<script type="text/x-handlebars" id="posts">
{{#each model}}
{{#link-to 'post' this}}{{title}}{{/link-to}}
{{#if :post_id given and corresponding with this post}} <-- that's pseudo code ;-)
{{outlet}}
{{/if}}
{{/each}}
</script>
any idea how to accomplish this properly?
UPDATE 1:
to clarify, output on url '/posts' should be:
post 1 title
post 2 title
post 3 title
on url '/posts/1':
post 1 title
complete post 1
post 2 title
post 3 title
on url '/posts/2':
post 1 title
post 2 title
complete post 2
post 3 title
Basically, you want to be able to displays all of the posts on the posts route as an accordion? You're going about it wrong because you can only have one {{outlet}} per template. What you want to do is build a component, which would look something like this:
<script type="text/x-handlebars id="components/post-card">
{{#link-to 'post' post tagName="h2"}}{{post.title}}{{/link-to}}
<p>{{post.snippet}}</p>
</script>
And call it in the posts route like this:
<script type="text/x-handlebars" id="posts">
<ul>
{{#each}} -> if you're iterating through the model, no need to specify
<li>
<h3 {{action makeActive}}>{{title}}</h3>
{{#if active}}
{{post-card post=this}}
{{/if}}
</li>
{{/each}}
</ul>
</script>
You would add active as a property in your model, and makeActive would be an action in your controller that would make active true for the given model, and make the other ones false. This would model an accordion, though your route would not update.

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 });