Sample task application with drag and drop - ember.js

app.js
App = Em.Application.create();
App.IndexRoute = Em.Route.extend({
model: function(){
return {
newTasks: Em.A([
{id: 1, name: "Task 1"},
{id: 2, name: "Task 2"},
{id: 3, name: "Task 3"}
]),
inProgressTasks: Em.A([
{id: 4, name: "Task 4"},
{id: 5, name: "Task 5"}
]),
doneTasks: Em.A([
{id: 6, name: "Task 6"}
])
};
}
});
App.IndexController = Em.Controller.extend({
actions: {
moveTask: function(taskID, from, to){
var model = this.get('model');
var task = model[from].findProperty('id', parseInt(taskID, 10));
model[to].pushObject(task);
model[from].removeObject(task);
}
}
});
App.TaskContainerComponent = Em.Component.extend({
classNames: ['col-xs-4', 'taskContainer'],
isOverdrop: false,
classNameBindings: ['isOverdrop:isOverdrop'],
setOverdropIfNotOriginator: function(event, valueToSet){
var data = JSON.parse(event.dataTransfer.getData('text/data'));
if(data.stage !== this.get('stage')) {
this.set('isOverdrop', valueToSet);
}
},
dragEnter: function(event) {
this.setOverdropIfNotOriginator(event, true);
},
dragLeave: function(event){
this.setOverdropIfNotOriginator(event, false);
},
dragOver: function(event){
this.setOverdropIfNotOriginator(event, true);
event.preventDefault();
},
drop: function(event) {
var data = JSON.parse(event.dataTransfer.getData('text/data'));
if(data.stage === this.get('stage')) return;
// from: data.stage, to: this.get('stage')
this.sendAction('action', data.id, data.stage, this.get('stage'));
this.set('isOverdrop', false);
}
});
App.DragTaskComponent = Em.Component.extend({
dragStart: function(event) {
var data = { id: this.get('task.id'), stage: this.get('stage')};
event.dataTransfer.setData('text/data', JSON.stringify(data));
}
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mini Scrumboard</title>
<link href="http://getbootstrap.com/dist/css/bootstrap.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script type="text/x-handlebars" data-template-name="index">
<div class="contents">
<div class="row">
{{ task-container containerTitle="New" stage="newTasks" tasks=model.newTasks
action="moveTask" on="drop"}}
{{ task-container containerTitle="In Progress" stage="inProgressTasks"
tasks=model.inProgressTasks action="moveTask" on="drop" }}
{{ task-container containerTitle="Done" stage="doneTasks" tasks=model.doneTasks
action="moveTask" on="drop" }}
</div>
<br>
<br>
</div>
</script>
<!-- Properties: task, stage -->
<script type="text/x-handlebars" id="components/drag-task">
<div class="task" draggable="true">
{{task.name}}
</div>
</script>
<!-- Properties: containerTitle, stage, tasks -->
<script type="text/x-handlebars" id="components/task-container">
<h3>{{containerTitle}}</h3>
{{#each task in tasks}}
{{drag-task task=task stage=stage}}
{{/each}}
</script>
<script src="js/libs/jquery-1.10.2.js"></script>
<script src="js/libs/handlebars-1.1.2.js"></script>
<script src="js/libs/ember-1.5.1.js"></script>
<script src="js/app.js"></script>
<!-- to activate the test runner, add the "?test" query string parameter -->
<script src="tests/runner.js"></script>
</body>
</html>
I have this working perfectly fine on my local box, but it is errorring out on jsbin.
http://emberjs.jsbin.com/movex/4/edit?html,css,js,output
I am guessing that the issue has to do with parsing the output from the drag and drop, but have no clue where to go with this.
Any help would be greatly appreciated...
UPDATE: The JSBin version even works fine on the FF on the Mac, but not on Safari or Chrome... :(

The reason it was not working is because of the very unexpected way that the drag and drop specifications work. The problem is that in the drag, dragEnter, dragLeave, dragOver and dragEnd events the dragTransfer data is in protected mode. Which again according to the spec means.
the data itself is unavailable and no new data can be added.
It seems as though Mozilla exercised some common sense and didn't implement drag and drop in compliance with the spec. Which explains why it was working in Firefox for you, but nowhere else.
To get your jsbin working I added a theData element to your index controller and did the setting and getting of the JSON values against that.
Here is the working version. http://emberjs.jsbin.com/dasonona/1/edit

Related

Ember - Nothing handled the action occurs

Nothing handled the action error occurs for the following code. How to resolve this?
I have created a view, an object for my sample app using ember. But the action part is not working.
How to bind an action to a view?
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
</head>
<body>
<script type="text/x-handlebars">
<ul class="mod-choosable-list">
{{view Ember.CollectionView
contentBinding="App.teachersController"
itemViewClass="App.TeacherView"
tagName="div"
}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="teacher-view">
<div {{action 'refresh'}}><b>{{view.content.name}}</b></div>
</script>
<script src="js/libs/jquery-v1.11.1.js"></script>
<script src="js/libs/handlebars-v1.3.0.js"></script>
<script src="js/libs/ember-v1.6.1.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.1.0/moment.min.js"></script>
<script src="js/app.js"></script>
</body>
</html>
JS:
App = Ember.Application.create({});
App.Teacher = Ember.ObjectController.extend({
id: null,
name: null,
students: null,
actions: {
refresh: function(){
alert("refresh");
}
}
});
App.TeacherView = Ember.View.extend({
templateName: 'teacher-view'
});
App.set('teachersController', Ember.ArrayController.create({
content: [
App.Teacher.create({id:1, name: "mr.katz", students: [2, 3]}),
App.Teacher.create({id:2, name: "mr.dale", students: [1]})
]
}));
When you trigger the action refresh, ember will look for the action in the controller. Since you have not specified a controller for the view, the controller for the application template will be used which is App.ApplicationController.
You can use the following code and your action will trigger.
App.ApplicationController = Em.Controller.extend({
actions: {
refresh: function(){
alert("refresh");
}
}
});
You can specify the actions in the view too. In that case you will need to specify the target for the action. This will tell ember where to look for the action handler.
App.TeacherView = Ember.View.extend({
templateName: 'teacher-view',
actions: {
refresh: function(){
alert("refresh");
}
}
});
<div {{action 'refresh' target="view"}}><b>{{view.content.name}}</b></div>
You can specify a controller for view on its init event.
App.TeacherView = Ember.View.extend({
templateName: 'teacher-view',
setup:function() {
this.set('controller', App.Teacher.create());
}.on('init')
});

Parse Ember fixture data and make available to nested view

I am trying to feed fixture data to both a handlebars template and a nested view. With my current setup, the data reaches my handlebars template just fine, but not the view. I suspect I am missing some basic piece of the ember.js puzzle. Would someone please advise me?
I've asked a question about a setup that is similar but uses ajax instead of fixture data, but have further simplified my setup in the hopes of getting some direction.
Thank you!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/leaflet.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
{{view App.MapView id="map" contentBinding="this"}}
<div id="blog">
<ul>
{{#each}}
<li>{{title}}</li>
{{/each}}
</ul>
</div>
</script>
<script src="js/libs/jquery-1.10.2.js"></script>
<script src="js/libs/handlebars-1.1.2.js"></script>
<script src="js/libs/ember-1.5.0.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/libs/leaflet-src.js"></script>
<script src="js/app.js"></script>
</body>
</html>
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.Router.map(function() {
this.resource('index', { path: '/' });
});
App.IndexRoute = Ember.Route.extend({
model: function () {
return this.store.find('storyPrev');
}
});
App.StoryPrev = DS.Model.extend({
title: DS.attr('string')
});
App.StoryPrev.FIXTURES = [
{
id: 1,
title: 'You Better Believe This!',
coordinates: [-73.989321, 40.6778]
},
{
id: 2,
title: 'Holy Crap, Unreal!',
coordinates: [-73.989321, 40.6779]
},
{
id: 3,
title: 'Big Bucks Made E-Z!',
coordinates: [-73.989321, 40.6780]
}
];
App.MapView = Ember.View.extend({
didInsertElement: function () {
var map = L.map('map', {zoomControl: false}).setView([40.685259, -73.977664], 14);
L.tileLayer('http://{s}.tile.cloudmade.com/[redacted key]/[redacted id]/256/{z}/{x}/{y}.png').addTo(map);
L.marker([40.685259, -73.977664]).addTo(map);
console.log(this.get('content'));
//THIS IS WHERE I GET STUCK
}
});
The view is backed by a controller, so you would do this.get('controller') to get the controller which is backed by your collection which if you wanted to get the collection (which isn't necessary since you can iterate the controller) you could do this.get('controller.model').
var controller = this.get('controller');
controller.forEach(function(item){
console.log(item.get('title'));
});
http://emberjs.jsbin.com/OxIDiVU/373/edit

Ember JS app shows blank page

I'm creating a simple Ember JS app based loosely on the Beginner's Guide on the website. However, I can't seem to get anything to render on the page. When I check the Ember Inspector my routes are all present and I don't see any errors so it's not immediately clear where the problem is.
Here is the html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Coin</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/bootstrap.min.css">
</head>
<body>
<script type="text/x-handlebars">
<div>Hello</div>
{{outlet}}
</script>
<script type="text/x-handlebars" id="accounts">
<div>accounts</div>
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked">
{{#each}}
<li>account link {{#link-to 'account' this}}{{name}}{{/link-to}}</li>
{{/each}}
</ul>
</div>
<div class="col-md-10">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" id="account">
<ul>
{{#each}}
<li>{{comment}}</li>
{{/each}}
</ul>
</script>
<script src="js/libs/jquery-1.10.2.js"></script>
<script src="js/libs/handlebars-1.1.2.js"></script>
<script src="js/libs/ember-1.4.0.js"></script>
<script src="js/app.js"></script>
<script src="dist/js/bs-core.min.js"></script>
<script src="dist/js/bs-nav.min.js"></script>
<!-- to activate the test runner, add the "?test" query string parameter -->
<script src="tests/runner.js"></script>
</body>
</html>
And here is my app.js file:
App = Ember.Application.create();
App.Router.map(function() {
this.resource('accounts');
this.resource('transactions', { path: ':accounts_id' });
})
App.AccountsRoute = Ember.Route.extend({
model: function() {
return accounts;
}
})
App.TransactionsRoute = Ember.Route.extend({
model: function() {
return transactions;
}
})
var accounts = [{
id: 1,
name: 'Account 1',
}, {
id: 2,
name: 'Account 2'
}];
var transactions = [{
id: 1,
id_account: 1,
amount: 12,
comment: 'blah'
}, {
id: 2,
id_account: 1,
amount: 5,
comment: 'blah2'
}, {
id: 3,
id_account: 2,
amount: 98,
comment: 'blah4'
}];
The one thing that renders properly on the page is the "Hello" in the application template. Nothing shows in the {{outlet}} when I navigate to #/accounts or any of the other defined routes. And in fact, when I add #/accounts in the url even my "Hello" disappears.
Is there something obvious that I'm overlooking? I'm new to Ember so I'm sure it's something silly but I can't figure out where the problem is from the documentation.
edit: I'm using Ember v1.4.0 linked directly from the website and I have checked "Allow access to file urls" in Ember Inspector.
I made a working version of what you started in this ember jsbin
What I noticed you were doing wrong was by trying to link to a non-existing route with this {{#link-to 'account' this}}. You didn't define the account route that I notice you want to receive a parameter. However I think you want to send the id of the current item in the iteration. So you can simply do that by changing this into {{#link-to 'account' id}}.
So 2 important modifications to get to something working was to add the route and to fix the param of the link-to helper. Then I just assumed that you want to show transactions as well and fixed that as well.
Hope this helps.

rendering a template with multiple outlets

I was trying to render out some content n the named outlets with no success. I tried following How to render multiple templates for a route in Router v2 but the solution there is not working for me!
My html page looks like:`
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<!-- Set the viewport width to device width for mobile -->
<meta name="viewport" content="width=device-width" />
<title>Demo</title>
<style>
.main-container{
width:800px;
margin:0px auto;
}
</style>
</head>
<body>
<script type="text/x-handlebars" data-template-name="application">
<div class="main-container">
<div>{{outlet topMenu}}</div>
<div>
<div style="float:left; width:30%;">{{outlet sideMenu}}</div>
<div style="float:left; width:70%;">{{outlet content}}</div>
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="menu">
<ul>
{{#each model}}
<li>{{label}}</li>
{{/each}}
</ul>
</script>
<script src="../js/ember/libs/jquery-1.9.1.js"></script>
<script src="../js/ember/libs/handlebars-1.0.0-rc.4.js"></script>
<script src="../js/ember/libs/ember-1.0.0-rc.6.1.js"></script>
<script src="../js/ember/libs/ember-data-master.js"></script>
<script src="../js/ember/demo.js"></script>
</body>
</html>
At the moment I don't even want to bother about the sideMenu and content, I just wanted to get the top menu showing up to start with.The corresponding js is:
window.App = Ember.Application.create();
// Router
App.Router.map(function() {
this.resource('menu',{ path: "/" },function(){
this.resource('sideMenu',{ path: "/:menu_id" },function(){
this.resource('content',{path:'/content/:content_id'});
});
});
});
App.IndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('menu',{outlet:'topMenu',controller:this.controllerFor('menu', App.Menu.find())});
}
});
App.MenuRoute = Ember.Route.extend({
model:function(){
return App.Menu.find();
}
});
App.Store = DS.Store.extend({
revision:12,
adapter:'DS.FixtureAdapter'
//adapter:DS.RESTAdapter.extend({
// url: 'http://....'
//})
});
App.Menu = DS.Model.extend({
label: DS.attr('string')
,subMenu: DS.hasMany('App.subMenu')
});
App.SubMenu = DS.Model.extend({
label: DS.attr('string')
,content :DS.attr('string')
});
App.SubMenu.FIXTURES = [
{
id:1
,label:"Sub1"
,content:'Hello there 1'
}
,{
id:2
,label:"Sub2"
,content:'Hello there 2'
},{
id:3
,label:"Sub3"
,content:'Hello there 3'
},{
id:4
,label:"Sub4"
,content:'Hello there 4'
}
];
App.Menu.FIXTURES = [
{
id:1,
label:"Home",
subMenu:[1,2]
},
{
id:2,
label:"Api",
subMenu:[3,4]
}
];`
I am not sure if my router is set up in-correctly. Any feedback would be highly appreciated.
Regards,
Dee
I'm guessing that IndexRoute is not being used by ember. Probably what you want instead is to use MenuIndex:
App.MenuIndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('menu',{outlet:'topMenu',controller:this.controllerFor('menu', App.Menu.find())});
}
});
Try turning on logging so that you can see which routes/controllers/views/templates are being used by ember:
var App = Ember.Application.create({
LOG_VIEW_LOOKUPS: true,
LOG_ACTIVE_GENERATION: true
});
http://blog.emberwatch.com/2013/06/13/logging-the-magic-in-ember-js.html

The bound view does not update upon saving when using ember-model RESTAdapter.

I am exploring using ember-model (https://github.com/ebryn/ember-model), as an alternative to ember-data in my ember.js application. I don't have enough rep yet to create a new tag, but if so, I'd create ember-model.js to avoid confusion.
The desired behavior is to display a simple list of records and display a form to create new records via a RESTful rails API, Using GET and POST.
This worked smoothly with the Fixture Adaptor inside ember-model. Upon creation, as expected, the new record is pushed onto the Fixture array and the browser automatically updates to reflect the new record.
Using the ReST Adaptor, I was able to display the list and create a new record. However, the newly created record, which is bound to a template, will not update unless I hit the refresh button on the browser. And now I wonder what I might be doing wrong.
I dug around StackOverflow and elsewhere before I posted. I'm going to dig into the source next, but I thought I'd ask to see if anyone else has any insights.
Thank you, good people of Stack Overflow.
This is my app.js:
window.Dash = Ember.Application.create({
LOG_TRANSITIONS: true
});
Dash.Router.map(function() {
// put your routes here
this.resource('apps', function() {
this.route('add');
this.resource('app', { path: ':app_id' });
});
});
Dash.IndexRoute = Ember.Route.extend({
//temporary redirect to games list
redirect: function () {
this.transitionTo('apps');
}
});
Dash.AppsRoute = Ember.Route.extend({
model: function() {
return Dash.App.find();
}
});
Dash.AppsAddController = Ember.Controller.extend({
save: function() {
var name = this.get('name');
var id = this.get('id');
var account_id = this.get('account_id');
var auth_id = this.get('auth_id');
var state = Dash.AppState.selectedState;
var store_type = Dash.AppPlatform.selectedPlatform;
var store_app_id = this.get('store_app_id');
var download_url = this.get('download_url');
var newApp = Dash.App.create({
name: name,
id: id,
auth_id: auth_id,
state: state,
store_type: store_type,
store_app_id: store_app_id,
download_url: download_url
});
newApp.save();
}
});
Dash.AppPlatform = Ember.ArrayController.create({
selectedPlatform: null,
content: [
Ember.Object.create({id: 'itunes', name: 'iOS'}),
Ember.Object.create({id: 'android', name: 'Google Play'}),
Ember.Object.create({id: 'amazon', name: 'Amazon Appstore'}),
]
});
Dash.AppState = Ember.ArrayController.create({
selectedState: null,
content: [
Ember.Object.create({name: 'test'}),
Ember.Object.create({name: 'production'})
]
})
Dash.App = Ember.Model.extend({
id: Ember.attr(),
name: Ember.attr(),
auth_id: Ember.attr(),
state: Ember.attr(),
store_type: Ember.attr(),
store_app_id: Ember.attr(),
download_url: Ember.attr(),
});
Dash.App.adapter = Ember.RESTAdapter.create();
//hardcoding account id for now
Dash.App.url = 'http://localhost:3000/accounts/980190962/apps';
Dash.App.collectionKey = 'apps';
This is index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dashboard</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h2>Dashboard</h2>
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="apps">
{{#linkTo 'apps.add' }}Add an App{{/linkTo}}
<h3>Apps List</h3>
{{#if model}}
<ol>
{{#each model}}
<li>{{#linkTo 'app' this}}{{name}}{{/linkTo}}</li>
{{/each}}
</ol>
<div>{{outlet}}</div>
{{else}}
<p>You have no Apps.</p>
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="apps/index">
<p>Click a game to Edit or click Add to enter a new App.</p>
</script>
<script type="text/x-handlebars" data-template-name="app">
<div>
Name: {{name}}<br />
ID: {{id}}<br />
Account ID: {{account_id}}<br />
AuthID : {{auth_id}}<br />
Test Mode?: {{state}}<br />
Store Type: {{store_type}}<br />
Store App ID: {{store_app_id}}<br />
Download URL: {{download_url}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="apps/add">
<p><label for="name">Name:</label><br />{{view Ember.TextField valueBinding='name'}}</p>
<p><label for="id">ID:</label><br />{{view Ember.TextField valueBinding='id'}}</p>
<p><label for="account_id">Account ID:</label><br />{{view Ember.TextField valueBinding='account_id'}}</p>
<p><label for="auth_id">Auth ID:</label><br />{{view Ember.TextField valueBinding='auth_id'}}</p>
<p><label for="state">Test Mode:</label><br />{{view Ember.Select contentBinding='Dash.AppState' valueBinding='Dash.AppState.selectedState' optionValuePath="content.name" optionLabelPath="content.name"}}</p>
<p><label for="store_type">Store Type:</label><br />{{view Ember.Select contentBinding='Dash.AppPlatform' valueBinding='Dash.AppPlatform.selectedPlatform' optionValuePath="content.id" optionLabelPath="content.name"}}</p>
<p><label for="store_app_id">Store App ID:</label><br />{{view Ember.TextField valueBinding='store_app_id'}}</p>
<p><label for="download_url">Download URL:</label><br />{{view Ember.TextField valueBinding='download_url'}}</p>
<p><button {{action 'save'}}>Save</button></p>
</script>
<script src="js/libs/jquery-1.9.1.js"></script>
<script src="js/libs/handlebars-1.0.0-rc.4.js"></script>
<script src="js/libs/ember-1.0.0-rc.6.js"></script>
<script src="js/libs/ember-model.js"></script>
<script src="js/app.js"></script>
</body>
</html>
It won't automatically work this way. Your content array in the AppState controller is just a static array with items in it. You'd have to do something like this
this.controllerFor('Apps').get('content').pushObject(newApp) to see it reflect in the UI.
Your Apps controller will be an ArrayController because the Apps route's model function returned an array.