How do you add dynamic partial by concatenating strings in ember js? - ember.js

We can add partials onto templates in ember js.
{{#each subdetail in leftSubDetails}}
{{partial 'lists/details/link-'+subdetail}}
{{/each}}
Gives following error
Error: Parse error on line 22:
...lists/details/link-'+subdetail}}
-----------------------^
Expecting 'CLOSE', 'CLOSE_UNESCAPED', 'STRING', 'INTEGER', 'BOOLEAN', 'OPEN_SEXPR', 'CLOSE_SEXPR', 'ID', 'DATA', got 'INVALID'

Compute the name of the partial as in the view or controller:
partialName: function() {
return 'lists/details/link/ + this.get('subdetail');
}.property('subdetail')
Then call it like
{{partial partialName}}
Not tested.

Try passing a parameter instead, like this:
{{partial 'lists/details/link' subdetail=blah}}

Related

How to use function with arguments in IF statement in the template

Currently Im trying to use a function that observes a field of a controller/component in ember template (handlebars).
index.hbs
<div>
{{ input value=model.field1 }}
{{if hasFieldError('field1')}}
<span>This field is required</span>
{{/if}}
</div>
<div>
{{ input value=model.field2 }}
{{if hasFieldError('field2')}}
<span>This field is required</span>
{{/if}}
</div>
index.js
hasFieldError: function(val) {
return true if val is found in an array
}.observes('field1', 'field2'),
But this of course returns a build error:
{#if hasFieldError('compa ----------------------^ Expecting
'CLOSE_RAW_BLOCK', 'CLOSE', 'CLOSE_UNESCAPED', 'OPEN_SEXPR',
'CLOSE_SEXPR', 'ID', 'OPEN_BLOCK_PARAMS', 'STRING', 'NUMBER',
'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', 'SEP', got 'INVALID'
Any idea how to achieve this?
You can't call a function from a template, except using an action. You can only reference properties, such as fields and computed properties.
Observers are generally not a good idea
Are you really trying to determine if the value of field1 is in an array? Let's assume the array is found at array1. Then you could write a helper named contains:
{{#if (contains array1 field1)}}
But someone has already written this. Welcome to the wonderful Ember community of addons! See https://github.com/DockYard/ember-composable-helpers#contains
Just replace your observer function with a computed property:
hasFieldError: Ember.computed('field1', 'field2', function(val) {
return true if val is found in an array
}),

Alternative to the 'hash parameters' limitation of query-params in link-to helper in Ember

Is there a way to pass a hash from a model as the query params to the link-to helper?
For example, I have a model:
export default DS.Model.extend({
val1: DS.attr('string'),
val2: DS.attr('string'),
asHash: Ember.computed('val1', 'val2', function() {
'val1': this.get('val1'),
'val2': this.get('val2'),
})
});
And in a component I want to use that value like:
<div>
{{#link-to 'query-page' (query-params model.asHash)}}query{{/link-to}}
</div>
The example above results in an error:
Uncaught Error: Assertion Failed: The query-params helper only accepts hash parameters, e.g. (query-params queryParamPropertyName='foo') as opposed to just (query-params 'foo')
Any ideas on how this can be worked around?
I don't think it is possible, (query-params) needs property name for query value you are passing queryParamPropertyName='foo', instead of passing model as queryparam you can pass it directly link-to {{#link-to 'my-route' model }}query{{/link-to}} but my-route should support it.

ember render w/ ArrayController

I am trying to get a menu working using {{render}} in ember.
Controller:
App.NavigationController = Ember.ArrayController.extend({
itemController: 'navSheet',
init: function() {
this._super();
this.set( 'content', [
{ name: 'Sheet 1', type: 'Sheet', id: 1 },
{ name: 'Sheet 2', type: 'Sheet', id: 2 }
]);
}
});
App.NavSheetController = Ember.ObjectController.extend({
getID: function() {
return 'sheet-' + this.get( 'id' );
}
});
Then I use {{render "navigation"}} to render using the following emblem template:
ul
each controller.content
li data-type=type! id=getID! = name
[UPDATE WITH OUTPUT]
With "controller.content" here, I do loop through the content. With use "each" this isn't available. However, in any case the itemController is not in the context inside the loop. The resulting HTML is:
<li data-type="Sheet" id="">
<script id="metamorph-8-start" type="text/x-placeholder"></script>Sheet 1
<script id="metamorph-8-end" type="text/x-placeholder"></script></li>
<li data-type="Sheet" id="">
<script id="metamorph-9-start" type="text/x-placeholder"></script>Sheet 2
<script id="metamorph-9-end" type="text/x-placeholder"></script></li>
Note in particular that "getID" value doesn't show up. In fact I can't seem to access the itemController at all. What am I doing wrong?
I've never used a emblem, so I don't totally understand the template, but if I understand correctly you're trying to get sheetId into the li and you're using the getID function to do this. I'm going to guess that Emblem will convert it into this template
<li {{bind-attr data-type=type dataid=getID}}>
But it also looks like you're doing getID!=name or something like that. That kind of logic wouldn't work in handlebars.
Now getID won't run because it needs to be a computed property
App.NavSheetController = Ember.ObjectController.extend({
getID: function() {
return 'sheet-' + this.get( 'id' );
}.property('id')
});

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

Escape keyword in the handlebar template

I have a collection of objects (MyApp.instrsController, an ArrayController) and each object has a property called 'action' (a float number), how can I display it using the handlebars template?
Below is the code I've tried:
<ul>
{{#each MyApp.instrsController}}
<li>{{action}}</li>
{{/each}}
</ul>
But since 'action' is a 'keyword', it throws javascript runtime error 'options is undefined'.
You can manually specify that you're looking for a property in the current context by preceding the property name with this.:
{{this.action}}
If you can't change the property name you can use a computed property in your model object, see http://jsfiddle.net/pangratz666/4ZQM8/:
Handlebars:
<script type="text/x-handlebars" >
<ul>
{{#each App.controller}}
<li>{{actionProp}}</li>
{{/each}}
</ul>
</script>
JavaScript:
App.Object = Ember.Object.extend({
actionProp: function() {
return this.get('action');
}.property('action')
});
App.controller = Ember.ArrayController.create({
content: [],
addObj: function(number) {
this.pushObject(App.Object.create({
action: number
}));
}
});
If you don't have a custom model object, you can use a computed property on a CollectionView, see http://jsfiddle.net/pangratz666/r6XAc/:
Handlebars:
<script type="text/x-handlebars" data-template-name="item" >
{{actionProp}}
</script>​
JavaScript:
Ember.CollectionView.create({
contentBinding: 'App.controller',
itemViewClass: Ember.View.extend({
templateName: 'item',
actionProp: function(){
return this.getPath('content.action');
}.property()
})
}).append();
I seriously suggest that you just change the name of the property.
You are spending time working on a problem that is not core to your domain.
YAGNI. KISS.
The biggest lesson to learn in programming is how to get things made and done rather than how to manipulate javascript into not throwing a snit over your use of a reserved keyword. You will be happier later if you don't have a fragile solution that looks tricky to understanc
Since this is a float, can I suggest you use "actionLevel"?
just use model.property,such as:
{{input class="input-xlarge" value=model.content }}