Reputation: 23
I've got a problem with my Backbone.js app (Fiddle: http://jsfiddle.net/the_archer/bew7x010/3/). The app is list (ol element) with nested lists within. Here's my starting HTML:
<body>
<ol id="flowList"></ol>
</body>
<script type="text/template" id="item-template">
<%= content %>
</script>
When the list is empty. I add a new <li>
element within it using Backbone.js and focus on the li
.
When within the li
, if I press the enter key, I want to insert a new li
just after the li
I pressed enter within.
If I press tab, I want to add a new sub ol
element within the list element. I handle the keypress within the li
elements like so:
handleKeyboardShortcuts: function(e){
if (e.keyCode == 13 && !e.shiftKey){
e.preventDefault();
this.el = $(e.target).parent();
Items.create({contnet: "New Item!"});
}
}
I have a listenTo
on my collection, which on add, appends a li
to the #flowList ol
element using the addOne
function:
addOne: function(todo) {
var view = new ItemView({model: todo});
$(this.el).append(view.render().el);
}
My problem is:
addOne
function?addOne
function, which instead of doing $(this.el).append
does $(this.el).after
?For some reason, I can't get my head around how to pass around those details. Here's the full Backbone.js code:
$(function() {
var Item = Backbone.Model.extend({
defaults: function() {
return {
content: "empty item..."
};
}
});
var ItemList = Backbone.Collection.extend({
model: Item,
localStorage: new Backbone.LocalStorage("todos-backbone"),
});
var Items = new ItemList;
var ItemView = Backbone.View.extend({
tagName: "li",
template: _.template($('#item-template').html()),
events: {
"click": "enableEdit",
"blur": "disableEdit",
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
enableEdit: function(){
this.$el.attr("contenteditable","true").focus();
},
disableEdit: function(){
this.$el.attr("contenteditable","false");
}
});
var AppView = Backbone.View.extend({
el: $("#flowList"),
events: {
"keydown li": "handleKeyboardShortcuts"
},
initialize: function() {
this.listenTo(Items, 'add', this.addOne);
this.listenTo(Items, 'reset', this.addAll);
this.listenTo(Items, 'all', this.render);
Items.fetch();
if (Items.length === 0){
Items.create({content: "Sample Item!"});
}
},
render: function(e) {
console.log(e);
},
addOne: function(todo) {
var view = new ItemView({model: todo});
$(this.el).append(view.render().el);
},
addAll: function() {
Items.each(this.addOne, this);
},
handleKeyboardShortcuts: function(e){
if (e.keyCode == 13 && !e.shiftKey){
e.preventDefault();
this.el = $(e.target).parent();
Items.create({contnet: "New Item!"});
}
}
});
var App = new AppView;
});
Here's a link to the fiddle: http://jsfiddle.net/the_archer/bew7x010/3/
Upvotes: 2
Views: 937
Reputation: 2649
You could keep track of the selected item, enabling you to insert the new one after or within it.
In your existing enableEdit handler, trigger an 'item:select' event:
var ItemView = Backbone.View.extend({
enableEdit: function(){
this.trigger('item:select', this);
this.$el.attr("contenteditable","true").focus();
}
});
Then in your AppView, when you add a new item, attach a listener for the new item that updates the value of a 'selectedItem' property.
Then you can insert the new item into the dom based on the state of that property:
var AppView = Backbone.View.extend({
addOne: function(todo) {
var view = new ItemView({model: todo});
this.listenTo( view, 'item:select', this.handleItemSelect );
if(this.selectedItem) {
$(this.selectedItem.el).after(view.render().el);
}
else {
$(this.el).append(view.render().el);
}
},
handleItemSelect: function(item) {
console.log('item selected:', item);
this.selectedItem = item;
}
});
You should be able to do something similar for the tab key behaviour, by setting a flag when the tab key is pressed, before calling Items.create.
Upvotes: 1