How to select an element using mouse drag in ember? - ember.js

I have my template like this,
<div>
{{#each model as |item|}}
{{#view "selection" model=item}}
<div class="child_div">{{item.name}}</div>
{{/view}}
{{/each}}
</div>
In my js, I am using a view for select an clicked element like,
Model:
App.IndexRoute = Ember.Route.extend({
model: function() {
return [{'is_active':false, 'name':'One'}, {'is_active':false, 'name':'Two'}, {'is_active':false, 'name':'Three'}, {'is_active':false, 'name':'Four'},{'is_active':false, 'name':'Five'}];
}
});
Selection View:
App.SelectionView = Ember.View.extend({
classNameBindings: ["isActive"],
isActive: Ember.computed.alias('model.is_active'), // No I18N
click: function (){
var self = this; self.get("controller").setEach("is_active", false); // No I18N
self.toggleProperty("isActive"); // No I18N
}
});
Here, I am selecting that div's in click event. I need to select them when I do use mouse drag.
How do I do that using mouse drag selection? Kindly help me out of that.
DEMO: JSBIN

Add the draggable attribute to your view and use the dragStart event.
attributeBindings : [ 'draggable' ],
draggable : 'true',
dragStart: function(event) {
var self = this;
self.get("controller").setEach("is_active", false); // No I18N
self.toggleProperty("isActive");
}
Here is working demo.
Here is a good article from Lauren Tan on drag n drop in Ember.

Related

How to prevent double clicks with ember.js?

I'm trying to figure out the idiomatic way to prevent a button from being clicked multiple times.
Imagine I have a simple controller action like so ...
var FooController = Ember.ObjectController.extend({
actions: {
go: function() {
console.log("done!");
}
}
});
and in my template I have a button defined like so ...
<button {{action go}}>Click Me Fast</button>
Does the action have an option to disable it immediately / making it so only once true event will be handled by the controller (until it's disabled for example)
Edit
I'm looking for a long term / multi use solution. One idea I'm thinking about is creating a special ember-component called "button-disable" that would allow me to create a custom button type that generally disables after a single click -but will still allow me to bubble up events to a parent controller. This feels a little heavier weight than I'd like so if another option exists, or if someone has created an addon for just this - let me know
As a one-off, if you bind the disabled attribute on your button like so
<button {{action go}} {{bind-attr disabled=actionPerformed}}>
and then set up your controller like
var FooController = Ember.ObjectController.extend({
actionPerformed: false,
actions: {
go: function() {
this.set("actionPerformed", true);
console.log("done!");
}
}
});
then the button will become disabled after you click it once
If you want a reusable component I'd borrow the spinner button from http://emberjs.com/guides/cookbook/helpers_and_components/spin_button_for_asynchronous_actions/ and tweak it as you need.
So your JS would be along the lines of
window.SpinEg = Ember.Application.create({});
SpinEg.ApplicationController = Ember.Controller.extend({
isLoading:false,
buttonText:"Submit",
actions:{
saveData:function(){
var self = this;
var saveTime = Ember.run.later(function(){
self.set('isLoading', false);
}, 1000);
}
}
});
SpinEg.SpinButtonComponent = Ember.Component.extend({
classNames: ['button'],
buttonText:"Save",
isLoading:false,
actions:{
showLoading:function(){
if(!this.get('isLoading')){
this.set('isLoading', true);
this.sendAction('action');
}
}
}
});
The template for your component would be
<script type='text/x-handlebars' id='components/spin-button'>
<button {{bind-attr id=id}} {{action 'showLoading'}}>
{{#if isLoading}}
<img src="http://i639.photobucket.com/albums/uu116/pksjce/spiffygif_18x18.gif"></img>
{{else}}
{{buttonText}}
{{/if}}
</button>
</script>
and you would then just include the following where you need the button to appear
<script type='text/x-handlebars' id='application'>
{{spin-button id="forapplication" isLoading = isLoading buttonText=buttonText action='saveData'}}
</script>

Dashboard style quick editing in Ember.js

I'm attempting to make an admin backend for my Rails app with Ember.
Here's a JsBin illustrating the problems I'm having.
http://emberjs.jsbin.com/titix/20/edit
In short, I want to be able to edit the title of a arbitrary model inside of a list of other models when a user clicks on it.
Relevant CoffeeScript with questions in the comments:
App.ItemView = Ember.View.extend
templateName: "item"
isEditing: false
didInsertElement: ->
# 1. Is there a better way to toggle the isEditing property when the title is clicked?
view = #
#$('.title').click ->
view.toggleProperty('isEditing')
# 2. How would I unset isEditing when the user clicks on a different App.ItemView?
# 3. How do I set App.ItemController to be the controller for App.ItemView?
App.ItemController = Ember.Controller.extend
# 4. How would I then toggle the isEditing property of App.ItemView on either save of cancel from App.ItemController?
actions:
save: ->
# set isEditing is false on App.ItemView
#get('model').save()
cancel: ->
# set isEditing is false on App.ItemView
#get('model').rollback()
Any help on any of these questions would be appreciated.
Okay, let's see if I can remember to answer all of the questions.
Firstly we decide to wrap the entire set of items in an array controller (this allows us to keep track of all of the children item controllers). It also allows us to define an itemController which the items can use.
<script type="text/x-handlebars" data-template-name="item-list">
<h3>{{view.title}}</h3>
<ul>
{{render 'items' view.content}}
</ul>
</script>
App.ItemsController = Em.ArrayController.extend({
itemController:'item',
resetChildren: function(){
this.forEach(function(item){
item.set('isEditing', false);
});
}
});
Secondly the render template is defined ({{render 'items' view.content}} will render the items template)
<script type="text/x-handlebars" data-template-name="items">
{{#each item in controller}}
<li>{{view App.ItemView content=item}}</li>
{{/each}}
</script>
Thirdly since we iterated over the controller it will use this modified item controller
App.ItemController = Ember.ObjectController.extend({
isEditing: false,
isSaving: false,
actions: {
startEditing: function(){
this.parentController.resetChildren();
this.set('isEditing', true);
},
save: function() {
var self = this;
this.set('isEditing', false);
this.set('isSaving', true);
this.get('model').save().finally(function(){
//pretend like this took time...
Em.run.later(function(){
self.set('isSaving', false);
}, 1000);
});
},
cancel: function() {
this.set('isEditing', false);
this.get('model').rollback();
}
}
});
and here's our template
<script type="text/x-handlebars" data-template-name="item">
{{#if controller.isEditing}}
{{input value=controller.title }}
<button {{ action 'cancel' }}>Cancel</button>
<button {{ action 'save' }}>Save</button>
{{else}}
<div {{action 'startEditing'}}>
<div class="title">{{controller.title}}</div>
</div>
{{/if}}
{{#if controller.isSaving}}
Saving...
{{/if}}
</script>
Example: http://emberjs.jsbin.com/jegipe/1/edit
Here is a working bin toggles the state of the form item in the following conditions, save button click, cancel button click and click on an another item.
Every time we click on an item, I save the item views reference to the index controller. When an other item is clicked, I use the a beforeObserver to set the previous item views state to false.
I also specified the item controller in the template.
App.IndexController = Em.ObjectController.extend({
currentEditingItem: null,
currentEditingItemWillChange: function() {
if(this.get('currentEditingItem')) {
this.set('currentEditingItem.isEditing', false);
}
}.observesBefore('currentEditingItem'),
});
App.ItemController = Ember.Controller.extend({
needs: ['index'],
formController: Em.computed.alias('controllers.index'),
currentEditingItem: Em.computed.alias('formController.currentEditingItem'),
actions: {
save: function() {
this.set('currentEditingItem.isEditing', false);
return this.get('model').save();
},
cancel: function() {
this.set('currentEditingItem.isEditing', false);
return this.get('model').rollback();
}
}
});

How to add/remove class to array of objects in Ember JS

I try to learn Ember JS. And i can not find answer for my question . I have template
<script type="text/x-handlebars">
<table class="table">
{{#each App.todoListController}}
{{#view App.ViewTable todoBinding="this"}}
<td>{{title}}</td>
<td><a href="javascrpt:" {{action "deleteTodo" target="view"}}>X</a></td>
{{/view}}
{{/each}}
</table>
<button {{action "deleteTodo" target="App.todoListController"}}>Delete</button>
</div>
</script>
In app.js I have Controller and View :
/*******************
Controller
*******************/
App.todoListController = Em.ArrayController.create({
content : [],
createTodo : function(title) {
var todo = App.Todo.create({title:title})
this.pushObject(todo)
}
});
/*******************
View
*******************/
App.ViewTable = Em.View.extend({
tagName : 'tr',
classNameBindings : ['isHover:hover'],
isHover : false,
todo : null,
deleteTodo : function(){
var tr = this.get('todo');
App.todoListController.removeObject(tr);
},
click : function()
{
this.set('isHover',true);
}
})`
When i clicked to row of table , it changed class to "hover" . Now question : I can't remove class "hover" from all objects (it is necessary for only one object can be selected)
PS : Sorry for my English and sorry for the formatting.
One way to do this would be to move the "isHover" property to the "todo" item so that you can search all the "todo" items in the controller, and set/unset the "isHover" property on them :
Rename:
App.ViewTable
to
App.TableView
Ember looks for the keyword 'View' at the end of the name.
Add a name to the items in your each statement (I used "thing"):
{{#each thing in App.todoListController}}
instead of :
{{#each App.todoListController}}
that way it's easier to make references later.
Use the name you defined above (thing in this case) for your binding (and remove the quotes):
{{#view App.TableView todoBinding=thing}}
Instead of:
{{#view App.ViewTable todoBinding="this"}}
Now your tableView will have a reference to the 'todo' that it is displaying
Move "isHover" into the Todo item's object:
App.Todo = Em.Object.extend({
title:"...",
isHover: false
});
Bind "isHover" in your table view:
....
tagName : 'tr',
isHoverBinding: 'this.todo.isHover',//this should be before your classNameBindings
classNameBindings : ['isHover:hover'],
...
Now change your 'click' function to:
click : function() {
//Get a list of the other hovered items from the controller:
var otherHoveredItems = App.todoListController.get('content').filterProperty('isHover', true);
//Iterate each hovered item and set hover to false
for ( var i = 0; i < otherHoveredItems.length; i++) {
otherHoveredItems[i].set('isHover', false);
}
//set this item to hover
this.get('todo').set('isHover', true);
}
Here's a fiddle example: http://jsfiddle.net/amindunited/kY4nh/
Another method, would be to move your {{#each}} into a collectionView. The handlebars {{each}} is a collectionView, so this wouldn't be a big jump. One caveat is that the click method alone won't give you the context, BUT if you wrap the click function in an eventManager, you will get the view as the second reference...sounds messy, but it's actually tidier: http://jsfiddle.net/amindunited/5hNSZ/

Event delegation for target

In the following code, my click events delegate and all three 'click' handlers in my view hierarchy get fired.
However, I also want to fire 'edit' in my entire view hierarchy. 'edit' is simply the target of an element in my 'child' view.
Template
<script type="text/x-handlebars">
{{#view App.GrandparentView}}
{{#view App.ParentView}}
{{#view App.ChildView}}
<span {{action "edit" target="view"}}>Click Me</span>
{{/view}}
{{/view}}
{{/view}}
</script>
​
JavaScript
App.GrandparentView = Ember.View.extend({
click: function() {
console.log('Grandparent Click Fired!');
},
edit: function () {
console.log('GrandParent edit fired');
}
});
App.ParentView = Ember.View.extend({
click: function() {
console.log('Parent Click Fired!');
},
edit: function () {
console.log('Parent edit fired');
}
});
App.ChildView = Ember.View.extend({
click: function() {
console.log('Child Click Fired!');
},
edit: function () {
console.log('Child edit fired');
}
});​
Is there no way to delegate the target handlers in the view hierarchy? What I dont want to do is this:
App.ChildView = Ember.View.extend({
click: function() {
console.log('Child Click Fired!');
},
edit: function () {
console.log('Child edit fired');
this.get('parentView').edit(); //DO NOT WANT TO DO THIS.
}
});​
Here is a jsFiddle as an example to test.
It looks like the same question you've posted about a week ago. As far as I can see, such feature is not implemented in ember. The event is propagated to the view hierarchy, but the action name is lost, and the default click handler is triggered.
The only workaround I found is to reopen the Ember.View class itself, and override the click handler like this:
Ember.View.reopen({
click: function(event){
if(event.view !== this && this[event.actionName]){
return this[event.actionName](event);
}
}
})
See the fiddle here:
http://jsfiddle.net/Sly7/zZyCS/

How to pass events to a parent View, passing the child View that triggered the event?

Consider a View that defines a list of objects:
App.ListView = Ember.View({
items: 'App.FooController.content'
itemClicked: function(item){
}
)};
with the template:
<ul>
{{#each items}}
{{#view App.ItemView itemBinding="this" tagName="li"}}
<!-- ... -->
{{/view}}
{{/each}}
</ul>
and the ItemView:
App.ItemView = Ember.View.extend({
click: function(event){
var item = this.get('item');
// I want to call function itemClicked(item) of parentView
// so that it handles the click event
}
})
So basically my question is how do I pass events to parent views, especially in the case where the parent view is not known by the child view? I understand that you can get a property foo of a parentView with either this.getPath('parentView').get('foo') or this.getPath('contentView').get('foo'). But what about a function (in this case, itemclicked())?
this.get('parentView').itemClicked(this.get('item')); should do the trick.
You can use the {{action}} helper, see: http://jsfiddle.net/smvv5/
Template:
<script type="text/x-handlebars" >
{{#view App.ListsView}}
{{#each items}}
{{#view App.ListView itemBinding="this" }}
<li {{action "clicked" target="parentView" }} >{{item.text}}</li>
{{/view}}
{{/each}}
{{/view}}
</script>​
JS:
App = Ember.Application.create({});
App.Foo = Ember.ArrayProxy.create({
content: [Ember.Object.create({
text: 'hello'
}), Ember.Object.create({
text: 'action'
}), Ember.Object.create({
text: 'world'
})]
});
App.ListsView = Ember.View.extend({
itemsBinding: 'App.Foo',
clicked: function(view, event, ctx) {
console.log(Ember.getPath(ctx, 'item.text'));
}
});
App.ListView = Ember.View.extend({
});​
Recent versions of Ember use the actions hash instead of methods directly on the object (though this deprecated method is still supported, it might not be for long). If you want a reference to the view passed to the handler, send through "view" as a parameter and use the parentView as the target.
<button {{action "onClicked" view target="view.parentView"}}>Click me.</button>
App.ListsView = Ember.View.extend({
actions: {
onClicked: function(view) {
}
}
});
{{action}} helper does not send through the event object. Still not sure how to get reference to the event if you need it.
source