Reputation: 799
I'm trying to build a todo list with multiple lists. Everything works perfectly fine. The issue comes when I want to use JQuery UI Sortable. I'm adding the snippet in my TaskListView in the render function, but I had to wrap it into a setTimeout function so that it waits until the DOM is loaded.
I'm pretty sure there is a better way to do this, but I just couldn't find it yet. Help is appreciated.
var TaskView = Backbone.View.extend({
initialize: function(task) {
this.task = task;
},
render: function() {
var completedClass = "completedTask";
if (this.task.completed == 100) completedClass = "pendingTask";
var html = ""
+ "<li class='task-<%= task.get('id') %> ' data-id='<%= task.get('id') %>' sortable='true'>"
+ "<div class='float-left'><i class='icon-trash removeTask' data-id='<%= task.get('id') %>'></i></div>"
+ "<div class='float-left'>"
+ "<span class='editTask ' data-type='title' data-id='<%= task.get('id') %>'><%= task.get('title') %></span> "
+ "</div>"
+ "<div class='float-right'>"
+ "<span class='editTask task-responsible label' data-type='responsible' data-id='<%= task.get('id') %>'><%= task.get('responsible') %></span>"
+ "<span class='editTask task-deadline label' data-type='ended' data-id='<%= task.get('id') %>'><%= task.get('ended') %></span>"
+ "<span class='label criticallyLevelToggle editTask <%= criticallyLevelClass %>' data-type='completed' data-id='<%= task.get('id') %>'><%= parseInt(task.get('completed')) %>%</span>"
+ "</div>"
+ "<div class='clear'> </div>"
+"</li>";
var template = _.template( html, {task: this.task, criticallyLevelClass:this.task.getCriticallyLevelClass(), completed: completedClass});
this.$el.html(template);
return this;
}
});
var TaskListView = Backbone.View.extend({
initialize: function(tasks) {
this.tasks = tasks;
},
render: function() {
var html = ""
+"<ul class='tasks'>"
+"<%= tasksli %>"
+"<li><div class='float-left'><input type='text' placeholder='New task...' class='new-task' name='new-task'/></div><div class='clear'> </div></li>"
+"</ul>";
+ "";
var tasksRendered = "";
if (_.isArray(this.tasks)) {
for (var i = 0; i < this.tasks.length; i++) {
var tView = new TaskView(new Task(this.tasks[i]));
tasksRendered += tView.render().el.innerHTML;
};
}
var template = _.template(html, { tasksli: tasksRendered});
this.$el.html(template);
setTimeout(function(){this.$('.tasks').sortable({
stop: function(e, ui) {
ui.item.trigger('drop', ui.item.index());
}
});}, 500);
return this;
}
});
Upvotes: 4
Views: 7978
Reputation: 239311
Instead of setTimeout
, just use $(function () { })
(or $(document).ready
) as you would normally to wait for the DOM to be ready. There is nothing special about these functions, and you can add new callbacks to them at any point. All you have to worry about is maintaining the meaning of this
in your callback.
render: function() {
var html = ""
+"<ul class='tasks'>"
+"<%= tasksli %>"
+"<li><div class='float-left'><input type='text' placeholder='New task...' class='new-task' name='new-task'/></div><div class='clear'> </div></li>"
+"</ul>";
+ "";
var tasksRendered = "";
if (_.isArray(this.tasks)) {
for (var i = 0; i < this.tasks.length; i++) {
var tView = new TaskView(new Task(this.tasks[i]));
tasksRendered += tView.render().el.innerHTML;
};
}
var template = _.template(html, { tasksli: tasksRendered});
this.$el.html(template);
var self = this;
$(function(){
self.$('.tasks').sortable({
stop: function(e, ui) {
ui.item.trigger('drop', ui.item.index());
}
})
return this;
}
Alternatively, (and probably more correctly) take steps to make sure that your backbone view isn't having its render
method called until the DOM is ready.
Upvotes: 5