La pagination est une première méthode pour présenter les listes d’item à l’utillisateur. Il existe une seconde méthode, le défilement infini de la liste dans la table LiveView.
Quel environnement est installé sur notre poste de travail ?
Nous réa&lisons le projet sur Windows. Le dossier utilisé est celui du projet meow. Commençons par nous placer dans ce dossier, puis vérifions les outils installés :
- cd C:\CarbonX1\Phoenix\Public\meow
- code .
Quelles sont les versions disponibles :
- elixir -v ‘version d’elixir
- mix local.hex ‘mise à jour des outils hex
- mix archive.install hex phx_new ‘mise à jour de phoenix
- psql -V ‘version de postgres
C:\CarbonX1\Phoenix\Public\meow>elixir -v Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns] Elixir 1.15.5 (compiled with Erlang/OTP 26) C:\CarbonX1\Phoenix\Public\meow>mix local.hex Found existing entry: c:/Users/broussel/.mix/archives/hex-2.0.6 Are you sure you want to replace it with "https://builds.hex.pm/installs/1.14.0/hex-2.0.6.ez"? [Yn] n C:\CarbonX1\Phoenix\Public\meow>mix archive.install hex phx_new Resolving Hex dependencies... Resolution completed in 0.083s New: phx_new 1.7.10 * Getting phx_new (Hex package) All dependencies are up to date Compiling 11 files (.ex) Generated phx_new app Generated archive "phx_new-1.7.10.ez" with MIX_ENV=prod Found existing entry: c:/Users/broussel/.mix/archives/phx_new-1.7.10 Are you sure you want to replace it with "phx_new-1.7.10.ez"? [Yn] n C:\CarbonX1\Phoenix\Public\meow>psql -V psql (PostgreSQL) 16.1 C:\CarbonX1\Phoenix\Public\meow>
Préparation pour le défilement infini de la liste dans la table LiveView
Le défilement infini permet de chargfer les donneées de la base de onnées en bloc sans avoir besoin d’intervention de l’utilisateur. La liste se charge lorsque l’utilisateur arrive dans la fin de la liste aussi bien en bas de dliste qu’en haut de liste. Ce sont ces eveenements qui déclenche le chargement.
Du point de vue technique vis à vis de la base de données, cela permet d’éviter la manipulation d’un trop gros volumen de données, ce qui aurait pour conséquence de ralentir l’application.
Du point de vue de l’utilisateur, cela permet de voir la liste sans intervention particulière, grâce a des évènements declenchés automatiquement en fin de liste.
Le défilement infini de la liste dans une table LiveView est à la fois un confort pour l’utilisateur et une solution technique élégante.
L’utilisation du défilement infini est réalisé dans une table sans trie ni filtre activable par l’utilisateur.
Défilement infini pour la liste LiveView
Modification du contexte du projet
DAns le fichier Context du projet, nous ajoutons deux fonctions :
- meerkat_count ‘pour avoir le nombre d’item total
- list_meerkats_with_pagination ‘pour obtenir les items d’un bloc
def meerkat_count(), do: Repo.aggregate(Meerkat, :count) def list_meerkats_with_pagination(offset, limit) do from(m in Meerkat) |> limit(^limit) |> offset(^offset) |> Repo.all() end
La fonction meerkat_count va nous permettre de savoir lorsque nous avosn atteind la fin de la liste dans notre défilement infini de la liste de la table LiveView.
La fonction list_meerkats_with_pagination utilise deiux parametre, offset pour indiquer le début du bloc d’item à choisir et limit pour donner le nombre d’item à prendre dnas le bloc.
Création de la vue LiveView pour defilement infini de la liste
Nous créons un vue LiveView qui contient la génération de la partie html composée d’une table affichant une ligne par meerkat avec son id et son name :
defmodule MeowWeb.InfinityLive do use MeowWeb, :live_view alaias Meow.Meerkats def render(assigns) do ~H""" <table> <tbody id="meerkats" phx-update="append" phx-hook="InfinityScroll" > <%= for meerkat <- @meerkats do %> <tr id={"meerkat-#{meerkat.id}"}> <td><%= meerkat.id %></td> <td><%= meerkat.name %></td> </tr> <% end %> </tbody> </table> """ end end
Ce qui caractérise cette table ce sont les deux tag :
- phx-update ‘parametre sur la façon de mettre à jour la table lorsque les données ont changées dans la variable @meerkats (par defaut c’est replace et non append)
- phx-hook ‘défini l’évenement à déclencher lorsque l’utilisateur arrive en bas de la liste
Quelles sont les compôrtement possibles avec les valeurs de phx-update :
- ignore ‘ne pas faire de mise à jour automatique lorsque les données change, permet une gestion par javascript des changements de la table
- prepend ‘ajoute les element au debut de la table et non à la fin
- append ‘ajoute les ellements à la fin de la table
- replace ‘remplace toute la table avec les nouveaux elements
Avec append et prepend, chaque ligne de la table doit disposer d’un identifiant unique afin de ne pas créer de doublon.
Initialisation de la liste
La fonction mount initialise la liste dans InfinityLive. Nous accédons à la base de données à travers le contexte Meerkats pour :
- connaitre le nombre d’item avec meerkat_count,
- charger le premier lot d’item grâce à list_meerkats_with_pagination.
Pour simplifier le code, nous avons au début de InfinityLive défini l’alias Meow.Meerkats.
La fonction mount défini les valeurs initialle avec assign(socket, offset: 0, limit: 25, count: count) en ligne 6 ci-dessous. Rappel : le pipe ‘|>‘ ajoute le résultat de la fonction précédente en premier argument de la fonction suivante.
def mount(_params, _session, socket) do count = Meerkats.meerkat_count() socket = socket |> assign(offset: 0, limit: 25, count: count) |> load_meerkats() {:ok, socket, temporary_assigns: [meerkats: []]} end defp load_meerkats(socket) do %{offset: offset, limit: limit} = socket.assigns meerkats = Meerkats.list_meerkats_with_pagination(offset, limit) assign(socket, :meerkats, meerkats) end
La ligne 9 ci-dessus indique temporary_assigns: [meerkats: []]. Cette option permet à Elixir de vider la variable @meerkats après l’affichage avec LiveView. Cela évite de surcharger la mémoire du serveur. Nous pouvons aussi jouer sur la variable limit qui donne le nombre d’item de chaque lot. Si le nombre d’item est trop faible la page va demander trop souvent le lot suivant. Ajuster la valeur de limit se fait par essai-erreur grâce à des outils comme : wrt, k6, Jmeter.
Le navigateur déclenche les chargements de la table grâce aux hoock
Les évènement déclenchant le chargement du lot suivant proviennent du navigateur. LiveView permet de créer des évènement à partir de code javascript installés dans la page. Ce sont les hooks. L’évènement utilise le websocket pour communiquer avec le serveur.
Pour gérer la demande de chargement, nous créons l’évènement load_more. Nous créons dans la page InfinityLive, la fonction handle_event pour prendre en charge cet évènement :
def handle_event("load-more", _params, socket) do %{offset: offset, limit: limit, count: count} = socket.assigns socket = if offset < count do socket |> assign(offset: offset + limit) |> load_meerkats() else socket end {:noreply, socket} end
LE traitement de l’évènement est assez classique. Nous prenons les parametres dans le socket.assigns. Si il reste des items à afficher, nous modifions le pointeur de début de lot offset, et demandons l’affichage du lot ainsi défini.
Mise en place du défilement infini pour la liste dans la table LiveView
Nous devons maintenant déclarée la route pour pouvoir accéder à notre page :
MEOW/lib/meow_web/router.ex :
defmodule MeowWeb.Router do use MeowWeb, :router pipeline :browser do plug(:accepts, ["html"]) plug(:fetch_session) plug(:fetch_live_flash) plug(:put_root_layout, {MeowWeb.LayoutView, :root}) plug(:protect_from_forgery) plug(:put_secure_browser_headers) end pipeline :api do plug(:accepts, ["json"]) end scope "/", MeowWeb do pipe_through(:browser) live("/infinity", InfinityLive) live("/", MeerkatLive) end end
La page InfinityLive est accessible par l’url :
- http://localhost:4000/infinity
Démarrons le serveur et vérifions le fonctionnement de notre page :
- cd C:\CarbonX1\Phoenix\Public\meow
- mix phx.server
- http://localhost:4000/infinity
Nous avons bien la table qui apparait avec le premier jeux de données initial. Nous devons maintenant ajouter la partie javascript dans la page. C’est l’évènement load_more qui demande au serveur d’envoyer les données suivantes.
Mettre en place dans la page le hook client en javascript
La communication du navigateur vers le serveur suppose la mise en place d’un code projet en javascript.
Pour mettre en place un hoock nous devons :
- définir le hook
- ajouter ce hook au LiveSocket
- ajouter le phx-hook tag dans la page pour envoyer une action au serveur
Nous créons le fichier javascript.
MEOW/assets/js/infinity-scroll.js :
export default{ rootElement(){ return ( document.documentElement || document.body.parentNode || document.body ); }, scrollPosition(){ const {scrollTop, clientHeight, scrollHeight} = this.rootElement(); return ( (scrollTop + clientHeight ) / scrollHeight ) * 100; }, mounted(){ this.threshold = 90; this.lastScrollPosition = 0; window.addEventListener( "scroll", () => { const currentScrollPosition = this.scrollPosition(); const isCloseToBottom = currentScrollPosition > this.threshold this.lastScrollPosition <= this.threshold ; if (isCloseToBottom) this.pushEvent ("load-more", {}); this.lastScrollPosition = currentScrollPosition; } ); } }
Le principe est le suivant :
on défin deux fonctions :
- prendre l’élement html de la fenetre avec rootElement()
- obtenir la position courante de l’ascenceur avec scrollPosition()
Puis on ajoute l’évènement sur « scroll » à la fenêtre window pour calculer le déclenchement de l’évènement phx-hook appelé « load_more » à envoyer au serveur. Le serveur va recevoir cet evènement et va raffraichir la liste des items dans la table LiveView.
Relançons le serveur, malheureusement cela ne fonctionne pas. Nous avons l’erreur :
- unknown hook found for « InfinityScroll »
- <tboby id= »meerkats » phx-update= »append » phx-hook= »InfinityScroll »>…</tbody>
La solution consiste à ne pas oublier de déclarer notre hook dans le fichier app.js :
MEOW/assets/js/app.js :
// The below line isn't mentioned in the book but needed in Chapter 5 import InfinityScroll from "./infinity-scroll"; let Hooks = {}; // The below line isn't mentioned in the book but needed in Chapter 5 Hooks.InfinityScroll = InfinityScroll;
- cd C:\CarbonX1\Phoenix\Public\meow
- mix phx.server
- http://localhost:4000/infinity
Conclusion, le défilement infini fonctionne
Nous avons bien le défilement infini en place pour une liste d’item dans une table LiveView.
Pour ce faire, nous avons utilisé le Hook qui permet de créer la communication entre le navigateur et le serveur. Cela suppose de créer le javascript dans un fichier dédié au nom du hook, et de rajouter 2 lignes dans le fichier app.js pour permettre le téléchargement du code inifity_scroll.js.
l’évenement est traité au niveau de la page inifinty_live.ex sur le serveur avec un évenement géré par la fonction handle_event(« load-more », _params, socket), qui va demander le chargement des données.
Pour créer cette communication navigateur vers serveur nous avons:
- créer le code javascript infinity-scroll.js
- déclaré le code javascript dans app.js
- ajouter l’évènement dans le html de la page infinity_live.ex avec phx-hook
- créer le handle_event dans la page infinity_live.ex