javascript 通过拖放更改数据库

qojgxg4l  于 5个月前  发布在  Java
关注(0)|答案(1)|浏览(64)

我正在尝试实现看板应用程序(待办事项应用程序)在Flask和HTML。我可以添加任务,它们显示在各自的列(To-Do,In Progress,Done),我可以改变任务的状态,这会将它们移动到相应的列中。现在,我正在尝试使任务可拖动,并根据它们被放入的相应列来改变它们的状态,那时我不得不开始使用JavaScript来实现HTML的拖放API的事件处理程序。
在应用程序的Jinja模板中,我为拖放API定义了事件侦听器:

<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>

字符串
实际的逻辑发生在对dropped的调用中,dropped是传递给Jinja模板的Python函数:

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
    )


Jinja模板中设置事件处理程序的部分是(还有两个基本相同的“In Progress”和“Done”有序列表,所以我省略了它们):

<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>


所以从我在测试和尝试过程中看到的:

  • 我可以拖动标有draggable=true的列表项
  • 当我将可拖动项移动到有序列表元素上时,什么也没有发生,尽管应该调用drageEnter并使元素看起来稍微大一点
  • 同样,当我在有序列表元素上放置一些东西时,
  • 当我使用硬编码的值来调用dropped(1,1,"pending")时,我可以看到函数在页面加载时打印到控制台,而不是在触发drop事件时打印。
  • 经过更多的调查,似乎 dropzone 事件处理程序(ondragenter,ondragnet,ondragleave,ondrop)在应该触发事件时没有被调用,而ondragstartondragend事件处理程序确实工作=>难道有序列表不能用作 * dropzone *?

我也试着不把dropped函数传递给模板,而是把它添加到flask上下文处理器,这也没有帮助。希望有人知道发生了什么,我在哪里犯了错误-我希望这不是太傻。非常感谢,谢谢!

jexiocij

jexiocij1#

就像Qori说的,每当页面加载时,传递给jinja模板的函数就会被调用,结果会被放入模板中以生成显示的HTML页面。因此,为了在后端站点上使用正确的参数调用函数,我需要通过fetch API发送数据.:

const kanbanUrl = "{{url_for('main.kanban', project_id=project.id)}}";
  async function drop(ev, el) {
    ev.preventDefault();
    
    var taskId = ev.dataTransfer.getData("task_id");
    var targetState = el.id;

    document.getElementById("main").style.opacity = "1.0";

    const response = await fetch(kanbanUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      redirect: "follow",
      body: JSON.stringify({task_id: taskId, target_state: targetState}),
    }).then(() => {window.location.reload();});
    return response.json();
  }

字符串
然后处理它:

@main.route("/<int:project_id>/kanban", methods=["GET", "POST"])
def kanban(project_id):
    project = Project.query.get(project_id)
    if project is None:
        return render_template("404.html"), 404
    try:
        session = sessions_by_project[project_id]
    except KeyError:
        return render_template("404.html"), 404

    tasks_by_status = defaultdict(list)
    for depth, task in walk_list(session.query(Task).all()):
        tasks_by_status[task.status.value].append((depth, task))

    if request.method == "POST" and "task" in request.form:
        return redirect(
            url_for("main.task", project_id=project_id, id=int(request.form["task"]))
        )

    if (
        request.method == "POST"
        and "task_id" in request.json
        and "target_state" in request.json
    ):
        task_id = int(request.json["task_id"])
        target_state = request.json["target_state"]
        
        print(f"Changig state of task: {task_id} to state: {target_state}")
        task = session.query(Task).get(task_id)
        if task is None:
            return render_template("404.html"), 404
        task.change_status(str2status(target_state))
        session.commit()
        
        next = request.args.get('next')
        if next is None or not next.startswith('/'):
            next = url_for('main.kanban', project_id=project_id)
        return redirect(next)

    return render_template(
        "kanban.html", tasks_by_status=tasks_by_status, project=project
    )


为了查看数据库的更改,必须重新加载页面,这发生在通过then传递给fetch API的回调中。
此外,为了在正确的元素上执行回调,在我的例子中是有序列表元素,而不是任何子元素,我需要通过以下方式将元素传递给回调:

<ol class="kanban To-do" id="todo" ondrop="drop(event, this)" ondragover="dragOver(event, this)" ondragenter="dragEnter(event, this)" ondragleave="dragLeave(event, this)">
        <h2> To-Do </h2>
        {% for (depth, pending) in tasks_by_status[1]%}
            <li class="dd-item indent{{depth}}" id="{{pending.id}}" draggable="true" ondragstart="dragStart(event)" ondragend="dragEnd(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>


另一个问题是,当鼠标悬停在子元素上时,ondragleaveondragenter也会被调用。我遵循了here的建议,并为ondragenterondragleave回调函数植入了一个计数器:

var counter = 0;

  function dragEnter(ev, el) {
    ev.preventDefault();
    ev.stopPropagation();
    counter++;
    el.style.opacity = "1.0";
    el.style.zoom = "1.1";
  }

  function dragLeave(ev, el) {
    ev.preventDefault();
    ev.stopPropagation();
    counter--;
    if(counter == 0)
    {
      el.style.opacity = "0.5";
      el.style.zoom = "1.0";
    }
  }


现在一切都按照预期工作了:我可以将list items拖到其中一个ordered lists上,并获得所需的突出显示效果,当悬停子元素时不会 Flink 。此外,正确的ID(task_idtarget_state)通过fetch API发送到后端,对数据库进行更改,页面重新加载,我可以看到更改。

相关问题