Rendering a partial multiple times in Ember - ember.js

I'm trying to render a view in a template multiple times with different data each time, specifically like this:
<script type="text/x-handlebars" data-template-name="foobar">
{{render "_people" peopleArray}}
<!-- ... -->
{{render "_people" anotherPeopleArray}}
</script>
<script type="text/x-handlebars" data-template-name="_people">
{{#each person in controller}}
{{person.name}}
{{/each}}
</script>
I get the JS error: "assertion failed: This view is already rendered".
If I change the {{render "_people"}} to {{partial "people"}} then it will render multiple times, but I do not know how to pass different data into there.
To clarify, I to be able to create a partial/view, which I can pass data to, and can call multiple times. I'm sure this is simple and I'm just missing something.
Any help would be appreciated. Thank you.

You can use {{render}} only once. If you need to do it multiple times, use {{control}} instead, such as:
{{control "people" peopleArray}}
This will create a people template with PeopleView and PeopleController with it's content set to peopleArray

Related

ember.js render partial on any template

I need to pick up an ember.js project to finish a few things. Unfortunately I don't have time to learn the whole framework and features but I was wondering how I create a partial.
I read the documentation below but don't really understand what goes where. I presume the second block of text goes in the .hbs template but where do I put the top block so that the second block can be rendered on any .hbs template?
<script type="text/x-handlebars" data-template-name='_author'>
Written by {{author.firstName}} {{author.lastName}}
</script>
<script type="text/x-handlebars" data-template-name='post'>
<h1>{{title}}</h1>
<div>{{body}}</div>
{{partial "author"}}
</script>
I worked it out, I didn't add the partial to:
EmberHandlebarsLoader.loadTemplates([])

EmberJS: How to position the application template

I'm running into a problem. I need App.rootElement = "body", because views and components will be used all over the place. I do not want to put the entire page in a handlebars template, due to SEO and other concerns. The main application template will exist somewhere in the center of the page:
<h1>Header markup</h1>
<script type="text/x-handlebars">
<h2>Application Template</h2>
{{outlet}}
</script>
<h1>Footer markup</g1>
However, when the page is rendered, this template is appended to the end of the body, instead of staying where the template script has been placed. (see jsbin exmaple) Is there any way to tell Ember to render the application template where it is in the markup?
One hacky solution I've found is to manually move the containing element in didInsertElement, but that seems a little heavy handed and not something that would be considered a best practice. (see jsbin example)
The script tag doesn't have anything to do with placement, if you want it to live somewhere create a div tag, id it, and change the rootElement to that div tag.
<script type="text/x-handlebars">
<h2>Application Template</h2>
{{outlet}}
</script>
...
<h1>Header markup</h1>
<div id='foo'></div>
<h1>Footer markup</h1>
App.rootElement = '#foo';

Nested routes and outlets within outlets

This could be a rather trivial question, but as an Ember beginner, I am struggling with it a bit.
The site I'm building has a parent navigation, but some of the templates inside its outlet may have their own navigation and their own outlet (at least that's what I'm intending). In other words, I might have something like (skipping a lot of HTML):
<script type="text/x-handlebars" data-template-name="application">
<nav>
<ul class="nav">
<li>{{#link-to 'index'}}Home{{/link-to}}</li>
<li>{{#link-to 'testing'}}Testing{{/link-to}}</li>
</ul>
</nav>
<div>{{outlet}}{/div>
</script>
<script type="text/x-handlebars" data-template-name="testing">
<ul class="nav">
<li>{{#link-to 'testing.encoding'}}Encoding{{/link-to}}</li>
<li>{{#link-to 'testing.user'}}User Admin{{/link-to}}</li>
</ul>
<div>{{outlet}}{/div>
</script>
<script type="text/x-handlebars" data-template-name="testing/encoding">
<h3>Encoding</h3>
</script>
<script type="text/x-handlebars" data-template-name="testing/user">
<h3>User Admin</h3>
</script>
The application template wires great. Click on "Home", you get the index template; click on "Testing", you get the testing template. Super. What I want is for when they click "Encoding", the route is then /testing/encoding and the testing/encoding template is rendered in the outlet inside of testing. My Router looks like this:
App.Router.map(function() {
this.route('testing', function() {
this.route('encoding');
this.route('users');
});
});
However, the page won't load at all, giving me: Uncaught Error: There is no route named encoding.index. I suspect I named my templates poorly, or configured my Router incorrectly, or perhaps need to name the outlet in the testing template... and I've attacked those potential issues, but have yet to come up with a resolution.
Ideas? This is probably a typical pattern and likely has a clean, easy solution with minimal javascript. I'm just not seeing it, I suppose.
You want your router to look like this.
App.Router.map(function() {
this.resource('testing', function() {
this.route('encoding');
this.route('users');
});
});
You can't nest a route under a route. What you get with the resource is the leaf routes of index, loading and error.
There is not route encoding.index in this router.
The routing guide here is really helpful.
Cheers
For one you didn't name your route correctly (it should be this.route('encoding'); instead of this.route('encode');. But the bigger issue is that Ember doesn't allow for nested routes, only nested resources (and routes nested in resources).
The Ember docs say: "You cannot nest routes, but you can nest resources."

Emberjs conditional output in a template with handlebars

I got following models:
A Community with a name, members and moderators(both are Users). Users, who have an id and a name.
In the CommunityMembers template i want to show all the users, and if that user is a moderator, i want to add some extra saying that he's a moderator
<script type="text/x-handlebars" data-template-name="communityMembers">
//model contains an array of users in a community
{{#each user in model}}
<li>{{user.name}}</li>
{{#each moderator in controllers.community.moderators}}
//here is the problem-->
{{#if moderator.id == user.id}}
<b>this is a moderator</b>
{{/if}}
{{/each}}
{{/each}}
</script>
i know that in handlebars you can't use moderator.id==user.id but it's an easy way to say what i want to do.
i tried to write a handlebars helper but when i checked in the helper what my argument was i got a string saying: "moderator.id" or "user.id" so that didn't work.
i also tried to do it with a method in my community-object:
App.Community = Ember.Object.extend({
isModerator: function(community, user_id){
return community.moderators.indexOf({"id":user_id})!=-1;
}
});
in the template:
{{#if isModerator(controllers.community,user.id)}}
<h>this is a moderator</h>
{{/if}}
but that gave me errors in the template like:
. Compiler said: Error: Parse error on line 12: .../if}}
{{#if isModerator(controll
----------------------^ Expecting 'CLOSE', 'CLOSE_UNESCAPED', 'STRING', 'INTEGER', 'BOOLEAN', 'ID', 'DATA', 'SEP', got 'INVALID'
Is there anyone who knows how to deal with this?
You can't do this in Handlebars (as you said) and you shouldn't try do mimic this behavior with a helper. This limitation is intentionally designed into the templating, because it is considered a bad practice to have too much logic in the template. Instead your goal should be to write your template like this:
<script type="text/x-handlebars" data-template-name="communityMembers">
//model contains an array of users in a community
{{#each user in controller.usersWithModeratorFlag}}
<li>{{user.name}}</li>
{{#if user.isModerator}}
<b>this is a moderator</b>
{{/if}}
{{/each}}
</script>
Now you are probably asking yourself how to implement this attribute. You could try something like this (if you can't embed this attribute into your user objects):
App.CommunityMembersController = Ember.ArrayController.extend({
needs : ["community"],
usersWithModeratorFlag : function(){
var moderators = this.get("controllers.community.moderators");
return this.get("model").map(function(user){
if(moderators.contains(user)){
user.set("isModerator", true);
}
}
}.property("model.#each", "controllers.community.moderators.#each")
});
As you can see, it is quite easy to move this logic out of the template and into the controller, where it belongs.
You can use ember-truth-helpers
{{#if (eq moderator.id user.id)}}
<b>this is a moderator</b>
{{/if}}

Empty vs. not-loaded Ember.js controller

The handlebars each helper is used to iterate over a list of items in a backing ArrayController.
We can use the following construct to do something with a list of items AND show alternative details when that list is empty:
{{#each item in controller}}
<!-- stuff goes here -->
{{else}}
<!-- other stuff goes here -->
{{/each}}
This is great, but what if we want to differentiate between empty and not loaded? I feel like this must be a fairly common use case, but I can't figure out how to approach it - I don't see anything in the guides. Any help?
For this use case, i just wrap the "each" with a "if", that test an additional argument 'loaded' on the model.
{{#if content.loaded}}
{{#each item in controller}}
<!-- stuff goes here -->
{{else}}
<!-- other stuff goes here -->
{{/each}}
{{/if}}
The 'loaded' is toggled to true when the ajax promise (or whatever you do) completes.
Hope it helps!
For me with Ember 1.8.1, Ember Data 1.0.0-beta11 and iterating over hasMany collection on a model loaded wasn't working, as well as isLoaded. The thing that worked was isFulfilled.