<div class="search-container">
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
hx-indicator=".htmx-indicator"
placeholder="Cari..."
/>
<div class="htmx-indicator">Mencari...</div>
</div>
<div id="search-results"></div>
@app.route('/search', methods=['POST'])
def search():
query = request.form.get('search')
results = perform_search(query) # Fungsi untuk melakukan pencarian di database atau sumber data lainnya
if not results:
return '<p>Tidak ada hasil yang ditemukan.</p>'
return render_template('search_results.html', results=results)
Penjelasan:
- Kita menambahkan elemen
<div>
dengan kelas “search-container” untuk membungkus input pencarian dan indikator loading. - Atribut
hx-indicator=".htmx-indicator"
digunakan untuk menampilkan indikator loading saat permintaan pencarian sedang berlangsung. - Pada sisi server, endpoint
/search
menerima kueri pencarian dari permintaan POST. - Fungsi
perform_search()
digunakan untuk melakukan pencarian berdasarkan kueri yang diterima. Fungsi ini dapat melibatkan logika pencarian yang kompleks, seperti pencarian teks penuh, pencarian berbasis kategori, atau pengurutan hasil berdasarkan relevansi. - Jika tidak ada hasil yang ditemukan, kita mengembalikan pesan “Tidak ada hasil yang ditemukan” dalam format HTML.
- Jika ada hasil pencarian, kita merender template
search_results.html
dengan meneruskan hasil pencarian ke template tersebut.
Template search_results.html
dapat berisi logika untuk menampilkan hasil pencarian dengan format yang lebih kaya, seperti menampilkan gambar, deskripsi, atau informasi tambahan lainnya.
14.2 Membangun Aplikasi Daftar Tugas (To-Do List) dengan HTMX
<div id="todo-app">
<form hx-post="/add-task">
<input type="text" name="task" placeholder="Tugas baru..." required />
<button type="submit">Tambah</button>
</form>
<div id="task-list">
{% for task in tasks %}
<div class="task" id="task-{{ task.id }}">
<input
type="checkbox"
hx-put="/toggle-task/{{ task.id }}"
{%
if
task.completed
%}checked{%
endif
%}
/>
<span
class="task-name"
{%
if
task.completed
%}style="text-decoration: line-through;"
{%
endif
%}
>{{ task.name }}</span
>
<button
hx-delete="/delete-task/{{ task.id }}"
hx-target="#task-{{ task.id }}"
>
Hapus
</button>
</div>
{% endfor %}
</div>
</div>
@app.route('/add-task', methods=['POST'])
def add_task():
name = request.form.get('task')
new_task = Task(name=name, completed=False)
db.session.add(new_task)
db.session.commit()
return render_template('task_list.html', tasks=Task.query.all())
@app.route('/toggle-task/<int:task_id>', methods=['PUT'])
def toggle_task(task_id):
task = Task.query.get_or_404(task_id)
task.completed = not task.completed
db.session.commit()
return ''
@app.route('/delete-task/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
task = Task.query.get_or_404(task_id)
db.session.delete(task)
db.session.commit()
return ''
Penjelasan:
- Aplikasi daftar tugas ditampilkan dalam elemen
<div>
dengan id “todo-app”. - Formulir untuk menambahkan tugas baru menggunakan atribut
hx-post="/add-task"
untuk mengirimkan permintaan POST saat formulir dikirim. - Daftar tugas ditampilkan menggunakan perulangan di dalam elemen
<div>
dengan id “task-list”. - Setiap tugas memiliki checkbox untuk menandai tugas sebagai selesai atau belum selesai. Atribut
hx-put="/toggle-task/{{ task.id }}"
digunakan untuk mengirimkan permintaan PUT saat checkbox diklik. - Nama tugas ditampilkan dengan tampilan yang berbeda tergantung pada status tugas (selesai atau belum selesai) menggunakan kondisional dalam templat.
- Tombol “Hapus” menggunakan atribut
hx-delete="/delete-task/{{ task.id }}"
untuk mengirimkan permintaan DELETE saat tombol diklik. Atributhx-target="#task-{{ task.id }}"
digunakan untuk menghapus elemen tugas yang sesuai setelah penghapusan berhasil. - Pada sisi server, endpoint
/add-task
menerima data tugas baru dari permintaan POST, membuat instanceTask
baru, dan menyimpannya ke database. Kemudian, daftar tugas yang diperbarui dikirimkan kembali dalam respons. - Endpoint
/toggle-task/<int:task_id>
menerima ID tugas dan memperbarui status tugas (selesai atau belum selesai) di database. - Endpoint
/delete-task/<int:task_id>
menerima ID tugas dan menghapus tugas yang sesuai dari database.
Dengan pendekatan ini, aplikasi daftar tugas menjadi lebih interaktif dan responsif. Pengguna dapat menambahkan tugas baru, menandai tugas sebagai selesai, dan menghapus tugas tanpa memuat ulang seluruh halaman.
14.3 Mengimplementasikan Fitur Endless Scrolling dengan HTMX
<div
id="post-list"
hx-get="/load-posts"
hx-trigger="revealed"
hx-swap="afterend"
hx-target="#post-list"
>
{% for post in posts %}
<div class="post">
<h3>{{ post.title }}</h3>
<p>{{ post.content }}</p>
<img src="{{ post.image_url }}" alt="{{ post.title }}" />
<a href="/posts/{{ post.id }}">Baca Selengkapnya</a>
</div>
{% endfor %}
</div>
@app.route('/load-posts')
def load_posts():
offset = int(request.args.get('offset', 0))
limit = 10
posts = Post.query.order_by(Post.id.desc()).offset(offset).limit(limit).all()
if not posts:
return '', 404
return render_template('post_list.html', posts=posts)
Penjelasan:
- Daftar posting awal ditampilkan dalam elemen
<div>
dengan id “post-list”. - Atribut
hx-get="/load-posts"
digunakan untuk mengirimkan permintaan GET ke endpoint/load-posts
saat pemicurevealed
terpenuhi. - Atribut
hx-swap="afterend"
menentukan bahwa respons akan disisipkan setelah elemen#post-list
. - Atribut
hx-target="#post-list"
digunakan untuk memastikan bahwa respons akan disisipkan ke dalam elemen#post-list
. - Setiap posting dalam daftar ditampilkan dengan informasi seperti judul, konten, gambar, dan tautan untuk membaca selengkapnya.
- Pada sisi server, endpoint
/load-posts
menerima parameteroffset
dari permintaan GET untuk menentukan offset saat mengambil posting berikutnya dari database. - Posting diambil dari database dengan menggunakan query yang sesuai, termasuk pengurutan berdasarkan ID posting secara menurun (posting terbaru di atas) dan pembatasan jumlah posting yang diambil (
limit
). - Jika tidak ada posting yang ditemukan (mencapai akhir daftar), endpoint mengembalikan respons dengan kode status 404.
- Jika ada posting yang ditemukan, template
post_list.html
di-render dengan posting yang diambil dan dikembalikan sebagai respons.
Dengan menggunakan atribut hx-trigger="revealed"
, permintaan untuk memuat posting berikutnya akan dikirimkan secara otomatis saat pengguna mencapai bagian bawah daftar posting. Respons yang diterima akan disisipkan setelah daftar posting yang ada, memberikan efek endless scrolling yang mulus.
14.4 Membuat Fitur Autocomplete dengan HTMX
<div class="autocomplete">
<input
type="text"
name="search"
hx-post="/autocomplete"
hx-trigger="keyup changed delay:300ms"
hx-target="#suggestions"
placeholder="Cari..."
/>
<div id="suggestions"></div>
</div>
@app.route('/autocomplete', methods=['POST'])
def autocomplete():
query = request.form.get('search')
suggestions = search_suggestions(query) # Fungsi untuk mencari saran berdasarkan input pengguna
if not suggestions:
return '<p>Tidak ada saran.</p>'
return render_template('suggestions.html', suggestions=suggestions)
Template suggestions.html
:
<ul>
{% for suggestion in suggestions %}
<li hx-get="/load-data/{{ suggestion.id }}">{{ suggestion.name }}</li>
{% endfor %}
</ul>
Penjelasan:
- Elemen
<div>
dengan kelas “autocomplete” digunakan untuk membungkus input pencarian dan daftar saran. - Atribut
hx-post="/autocomplete"
digunakan untuk mengirimkan permintaan POST ke endpoint/autocomplete
saat pemicukeyup
danchanged
terpenuhi, dengan jeda 300ms. - Atribut
hx-target="#suggestions"
menentukan bahwa respons akan ditampilkan dalam elemen dengan id “suggestions”. - Pada sisi server, endpoint
/autocomplete
menerima input pengguna dari permintaan POST. - Fungsi
search_suggestions()
digunakan untuk mencari saran berdasarkan input pengguna. Fungsi ini dapat melibatkan logika pencarian yang kompleks, seperti pencarian berbasis prefiks, pencarian berbasis similaritas, atau pengambilan saran dari sumber data eksternal. - Jika tidak ada saran yang ditemukan, endpoint mengembalikan pesan “Tidak ada saran” dalam format HTML.
- Jika ada saran yang ditemukan, template
suggestions.html
di-render dengan saran yang ditemukan dan dikembalikan sebagai respons. - Dalam template
suggestions.html
, setiap saran ditampilkan sebagai elemen<li>
dengan atributhx-get
yang mengarah ke endpoint/load-data/{{ suggestion.id }}
. Ini memungkinkan pemuatan data tambahan terkait saran saat pengguna mengklik saran tertentu.
Dengan pendekatan ini, fitur autocomplete menjadi lebih kaya dan interaktif. Saat pengguna mengetikkan input, saran yang relevan akan ditampilkan secara real-time. Pengguna dapat mengklik saran untuk memuat data tambahan terkait saran tersebut tanpa memuat ulang seluruh halaman.