使用 HTMX 實現 Django 頁面不刷新交互效果

更新日期: 2024 年 12 月 1 日

本文為 Django 與前端框架教學,第 2 篇

  1. 使用 Esbuild 實現 Django 中的靜態資源管理與優化
  2. 使用 HTMX 實現 Django 頁面不刷新交互效果 👈 所在位置
  3. 使用 HTMX 完成刪除功能的邏輯解析
  4. 使用 Tailwind CSS 和 Shared Template 設計 Django 項目導航欄與樣式

建議閱讀本文前,先閱讀完 Django 高階教學 系列文

在傳統 Django 開發中,每次操作如表單提交或數據更新,往往需要整頁刷新。

通過結合 HTMX,我們可以實現無刷新數據提交與更新的效果,大幅提升用戶體驗。

為什麼選擇 HTMX?

傳統方法的挑戰

  1. DOM 操作的複雜性:傳統的 JavaScript 需要手動操作 DOM 樹,代碼不易管理。
  2. 全頁刷新:每次提交數據後需重新加載整個頁面,影響用戶體驗。
  3. 代碼冗長:即便使用 Fetch API 或框架,代碼結構仍可能變得混亂。

HTMX 的優勢

  1. 輕量且簡單:只需引入一個腳本文件即可啟用。
  2. HTML 驅動:通過直接操作 HTML 標籤屬性,實現 AJAX 請求和 DOM 更新。
  3. 支持片段渲染:後端直接返回 HTML 模板片段,無需編寫複雜的 JSON 解析邏輯。

配置環境與初始化

使用 CDN 安裝 HTMX

在模板文件中加載 HTMX:

<script src="https://unpkg.com/htmx.org@2.0.3"></script>

使用 NPM 安裝 HTMX(可選)

如果需要使用模組化開發,可使用 NPM 安裝:

npm install htmx.org

然後在前端打包工具(如 Esbuild)的代碼中引入:

檔案位置:frontend/scripts/app.js
在這個檔案中,我們將導入需要的 HTMX 模組。

import "htmx.org";

調整 HTML 表單

檔案位置:resumes/templates/resumes/show.html

我們調整留言表單,使用 HTMX 的屬性進行動態更新。

原始代碼:

<section>
  <form action="{% url 'resumes:comments' resume.id %}" method="post">
    {% csrf_token %}
    <textarea name="content" id=""></textarea>
    <button>新增留言</button>
  </form>
</section>

<ul>
  {% for comment in comments %}
  <li>
    {{ comment.content|linebreaksbr }}
    <form action="{% url 'comments:delete' comment.id %}" method="post">
        {% csrf_token %}
        <button></button>
    </form>
    {{ comment.created_at }}
  </li>
  {% endfor %}
</ul>

調整代碼:

使用 HTMX 的 hx-swap 屬性靈活控制內容插入方式。

<form 
    hx-post="{% url 'resumes:comments' resume.id %}" 
    hx-target="#comment-list" 
    hx-swap="afterbegin"
>
  {% csrf_token %}
  <textarea name="content" id=""></textarea>
  <button>新增留言</button>
</form>
<ul id="comment-list">
  <!-- 最新留言會被插入到這裡的頂部 -->
</ul>

標籤詳解

  1. hx-post
    • 功能:設置向後端提交數據的 URL,實現表單的非同步提交。
    • 應用場景:例如,當用戶提交留言時,hx-post 可以將表單的數據直接傳送到後端的 API 端點,無需刷新頁面。
    • 代碼範例
<form hx-post="/comments/submit/" hx-target="#comment-list">
    <textarea name="content"></textarea>
    <button type="submit">新增留言</button>
</form>
  1. hx-target
    • 功能:指定後端返回的數據,要更新的 DOM 元素。
    • 應用場景:比如留言提交成功後,將新留言內容插入到特定的留言列表中。
    • 代碼範例
<form hx-post="/comments/submit/" hx-target="#comment-list">
  ...
</form>
  1. hx-swap
    • 功能:控制如何將後端返回的數據,插入到目標元素中。
    • 可選值
      • innerHTML(默認值):替換目標元素的內部 HTML。
      • beforeend:將新內容追加到目標元素的結尾處。
      • afterbegin:將新內容插入到目標元素的開頭處。
    • 代碼範例
<form hx-post="/comments/submit/" hx-target="#comment-list">
    ...
</form>

效果說明

  1. hx-swap="afterbegin" 的作用
    • 功能:當後端返回的數據到達時,將新內容插入到目標元素的頂部。
    • 應用場景:適用於需要按時間排序的留言列表,讓最新留言顯示在列表的第一個位置。
  2. 用戶體驗提升
    • 當用戶提交新留言後,無需刷新頁面,即可即時看到新內容出現在列表頂部。

    • 簡化了前後端的交互流程,避免了傳統方式中重新渲染整個頁面的開銷。

模板進階重構:使用變數傳遞數據

新增 HTML 模板片段

檔案位置:comments/templates/comments/_comment.html

建立一個只渲染單個留言的模板,讓 HTMX 插入的內容更高效。

代碼範例

<li>
  {{ comment.content|linebreaksbr }} <!-- 顯示留言內容,支持換行 -->
  <form action="{% url 'comments:delete' comment.id %}" method="post">
      {% csrf_token %} <!-- 防止 CSRF 攻擊 -->
      <button></button> <!-- 刪除按鈕 -->
  </form>
  {{ comment.created_at }} <!-- 顯示留言的創建時間 -->
</li>

重點解析

  1. 模板片段的作用
    • 單個留言渲染:只渲染新增的留言部分,避免重新渲染整個列表,提升性能。
    • 高效更新:模板片段直接返回,前端只需插入到指定位置(例如列表頂部)。
  2. 返回方式的優勢
    • 減少數據傳輸:返回的 HTML 僅包含新增的留言部分,而非整個頁面。
    • 高效頁面渲染:前端只需插入新增的 HTML,而不需重新載入整個頁面。
    • 即時交互:配合 HTMX 的屬性(如 hx-swap),實現無刷新動態更新。

修改模板

檔案位置:resumes/templates/resumes/show.html

使用 Django 的 include 標籤,重構模板邏輯,將數據與模板片段分離,便於動態更新。

<ul id="comment-list">
    {% for comment in comments %}
        {% include "comments/_comment.html" with comment=comment %}
    {% endfor %}
</ul>

修改視圖函數

檔案位置:comments/views.py

在後端的視圖中,我們使用 Django 的模板引擎,返回 HTML 片段而非整頁重定向。

具體步驟

  1. 匯入必要模組
    • get_object_or_404:快速獲取目標物件,若不存在直接返回 404 錯誤。
    • render:用於渲染模板並返回結果。
    • HttpResponse:可用於返回非模板內容(例如純文本或 JSON)。
    • 模型 Comment 和關聯的 Resume
  2. 新增視圖函數 add_comment
    此函數處理新增留言的提交邏輯,並返回渲染後的 HTML 片段。
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse
from .models import Comment
from resumes.models import Resume

def add_comment(request, id):
    if request.method == "POST":  # 確保只處理 POST 請求
        # 根據 id 獲取對應的 Resume,如果不存在返回 404
        resume = get_object_or_404(Resume, id=id)

        # 獲取用戶提交的留言內容
        content = request.POST.get("content")

        # 創建並保存新的 Comment 物件
        comment = resume.comment_set.create(content=content)

        # 返回渲染後的 HTML 模板片段
        return render(request, "comments/_comment.html", {"comment": comment})

執行邏輯

  1. 確保請求為 POST 方法,避免誤操作。
  2. 根據 Resumeid 找到對應的簡歷物件。
  3. 從請求的 POST 數據中提取 content,作為新留言的內容。
  4. 通過 comment_set.create 方法創建並保存留言,保證數據關聯性。
  5. 使用 render 返回渲染好的 HTML 模板片段,供前端直接插入頁面。

模板中 with 語法的重點解析

  1. with comment=comment 的作用
    • 功能:將父模板中的變數傳遞給子模板,便於在子模板中使用局部變數進行渲染。
    • 語法解釋
      • 父模板中的(等號後面) comment:來自數據庫查詢結果,通常在視圖函數中傳遞給模板。
      • 子模板中的(等號前面) comment:用於渲染單個留言的局部變數,通過 with 語法傳入。
  2. 代碼範例父模板中的代碼
<ul>
  {% for comment in comments %}
    {% include "comments/_comment.html" with comment=comment %}
  {% endfor %}
</ul>
  1. 子模板 comments/_comment.html 中的代碼
<li>
  {{ comment.content|linebreaksbr }}
  <form hx-target="closest li" hx-swap="outerHTML" hx-post="{% url 'comments:delete' comment.id %}">
      {% csrf_token %}
      <button></button>
  </form>
  {{ comment.created_at }}
</li>
  1. 可讀性與重用性提升
    • 通過將留言項目提取到子模板 _comment.html,可以更高效地進行模板的重用和維護。
    • 使用 with 語法進行變數傳遞,避免直接操作全局變數,提高了代碼的清晰度和模塊化程度。

目錄結構

以下是上述實現動態留言功能相關的目錄結構,方便新手快速對應:

mysite/
├── manage.py                     # Django 專案入口
├── mysite/                       # 專案設定目錄
   ├── settings.py               # 專案配置檔案
   ├── urls.py                   # 主路由設置
   ├── wsgi.py                   # WSGI 應用入口
   └── asgi.py                   # ASGI 應用入口

├── resumes/                      # 簡歷應用
   ├── models.py                 # 定義 Resume 模型
   ├── views.py                  # 定義簡歷視圖邏輯
   ├── urls.py                   # 簡歷路由設置
   ├── templates/                # 簡歷相關模板
      └── resumes/
          └── show.html         # 顯示簡歷詳細與留言區
   └── __init__.py               # 模塊初始化文件

├── comments/                     # 留言應用
   ├── models.py                 # 定義 Comment 模型
   ├── views.py                  # 定義留言處理邏輯
   ├── urls.py                   # 留言路由設置
   ├── templates/                # 留言相關模板
      └── comments/
          └── _comment.html     # 單個留言模板片段
   └── __init__.py               # 模塊初始化文件

├── static/                       # 靜態文件目錄
   ├── scripts/
      └── app.js                # 前端 JS 文件
   └── styles/
       └── app.css               # 前端樣式文件

└── templates/                    # 全局模板
    └── shared/
        └── layout.html           # 主模板,包含 JS 和 CSS 引用

結構說明

  • mysite/:專案根目錄,包含 Django 的主要設定檔案。
  • resumes/:簡歷相關功能,包含簡歷模型、視圖、路由及模板,show.html 中呈現留言區和新增留言的表單。
  • comments/:留言相關功能,處理留言的模型、視圖、模板,以及單個留言的模板片段 _comment.html
  • static/:靜態文件目錄,存放前端的 JS 和 CSS 文件,供模板引用。
  • templates/shared/:全局模板目錄,存放公共布局文件如 layout.html

這樣的結構使專案清晰、模塊化,方便擴展與維護。


小結

通過結合 Django 和 HTMX,我們實現了無刷新留言功能,並解決了傳統頁面刷新帶來的用戶體驗問題。其核心步驟包括:

  1. HTML 驅動的交互:簡化 JavaScript 邏輯。
  2. 模板片段渲染:後端直接返回局部 HTML,提高效率。
  3. 進階功能擴展:靈活使用 HTMX 的功能(如 swap)進一步提升頁面效果。

這種設計模式特別適合希望快速構建高效頁面的 Django 開發者!

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *