Membuat Infinite Scroll dengan Django

Bagus Aji Santoso 6 Februari 2018

Membuat Infinite Scroll dengan Django

Diterjemahkan dari How to Create Infinite Scroll With Django karya Vitor Freitas

Level menengah/intermediate. Pembaca diasumsikan sudah pernah membuat aplikasi web dengan Django.

Ditutorial ini penulis akan menunjukkan bagaimana membuat infinite scrolling sederhana dengan Django. Kita akan memanfaatkan API pagination Django dengan sebuah plug-in jQuery. Akan ada contoh menggunakan function-based views dan class-based views.

Membuat REST API Sederhana Dengan Django

Dependensi

Kita tidak akan membutuhkan pustaka tambahan untuk back-end Django. Dari sisi frontend kita akan membutuhkan jQuery dan Waypoints.

Setelah mengunduh dependensi tersebut, tambahkan skrip berikut ke dalam template:

base.html

<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
<script src="{% static 'js/jquery.waypoints.min.js' %}"></script>
<script src="{% static 'js/infinite.min.js' %}"></script>

Contoh Sederhana

Contoh ini menggunakan function-based view. Di sini penulis hanya melakukan menampilkan data kumpulan angka (sebagai ganti data artikel, berita, gambar, dll.) yang di generate secara otomatis.

urls.py

from django.conf.urls import url
from mysite.blog import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
]

views.py

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

def home(request):
    numbers_list = range(1, 1000)
    page = request.GET.get('page', 1)
    paginator = Paginator(numbers_list, 20)
    try:
        numbers = paginator.page(page)
    except PageNotAnInteger:
        numbers = paginator.page(1)
    except EmptyPage:
        numbers = paginator.page(paginator.num_pages)
    return render(request, 'blog/home.html', {'numbers': numbers})

Di dalam view ini, numbers_list akan berisi list berupa angka dari 1 sampai 1000, yang kemudian dibagi ke dalam blok dengan 20 anggota setiap bloknya. Selanjutnya kita melakukan proses paginating dan mengirim objek numbers ke template, dimana setiap objek itu akan terdiri dari 20 blok angka (1-20, 21-40, 41-60, dst.).

Untuk pembuatan pagination dengan Django sendiri dapat pembaca lihat di sini.

Berikut ini bagaimana melakukan infinite scroll di template:

blog/home.html

{% extends 'base.html' %}

{% block content %}
  <div class="infinite-container">
    {% for number in numbers %}
      <div class="infinite-item">{{ number }}</div>
    {% endfor %}
  </div>

  {% if numbers.has_next %}
    <a class="infinite-more-link" href="?page={{ numbers.next_page_number }}">More</a>
  {% endif %}

  <script>
    var infinite = new Waypoint.Infinite({
      element: $('.infinite-container')[0]
    });
  </script>
{% endblock %}

Elemen yang memiliki kelas .infinite-container adalah kontainer template memuat item-item berikutnya. Pemuatan item berikutnya akan terjadi setiap kali elemen dengan kelas .infinite-more-link muncul di layar (sudah berada dibagian akhir halaman tersebut). Saat elemen tersebut muncul, ia akan men-trigger rikues AJAX untuk memuat konten baru dari URL yang ada di properti href kelas .infinite-more-link.

Halaman ini akan terus mengambil item baru sampai .infinite-more-link tidak lagi muncul. Artinya, paginator sudah sampai di Page terakhir.

Itulah alasan kita menggunakan if numbers.has_next.

Menambah Status Loading

Kita juga bisa mengembangkan contoh di atas dengan memberikan status loading, saat halaman tersebut mengambil data baru.

blog/home.html

{% extends 'base.html' %}

{% block content %}
  <div class="infinite-container">
    {% for number in numbers %}
      <div class="infinite-item">{{ number }}</div>
    {% endfor %}
  </div>

  {% if numbers.has_next %}
    <a class="infinite-more-link" href="?page={{ numbers.next_page_number }}">More</a>
  {% endif %}

  <div class="loading" style="display: none;">
    Loading...
  </div>

  <script>
    var infinite = new Waypoint.Infinite({
      element: $('.infinite-container')[0],
      onBeforePageLoad: function () {
        $('.loading').show();
      },
      onAfterPageLoad: function ($items) {
        $('.loading').hide();
      }
    });
  </script>
{% endblock %}

Blok loading akan tampil saat AJAX masih berjalan di belakang layar:

Infinite Scroll Loading

Contoh Class-Based View dengan Model

Idenya masih sama. Namun, contoh ini akan menjadi lebih rapi.

Perhatikan model berikut:

models.py

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=30)
    body = models.TextField(max_length=2000)
    date = models.DateTimeField()
    author = models.CharField(max_length=30)

views.py

from django.views.generic.list import ListView
from .models import Article

class ArticlesView(ListView):
    model = Article
    paginate_by = 5
    context_object_name = 'articles'
    template_name = 'blog/articles.html'

blog/articles.html

{% extends 'base.html' %}

{% block content %}
  <div class="infinite-container">
    {% for article in articles %}
      <div class="infinite-item">
        <h3>{{ article.title }}</h3>
        <p>
          <small>{{ article.author }} / {{ article.date }}</small>
        </p>
        <p>{{ article.body|truncatechars:100 }}</p>
      </div>
    {% endfor %}
  </div>

  <div class="loading" style="display: none;">
    Loading...
  </div>

  {% if page_obj.has_next %}
    <a class="infinite-more-link" href="?page={{ articles.next_page_number }}">More</a>
  {% endif %}

  <script>
    var infinite = new Waypoint.Infinite({
      element: $('.infinite-container')[0],
      onBeforePageLoad: function () {
        $('.loading').show();
      },
      onAfterPageLoad: function ($items) {
        $('.loading').hide();
      }
    });
  </script>
{% endblock %}

Perbedaannya dicontoh ini, karena kita menggunakan ListView, tiap halaman (Page) akan tersedia lewat objek page_obj di template.


Kesimpulan

Semoga tutorial ini bermanfaat! Jika ingin mengeksplor contoh ini lebih jauh, silahkan untuk kodenya dari GitHub:github.com/sibtc/simple-infinite-scroll