I am trying to implement a Kanban application (To-Do application) in Flask and HTML.
I can add tasks, they are displayed in the respective column (To-Do, In Progress, Done) and I can change the status of the tasks, which moves them in the respective column.
Now I am trying to make the tasks draggable and change their status according to the respective column they are dropped in, that’s when I had to start using JavaScript to implement the event handlers for the Drag and Drop API of HTML.
In the Jinja Template of the application I have my event listeners defined for the Drag and Drop API ({#...#} are Jinja comments):
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function dragEnter(ev) {
ev.preventDefault();
$(this).addClass('zoomed');
}
function drag(ev) {
ev.dataTransfer.setData("task_id", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var task_id = ev.dataTransfer.getData("task_id");
var targe_state = ev.target.id;
{{ dropped(project_id=project.id, task_id=task_id, target_state=target_state) }}
}
</script>
The actual logic happens in the call to dropped which is a python function passed to the Jinja template:
def dropped(project_id, task_id, target_state):
project_id = int(project_id)
task_id = int(task_id)
if Project.query.get(project_id) is None:
return render_template("404.html"), 404
try:
session = sessions_by_project[project_id]
except KeyError:
return render_template("404.html"), 404
task = session.query(Task).get(int(task_id))
if task is None:
return render_template("404.html"), 404
task.change_status(str2status(target_state))
session.commit()
print(f"Dropped called with target ID: {task_id}, state: {target_state}")
return redirect(url_for("main.kanban", project_id=project_id))
@main.route("/<int:project_id>/kanban", methods=["GET", "POST"])
def kanban(project_id):
# ...more code ommited...
return render_template(
"kanban.html", tasks_by_status=tasks_by_status, project=project, dropped=dropped
)
The part of the Jinja template were the event handlers are set is (there are two more columns for “In Progress” and “Done” which are basically the same so I omitted them:
<ol class="kanban To-do" id="todo" ondrop="drop(event)" ondragover="allowDrop(event)" ondragenter="dragEnter(event)">
<h2> To-Do </h2>
{% for (depth, pending) in tasks_by_status[1]%}
<li class="dd-item indent{{depth}}" id="{{pending.id}}" draggable="true" ondragstart="drag(event)">
<h3 class="title dd-handle"><button name="task" value={{pending.id}}>{{pending.title}}</button></h3>
<div class="text" contenteditable="true">{{pending.description}}</div>
</li>
{% endfor%}
</ol>
So from what I see during testing and trying around:
- I can drag the list-items marked with
draggable=true
- Nothing happens when I move the draggable items over the ordered list elements, although
drageEnter should be called and make the element appear slightly bigger
- Also nothing happens when I drop something over the ordered list elements
- When I use hard coded values for calling the
dropped(1,1,"pending") I can see the function print to the console whenever the page is loaded but not when the drop event is fired.
I also tried not to pass the dropped function to the template but rather add it to the flask context processor which also didn’t help.
Hope anyone knows what’s going on and where I made a mistake – I hope it is not too silly. Much appreciated, thanks!