how to CheckALL with Ember.js checkBox - ember.js

could someone give me a sample?
{{view Ember.Checkbox checkedBinding="isAllChecked"}}
{{view Ember.Checkbox }}
{{view Ember.Checkbox }}

Like this http://jsfiddle.net/marciojunior/G2Hrz/
App.IndexController = Ember.Controller.extend({
isDog: false,
isCat: false,
isLion: false,
isAll: function(_, value) {
if (arguments.length == 2) {
this.set("isDog", value);
this.set("isCat", value);
this.set("isLion", value);
}
return this.get("isDog") && this.get("isCat") && this.get("isLion");
}.property("isDog", "isCat", "isLion")
});

I don't really know if this is what you want, but here is a try: http://jsbin.com/ekoyiw/8/edit
App.IndexController = Ember.ObjectController.extend({
checkedOne: false,
checkedTwo: false,
checkedThree: false,
isAllChecked: false,
checkAll: function() {
var isAll = this.get('isAllChecked');
this.setProperties({checkedOne: isAll, checkedTwo: isAll, checkedThree: isAll});
}.observes('isAllChecked')
});
Hope it helps.

You can iterate thru child views and make each item checked. For Ember 1.11 or higher.
Imagine you have a similar setup:
{{ input type='checkbox' checked=isAllChecked }}
{{#each message in model}}
{{#component 'message-row' tagName='div' as |component|}}
{{ input type='checkbox' checked=component.isChecked }}
{{/component}}
{{/each}}
First checkbox outside the #each block sets whether all items will be checked or not.
checkAll: function() {
var isAllChecked = this.get('controller.isAllChecked');
this.get('childViews').filterBy('tagName', 'div').forEach(function(row) {
row.set('isChecked', isAllChecked);
});
}.observes('controller.isAllChecked')
We observe it against any changes, if any occurs checkAll is triggered, where it finds all child views, filters only the tags we need (div's in this case, since each checkbox is wrapped in a div), iterates them and set isChecked=true.
Since childViews is only accessible from view, code above should reside in the view class, not controller.

Related

Ember.js Hiding a view if no childView is created

What I'm having here is basically look like this:
filter
|_ todo
|_ todo
filter
|_ todo
filter
|_ todo
Several filterView which have todoView nested inside.
So first I'm creating instances of filterView and pass in all the params.
<ul id="todo-list">
{{view 'filter' control=controller.beforeFilter title="Before" }}
{{view 'filter' param='0' control=controller.todayFilter title="Today"}}
{{view 'filter' param='1' control=controller.tomorrowFilter title="Tomorrow" }}
</ul>
This is how it look like in filterView:
App.FilterView = Ember.View.extend({
classNames: ['filter-container'],
templateName: 'datefilter',
title: 'Today',
param: null,
control: null,
isHide: false,
click: function(){
this.toggleProperty('isHide');
}
});
and the corresponding template:
<div class="filter-bar">
<label class="filter-title">{{view.title}}</label>
<label class="filter-date">{{generateDate view.param}}</label> <!-- This is a handlebar's helper -->
<div class="filter-right-container">
<div class="filter-count">
<label> count </label> <!-- Show number of todos in this filter -->
</div>
</div>
</div>
<div class="filter-box" {{bind-attr class=view.isHide:hide}}>
{{#each todo in view.control}} <!-- So this will turn to in controller.someFunction -->
{{view 'todo'}}
{{/each}}
</div>
And this will be the TodoView
App.TodoView = Ember.View.extend({
templateName: 'todolist',
contentBinding: 'this',
classNames: ['todo-box']
})
And finally the controller
App.TodosController = Ember.ArrayController.extend({
beforeFilter: function(){
return this.get('model').filter(function(todo, index){
var date = todo.get('date');
if(moment(date).isBefore(moment(), 'days')){
return todo;
}
});
}.property('model.#each.date'),
todayFilter: function(){
return this.get('model').filter(function(todo, index){
var date = todo.get('date');
if(moment().isSame(date, 'days')){
return todo;
}
});
}.property('model.#each.date'),
tomorrowFilter: function(){
return this.get('model').filter(function(todo, index){
var date = todo.get('date');
if((moment().add(1, 'days')).isSame(date, 'days')){
return todo;
}
});
}.property('model.#each.date'),
});
So the TodoView will be created according to the return filtered record, but sometimes nothing will get returned. So the question is how to hide the filterView if no TodoView is created?
Is there something like
{{#each todo in view.control}}
{{view 'todo'}}
{{else}}
{{'Set filterView isVisible to false'}}
{{/each}}
or I could easily get this done using collectionView? but how?
Really appreciate to any help
Here is complete solution.
To sum it up :
An ArrayController to hold all your events
Each event holds a date
Header elements hold a date with truncated hours and a boolean for display
In your template, simply iterate over your array and display the header as you like (this is my controller context):
{{#each randDate in this}}
<div {{bind-attr class=":border-row randDate.isHeader:header"}}>{{formatDate randDate.date isHeader=randDate.isHeader}}</div>
{{/each}}
To differenciate whether there is a date following or not, an easy choice would be to put all your events objects into a [LinkedList][2] data structure and not just a simple Array. This way, each event knows the one after himself and knows if it should be displayed. There are tons of implementations of this kind of list, so just pick one where an element knows its next element (the Doubly for instance, but maybe its not the best suited for your case). Then, you could do something like that (this is pseudo code) :
// inside the each loop
{{#if randDate.isHeader && randDate.next.isHeader}} // not sure this && operator is supported by handlebars at the moment
// here you have 2 headers one after the other, do nothing
{{else}}
// one of the 2 is not a header, display your header/event as usual
{{/if}}
Does it help ?
So what I did is instead of return directly from the controller, I check the length and save it in another variable:
beforeFilter: function(){
var data = this.get('model').filter(function(todo, index)
{
var date = todo.get('date');
if(moment(date).isBefore(moment(), 'days')){
return todo;
}
});
this.set('beforeCount', data.length);
return data;
}.property('model.#each.date')
When creating new instance of view, I'll pass one more param in (the controller.variable which save the length):
{{view 'filter' control=controller.beforeFilter countControl=controller.beforeCount title="Before" }}
And in the view, we can first check the length, and if theres nothing, we will hide the header:
dataChanged: function(){
var count = this.get('countControl'); //<-- this will get the length of the return data
if(count<1){
this.set('isVisible', false);
}
}.observes('countControl').on('didInsertElement')

EmberJS: Property Scopes in an ArrayController?

this is probably a grossly simple question to answer, so I apologize if I am cluttering this forum in advance.
I am displaying a list of items that share the same model and controller.
I made these items editable via a <button {{ action 'edit' }}> next to each item which toggles a boolean value of a property "isEditable" in the controller.
However clicking this button causes all items in the list to become editable because they all share the controller property "isEditable". The desired effect is to make a single item editable at a time instead of all items at once.
A simplified version of my template looks like this:
{{#if isEditing}}
<p>{{input type="text" value=title}}</p>
<button {{action 'doneEditing'}}>Done</button>
{{else}}
<span class="title">{{title}}</span>
<button {{action 'edit'}}><span class="edit"</span></button>
{{/if}}
and the controller looks like this
App.ItemController = Ember.ArrayController.extend({
isEditing : false,
actions : {
edit : function(){
this.set('isEditing', true);
},
doneEditing : function(){
this.set('isEditing', false);
},
}
});
Anybody know how to accomplish this? Is it because each item shares the "isEditable" property? If so, how do I get around this? I don't want to put this into the model because it's purely a display thing, even though I know I can get it to work doing that.
Thanks :)
By default the controller lookup within an {{#each}} block will be the controller of the template where the {{#each}} was used. If each item needs to be presented by a custom controller (to hold it's own state for example) you can provide a itemController option which references a controller by lookup name. Each item in the loop will be then wrapped in an instance of this controller and the item itself will be set to the content property of that controller.
So, I assume you are displaying the list of items using the {{#each}} helper. Therefore you can specify an itemController in the {{#each}} helper to hold the isEditable state on a per item basis. This would look something like this:
{{#each item in controller itemController="item"}}
...
{{/each}}
Moreover you should define the defined itemController of type Ember.ObjectController like:
App.ItemController = Ember.ObjectController.extend({
isEditing : false,
actions : {
edit : function(){
this.set('isEditing', true);
},
doneEditing : function(){
this.set('isEditing', false);
},
}
});
And for the list you should then have an App.ItemsController of type Ember.ArrayController:
App.ItemsController = Ember.ArrayController.extend({});
See here for more info on the mentioned itemController support for the {{#each}} helper: http://emberjs.com/api/classes/Ember.Handlebars.helpers.html#method_each
Hope it helps.

get a reference to this.objectController attribute from an Ember.TextField class

In my app I have a generic text field of type Em.TextField:
App.DetailTextField = Em.TextField.extend({
attributeBindings: ['required', 'readonly', 'name']
});
In my template I use the DetailTextField to show data, to specify an attribute, and to show it in the class of either 'editing' or 'viewing':
{{view App.DetailTextField viewName="tbSurname" placeholder="surname"
valueBinding="surname" required="required" classNameBindings="isEditing:editing:viewing" readonlyBinding='getReadOnlyState'}}
This works fine but I have several of these fields, all of which have the same part: classNameBindings="isEditing:editing:viewing" readonlyBinding='getReadOnlyState'. isEditing and getReadOnlyState are retrieved from the current objectController of the template view.
Is there a way to put the classNameBindings and readonlyBinding into the DetailTextField class definition, so that it does not need to be explicitely typed into every instance of the DetailTextField view? That is, can DetailTextField get the current context - e.g:
App.DetailTextField = Em.TextField.extend({
attributeBindings: ['required', 'readonly', 'name'],
classNameBindings: "this.view.get('isEditing'):editing:viewing"
});
I could make isEditing a function within the class definition that retrieved the value from the controller, but I still have the same problem in that I would not know how to reference the activeController / this.controller.
Any thoughts?
To access controller from your view, simply
mycontroller = this.get('controller');
Btw, I prefer to put isEditing attribute in my model, so I don't have to extend views.
App.MyModel.reopen({
isEditing: false
});
So I can loop each of them...
{{#each model}}
{{#if isEditing}}
{{view Em.TextField valueBinding="yourvaluebindinghere"}}
<button {{action 'update' this}}>Update</button>
{{else}}
{{yourvaluebindinghere}}
<button {{action 'edit' this}}>Edit</button>
{{/if}}
{{/each}}
And put the actions in my controller:
App.MyController = Em.ArrayController.extend({
actions: {
edit: function(model) {
model.set('isEditing', true);
},
update: function(model) {
model.save();
model.set('isEditing', false);
}
}
});

How can I render a block only if a specific route is active?

I wanna render a block in Ember Handlebars only, if a specific route is active.
So, how can I create a 'ifRoute' helper, with the same conditons then the 'active' class on the 'linkTo' helper?
I want this, because I've a two layer navigation. So, I want to show the sub-navigation only, if the head navigation point is active. I dont wanna use the 'active' class, because I use lazy loading and I only want to load the sub navigation when the head navigation point is active.
So, what I want to do is:
<ul>
{{#each assortmentGroups}}
<li>
{{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
{{#ifRoute "assortmentGroup" this}}
<ul>
{{#each itemCategories}}
<li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
{{/each}}
</ul>
{{/ifRoute}}
</li>
{{/each}}
<ul>
How can I do this or is there a better solution?
Thanks
Just add to the controller:
needs: ['application'],
isCorrectRouteActive: Ember.computed.equal('controllers.application.currentRouteName', 'correctRoute')
Similarly:
isCorrectPathActive: Ember.computed.equal('controllers.application.currentPath', 'correct.path')
isCorrectURLActive: Ember.computed.equal('controllers.application.currentURL', 'correctURL')
I am quite sure latest Ember does the rest
Here are two possible options, although for both you first have to save the currentPath in your ApplicationController to have access to it whenever you need it:
var App = Ember.Application.create({
currentPath: ''
});
App.ApplicationController = Ember.ObjectController.extend({
updateCurrentPath: function() {
App.set('currentPath', this.get('currentPath'));
}.observes('currentPath')
});
Using a computed property
Then in the controller backing up the template, let's say you have a NavigationController you create the computed property and define also the dependency to the ApplicationController with the needs API to gather access, then in the CP you check if the currentPath is the one you want:
App.NavigationController = Ember.Controller.extend({
needs: 'application',
showSubMenu: function(){
var currentPath = this.get('controllers.application.currentPath');
return (currentPath === "assortmentGroup");
}.property('controllers.application.currentPath')
});
So you can use a simple {{#if}} helper in your template:
...
{{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
{{#if showSubMenu}}
<ul>
{{#each itemCategories}}
<li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
{{/each}}
</ul>
{{/if}}
</li>
...
Using a custom '{{#ifRoute}}' helper
But if your really want a custom helper to deal with your condition then this is how you could do it, note that the currentPath stored on your application is still needed since we need a way to get the value of the current route:
Ember.Handlebars.registerHelper('ifRoute', function(value, options) {
if (value === App.get('currentPath')) {
return options.fn(this);
}
else {
return options.inverse(this);
}
});
And then you could use it like this:
...
{{#linkTo "assortmentGroup" this}} {{description}} {{/linkTo}}
{{#ifRoute "assortmentGroup"}}
<ul>
{{#each itemCategories}}
<li>{{#linkTo "itemCategory" this}} {{description}} {{/linkTo}}</li>
{{/each}}
</ul>
{{/ifRoute}}
</li>
...
See here also a simple Demo of the "custom helper" solution: http://jsbin.com/izurix/7/edit
Note: with the second solution there is a catch! Since bound helpers do not support blocks (in embers handlebars customization) I used a simple helper that does not reevaluate the condition depending on bindings which is may not what you want.
Hope it helps.
After investigating the ember code for the linkTo and if helpers, the answer from intuitivepixel and a blog post about writing my own bound block helper, I've found a solution:
var resolveParams = Ember.Router.resolveParams;
var resolvedPaths = function(options) {
var types = options.options.types.slice(1),
data = options.options.data;
return resolveParams(options.context, options.params, { types: types, data: data });
};
Ember.Handlebars.registerHelper('ifRoute', function(name) {
var options = [].slice.call(arguments, -1)[0];
var params = [].slice.call(arguments, 1, -1);
var theResolvedPaths = resolvedPaths({ context: this, options: options, params: params });
var router = options.data.keywords.controller.container.lookup('router:main');
var self = this;
var evaluateIsCurrentRoute = function() {
self.set('current_route_is_active_bool_for_ifroute', (function() {
return router.isActive.apply(router, [name].concat(theResolvedPaths)) ||
router.isActive.apply(router, [(name + '.index')].concat(theResolvedPaths));
})());
};
evaluateIsCurrentRoute();
router.addObserver('url', evaluateIsCurrentRoute);
options.contexts = null;
return Ember.Handlebars.helpers.boundIf.call(this, 'current_route_is_active_bool_for_ifroute', options);
});
I found an easy way to check if a route is active, but to get this into a computed property may not be so easy.
// Test if you are currently in a route by it's lowercase name
App.isInRoute = function(name) {
return App.Router.router.currentHandlerInfos.mapProperty('name').contains(name);
}
To use:
App.isInRoute('posts.show'); // true if in the route

What is the best way to handle events in Ember.js?

I'm beginning to learn Ember and it's not clear what the best, most acceptable, or even intended method to handle events is. Is it acceptable to check the target in the click functions event argument, should I make a new view for each item that requires an event other than {{action}}, or something totally different?
IMO you should use the {{action}} helper where possible. If you want to attach events on a tag in the template, use {{action}}; no need to make a new View:
<a {{action showPosts href=true}}>All Posts</a>
<form {{action validate target="controller"}}>
// ...
</form>
An exception to the above is when you want to handle more than one events on a specific element:
// Template
<ul>
{{#each post in controller}}
{{#view App.PostView}}
{{title}}
{{#if view.showDetails}}
<span>{{summary}}</span>
{{/if}}
{{/view}}
{{/each}}
</ul>
// View
App.PostView = Ember.View.extend({
tagName: li,
classNames: ['post-item'],
mouseEnter: function(event) {
this.set('showDetails', true);
},
mouseLeave: function(event) {
this.set('showDetails', false);
}
});
As we need to capture both mouseEnter and mouseLeave (to show and hide the details of the post respectively), it is better to do it in the View, avoiding too much logic in the templates. The alternative way for the above would be to use as many nested tags as the number of events we want to handle (in our case, 2):
// Template
<ul>
{{#each post in controller}}
<li class="post-item" {{action showTheDetails post on="mouseEnter" target="controller"}}>
<span class="dummy" {{action hideTheDetails post on="mouseLeave" target="controller"}}
{{title}}
{{#if post.showDetails}}
<span>{{summary}}</span>
{{/if}}
</span<
</li>
{{/each}}
</ul>
And then in the controller:
// Controller
App.PostsController = Ember.ArrayController.extend({
showTheDetails: function(event) {
var post = event.context;
post.set('showDetails', true);
},
hideTheDetails: function(event) {
var post = event.context;
post.set('showDetails', false);
}
});
But I think you will agree that this is uglier. See here.
In cases where you want to use Ember control views (Ember.TextField, Ember.TextArea, etc.) you have no choice but to capture events in the View. So you extend the control view and define the event handlers in the View:
// Template
<legend>Add a comment</legend>
{{view App.CommentInputField valueBinding="comment"}}
// View
App.CommentInputField = Ember.TextField.extend({
focusOut: function(event) {
this.get('controller').validateComment();
},
keyDown: function(event) {
if (event.keyCode === 13) { // Enter key
this.get('controller').createComment();
return false;
}
}
});