Ember Data + jQuery UI Sortable Problems
by Kerrick
HTML
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.1.2/handlebars.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.2.2/ember.js"></script>
<script src="http://builds.emberjs.com/beta/ember-data.js"></script>
<script type="text/x-handlebars" data-template-name="application">
<h1>Ember Data + jQueryUI Sortable Problems</h1>
{{outlet}}
Try sorting and then deleting something. The model is deleted properly, but the DOM does not reflect that. Sometimes it stays in the DOM, sometimes the wrong one is deleted, and sometimes both the right and a wrong one is deleted.
</script>
<script type="text/x-handlebars" data-template-name="index">
{{render 'formFields' formFields}}
</script>
<script type="text/x-handlebars" data-template-name="formFields">
{{#each arrangedContent}}
{{render 'formField' this}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="formField">
<div class="form-field" {{bind-attr data-id=id}}>
<span class="delete" {{action 'delete'}}>X</span>
{{name}} ({{displayOrder}})
<span class="handle">#</span>
</div>
</script>
CSS
body { padding: 2em; }
.form-field {
border:1px solid #ccc;
margin:2px;
padding:2px;
background:white;
overflow: hidden;
}
.ui-sortable-helper {
-webkit-transform: scale(1.03);
-moz-transform: scale(1.03);
transform: scale(1.03);
box-shadow: 1px 3px 5px rgba(0, 0, 0, 0.4);
}
.handle {
cursor: move;
font-size: 2em;
float: right;
margin-right: 0.5em;
}
.delete {
cursor: pointer;
color: red;
}
JavaScript
App = Ember.Application.create({});
App.ApplicationAdapter = DS.FixtureAdapter.extend();
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.find('formField');
}
});
App.IndexController = Ember.ObjectController.extend({
formFields: Ember.computed.alias('content')
});
App.FormFieldsController = Ember.ArrayController.extend({
sortProperties: ['displayOrder'], // Force sort by displayOrder, not ID
updatePositions : function(positionData){
this.propertyWillChange('content'); // Manually notify Ember
this.get('content').forEach(function(formField) {
var key = formField.get('id');
formField.set('displayOrder', positionData[key] + 1);
});
this.propertyDidChange('content'); // Manually notify Ember
},
actions: {
compactDisplayOrder: function() {
window.console.log('compactDisplayOrder');
this.get('content').sortBy('displayOrder').forEach(function(formField, i) {
formField.set('displayOrder', i + 1);
});
}
}
});
App.FormFieldsView = Ember.View.extend({
handleSort: function(event,ui){
var positionData = {};
this.$(".form-field").each(function(index, element){
// Get model ID from bind-attr in template
var key = $(element).data('id');
positionData[key] = index;
});
this.get('controller').updatePositions(positionData);
// Delay recreating the sortable
Ember.run.next(function(){ this.makeSortable(); }.bind(this));
},
makeSortable: Ember.on('didInsertElement', function(){
try {
this.$().sortable("destroy");
} catch(err){
window.console.warn('No sortable to destroy', err);
}
finally {
this.$().sortable({
handle: '.handle',
axis: 'y',
update:...