EmberJS - How to call view method from inside a nested {{#each}} - ember.js

I need to trigger the {{showAlias}} view method from within the {{#each content.activitytypes}} I have tried {{_parentView.showAlias}}. I believe I need to call it on the parent's parent, is that correct and how? I can call the {{showAlias}} outside of the {{#each...
Handlebars Template
<script type="text/x-handlebars">
<h2>Activities</h2>
<ul>
{{#each Km.activityController}}
{{#view Km.ActivityView contentBinding="this"}}
<li>
{{content.id}}:{{content.name}}
<ul>
{{#each content.activitytypes}}
<li>{{showAlias}}
{{name}} {{aliasname}}
</li>
{{/each}}
</ul>
</li>
{{/view}}
{{/each}}
</ul>
</script>
View
Km.ActivityView = Em.View.extend({
showAlias: function () {
var arr = this.getPath('content.activitytypes'),
show = false;
console.log(arr)
arr.forEach(function(item) {
var aliasArr = item.showaliasaccountid;
if (typeof aliasArr !== 'undefined') {
if (jQuery.inArray(2,aliasArr)) {
console.log(aliasArr);
show = true;
}
}
});
}.property('content.#each.activitytype.#each'),
});

{{parentView.showAlias}} will work, but in these situations with nested views and sub eachs I always find the code to be more maintainable with CollectionViews. Otherwise you end up stuffing too much inside of a single template/view.
http://docs.emberjs.com/#doc=Ember.CollectionView&src=false

Related

Multiple ul with single array in ember handlebars template

Lets say I've an array with 6 items and I want print them 3 per list
Ex
//arr = [1,2,3,4,5,6];
//html
<div class="first">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<div class="second">
<ul>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
</div>
How can I accomplish that with ember/handlebars?
Thanks
One option would be to write a computed property on your controller that splits the larger array into an array of arrays.
Then you could iterate through the arrays of the computed property and use a component to display each of the smaller arrays.
I'll leave this as an excercise to you unless you have other questions.
Similar to what #Oren is saying, you would need to decorate your model (the array) inside the controller and then display the decorated model in the handlebars (as there is no way to perform logic inside the handlebars itself).
So, something like:
App.IndexController = Ember.ArrayController.extend({
decoratedModel: function(){
var model = this.get('model');
return [
Ember.Object.create({
className: "first",
arr: model.slice(0, 3)
}),
Ember.Object.create({
className: "second",
arr: model.slice(3)
})
];
}.property('model')
});
Then, you can display that in your template as follows:
<script type="text/x-handlebars" data-template-name="index">
{{#each item in decoratedModel}}
<div {{ bind-attr class=item.className}}>
<ul>
{{#each thing in item.arr }}
<li>{{thing}}</li>
{{/each}}
</ul>
</div>
{{/each}}
</script>
I ended doing this:
in template.hbs
{{#each set in arraySets}}
<div class="col-sm-6">
<ul class="list-unstyled">
{{#each item in set }}
<li>{{item.name}}</li>
{{/each}}
</ul>
</div>
{{/each}}
in the related controller
import Ember from "ember";
import Collection from "../../utils/collection";
export default Ember.ObjectController.extend({
// ....
arraySets: function() {
var infos = this.get('model.infos');
return Collection.create({ content: infos }).divide();
}.property()
});
and who does the hard work is the utils/collection.js
import Ember from "ember";
var Collection = Ember.ArrayProxy.extend({
divide: function (size = 2) {
var array = this.get('content');
var length = array.length;
var limit = Math.ceil(length / size);
var sets = Ember.A([]);
for (var i = 0; i < Math.min(size, length); i++) {
sets.pushObject(array.slice(i * limit, (i + 1) * limit));
}
return Collection.create({content: sets});
}
});
export default Collection;
I hope that this could help someone else!

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 only one template instance in meteor

Hi I have the following template :
<template name="users">
<ul id="item-list">
{{#each trackedUser}}
<li id="{{_id}}">
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
{{/each}}
</ul>
</template>
With this helper :
Template.users.helpers({
trackedUser: function() {
var curs = TrackedUser.find();
users = curs.fetch();
return users;
}
});
The problem is that in this case for each new user, all the users are being reput in the dom.
Is there a better way to write this code so that for each new user only the
<li id="{{_id}}">
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
is redrawn ?
Thank you
Avoid using fetch. When you use fetch() you don't pass along a cursor to handlebars so it can't re-render only the portion which has changed.
Template.users.helpers({
trackedUser: function() {
var curs = TrackedUser.find();
return curs;
}
});
For the case of anywhere else on your template (not in a loop) you could also use Reactivity Isolation which is wrapping your content in {{#isolate}}..{{/isolate} blocks.

Where should I keep selection state for a list?

I'm using ember 1.0.0-pre4.
I want to display a list of Model instances. The user should be able to select as many list entries by clicking a button or checkbox that is rendered within each row.
I managed to display the list. But I don't know how to manage selection state. When I put something like {{view Ember.Checkbox checkedBinding="isSelected"}} into the template then the selection state will be held in my model. But I don't think this is the best place. I think selection state belongs to the controller or view state.
Could you please tell me the best way to store and access (multiple) list selection state?
One way is to just keep a second list in the controller:
App.FooController = Ember.ArrayController.create({
selectedContent: [],
selectToggle: function(event) {
var selectedContent;
selectedContent = this.get(selectedContent);
if (selectedContent.contains(event.context)) {
return this.set('selectedContent', selectedContent.without(event.context));
} else {
return this.get('selectedContent').push(event.context);
}
}
});
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each foo in controller}}
<li {{action selectToggle foo}}>{{foo.name}}</li>
{{/each}}
</ul>
</script>
That just maintains a separate list in the controller and pushes/removes based on whether or not the item was selected.
You could also use an Ember.ObjectProxy to augment the values of the foo object with an "isSelected" property.
App.FooController = Ember.ArrayController.create({
selectedContent: #get('content').map(function(item){
Ember.ObjectProxy.create({
content: item
isSelected: false
});
});
});
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each foo in controller.selectedContent}}
<li {{bindAttr class= "foo.isSelected"}}{{action selectToggle foo}}>{{foo.name}}</li>
{{/each}}
</ul>
</script>
Then in your selectToggle method you just set foo's isSelected property appropriately.

Can I access view property in {{#with}} scope?

I want to toggle visibility of element in {{#with}} scope, depending on the value of view's property.
I can access to view property in {{action}} in {{#with}}, but I can not access to view property in {{#with}.
Below is a sample program.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="js/libs/jquery-1.6.1.min.js"%3E%3C/script%3E'))</script>
<script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.5.js"></script>
<script type="text/javascript">
var App = Em.Application.create();
App.Test = Em.View.extend({
content: {
description: 'names',
names: [{name: 'bob'}, {name: 'john'}]
},
viewDetail: false,
toggleDetail: function() {
this.set('viewDetail', true);
}
})
</script>
<script type="text/x-handlebars">
{{#view App.Test}}
{{#with content}}
{{description}}<br/>
{{#each names}}
{{name}}
{{/each}}
<br/>
<button {{action toggleDetail}}>toggle Detail</button> <!-- can access toggleDetail -->
{{#if viewDetail}} <!-- but can not access viewDetail... -->
…detail Description...
{{/if}}
{{/with}}
{{/view}}
</script>
Can I access to view property in {{#with}} scope ?
#with modifies the Handlebars context. You can go back up a level by using ../.
Give this a try:
{{#if ../viewDetail}}