ember.js: conditional positioning of nested resource outlet - ember.js

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.

Related

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.

Ember.js {{linkTo}} to itself does not render

I have a problem with a linkTo that points to itself. The actual problem is in an application that has a sidebar with a set of links. The first time a click the link the model and view is loaded and displayed properly. However on the second click the view and/or model disappear.
I've condensed the problem in this jsfiddle. The index route displays a list of links and when clicking on one of the links it will display the detail. In the detail page I have a link to itself but when I click it the model does not show.
The detail template shows the content fine but note the {{firstName}} or {{lastName}}
<script type="text/x-handlebars" data-template-name="item">
<h2>Item Content:</h2>
{{#linkTo "item" id}}Reload me{{/linkTo}}
{{content}}
<ul>
<li>{{firstName}} {{lastName}}</li>
</ul>
</script>
I think the issue is similar to Ember.js - linkTo error on second call
Updated sample
Here is a new example With linkTo at the top levelwhere the {{linkTo}} item is in the top level application template
<script type="text/x-handlebars" data-template-name="application">
{{#linkTo "test" 0}}Test 0{{/linkTo}}
{{outlet}}
</script>
Following #intuitivepixel solution using an action gives me the same result
<script type="text/x-handlebars" data-template-name="application">
<a href="#" {{action reloadMe "test" 0}}>Test 0</a>
{{outlet}}
</script>
Not everything can be done with the {{linkTo}} helper, IMO for this kind of feature (like a reload) you should go for a {{action}} helper instead.
First define the action that does the routing for you when called:
App.TestRoute = Ember.Route.extend({
events: {
reloadMe: function(route, content) {
this.transitionTo(route, content);
}
},
serialize: function(model) {
return {id: model.id};
}
});
And then in your template:
<a href="#" {{action reloadMe "test" content}}>Reload me</a>
See here how you could implement it: http://jsfiddle.net/FSd6H/4/
Hope it helps.

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

following emberjs guide for #linkTo helper does not display individual post

I have a jsfiddle with a route as shown below, which is exactly thesame as the one in emberjs guides and when I click on the #linkTo helper attached to {{post.title}}, it ought to show me the individual post but it does not and instead the console shows this errors:
Uncaught Error: assertion failed: Cannot call get with 'id' on an undefined object.
Also when I click the posts link on the home page, it displays all the titles but in the console, it also shows this error:
Uncaught Error: Something you did caused a view to re-render after it rendered but before it was inserted into the DOM.
EmBlog.Router.map(function() {
this.resource("posts", function(){
this.route('show', {path: '/:post_id'}) ;
});
});
The template which is thesame as the emberjs guides
<script type="text/x-handlebars" data-template-name="posts/index">
{{#each post in content}}
<p>{{#linkTo 'posts.show' post}} {{post.title}} {{/linkTo}}</p>
{{/each}}
</script>
I looked at this commit that added support for string literals as param for {{linkTo}} and in particular the suggestions below from that commit:
Now, Ember allows you to specify string literals as arguments. {{#linkTo post popular}} would look up the "popular" property on the current context and generate a URL pointing to the model with that ID. While {#linkTo post "popular"}} would treat the string literal "popular" as the model.
It seems like the only issue was you forget to pass the context to the linkTo posts.edit helper in the posts/show template.
<script type="text/x-handlebars" data-template-name="posts/show">
<h1>Post</h1>
<p>Your content here.</p>
<h3> {{title}} </h3>
<h3> {{body}} </h3>
<br/>
<p> {{#linkTo 'posts.index'}} back {{/linkTo}}</p>
<p> {{#linkTo 'posts.edit' content}} Edit the post {{/linkTo}}</p>
</script>
Here is a working fiddle, BTW I've cleaned up a bit, some things seemed useless for me.
http://jsfiddle.net/rxWzu/9/