9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ElixirAdvent Calendar 2023

Day 22

Phoenix v1.7.10/LiveView v0.20.1でScrivenerを使おうとした② -ページネーションコンポーネントの作成

Last updated at Posted at 2023-12-18

こんにちは!
プログラミング未経験文系出身、Elixirの国に迷い込んだ?!見習いアルケミストのaliceと申します。
今回はPhoenix1.7にScrivenerを導入してみた手順をまとめます。
なお、今回の記事は下記の記事を参考にPhoenix1.7でやってみたログです。

目次

1.Scrivener導入
2.ページネーションコンポーネントの作成(本記事)

目的

Phoenix1.7にScrivenerを導入してページネーションをつけたい

実行環境

Windows 11 + WSL2 + Ubuntu 22.04
Elixir v1.14.3
Erlang v26.0.2
Phoenix v1.7.10
LiveView v0.20.1

今回やりたいこと

Phoenix1.7.10/LiveView v0.20.1のプロジェクトにScrivenerを導入したい。

画面上に表示するページネーションコンポーネントを作る

元記事様の手順に従って作っていきます。

関数コンポーネントを作る

Phoenix1.7に合わせて置き場所をcore_components.exに置きました。

lib/nov11_web/components/core_components.ex
+ def paginator(assigns) do
+     ~H"""
+     <div>
+       <div>
+         <p>表示件数:</p>
+         <div>
+           <form phx-change="update_page_size">
+             <select name="page_size">
+               <option value="5" selected={@page_size == 5}>5</option>
+               <option value="10" selected={@page_size == 10}>10</option>
+               <option value="15" selected={@page_size == 15}>15</option>
+               <option value="20" selected={@page_size == 20}>20</option>
+               <option value="25" selected={@page_size == 25}>25</option>
+             </select>
+           </form>
+         </div>
+         <p><%= @total_entries %>件中 <%= @page_size * (@page - 1) + 1 %>~<%= @page_size * @page %>件を表示</p>
+       </div>
+       <div>
+         <button phx-click="update_page" phx-value-page="1" disabled={@page == 1}>先頭へ</button>
+         <button phx-click="update_page" phx-value-page={@page - 1} disabled={@page == 1}>前へ</button>
+         <button phx-click="update_page" phx-value-page={@page + 1} disabled={@page == @total_pages}>次へ</button>
+         <button phx-click="update_page" phx-value-page={@total_pages} disabled={@page == @total_pages}>最後尾へ</button>
+       </div>
+     </div>
+     """
+   end

ビューにコンポーネントを設置

Phoenix1.7に合わせてcore_components.ex内に作った関数コンポーネントを呼び出しました。

lib/nov11_web/live/user_live/index.html.heex
+ <.paginator
+  total_entries={@users.total_entries}
+  page_size={@users.page_size} 
+  page={@users.page_number} 
+  total_pages={@users.total_pages} 
+ >
+ </.paginator>

Contextを修正

元記事様そのままです。

lib/nov11/users.ex
 def list_users() do
    build_list_query()
    |> Repo.paginate()
  end

  def list_users(page, page_size) do
    build_list_query()
    |> Repo.paginate(page: page, page_size: page_size)
  end

  defp build_list_query() do
    from(u in User,
      order_by: [desc: u.id]
    )
  end

LiveView側の記述を修正

ページが変更されたときに、ページサイズがリセットされないよう、すでにセット済みの値を考慮してリダイレクトしたいのですが、下記関数がLiveView v0.20.1では廃止されて使用できなかったので下記の通り変更しました。
 ・push_redirect -> push_navigateでクエリパラメータを渡すように変更

lib/nov11_web/live/user_live/index.ex
defmodule Nov11Web.UserLive.Index do
  use Nov11Web, :live_view

  alias Nov11.Users
  alias Nov11.Users.User

  @default_page 1
  @default_page_size 5

  @impl true
  def mount(_params, _session, socket) do
    {:ok, stream(socket, :users, Users.list_users())}
  end

  @impl true
  def handle_params(params, _url, socket) do
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
  end

  defp apply_action(socket, :edit, %{"id" => id}) do
    socket
    |> assign(:page_title, "Edit User")
    |> assign(:user, Users.get_user!(id))
  end

  defp apply_action(socket, :new, _params) do
    socket
    |> assign(:page_title, "New User")
    |> assign(:user, %User{})
  end

  defp apply_action(socket, :index, params) do
    socket
    |> assign(:page_title, "Listing Users")
    |> assign(:user, nil)
    |> assign(:users, list_users(params))
  end

  @impl true
  def handle_info({Nov11Web.UserLive.FormComponent, {:saved, user}}, socket) do
    {:noreply, stream_insert(socket, :users, user)}
  end

  @impl true
  def handle_event("delete", %{"id" => id}, socket) do
    user = Users.get_user!(id)
    {:ok, _} = Users.delete_user(user)

    {:noreply, stream_delete(socket, :users, user)}
  end

  @impl true
  def handle_event("update_page", %{"page" => page}, socket) do
-     params =
-       socket.assigns
-       |> Map.get(:users)
-       |> Map.take([:page_number, :page_size])
-       |> Map.merge(%{page_number: page})
-       |> Keyword.new()

+     page_size =
+       socket.assigns
+       |> Map.get(:users)
+       |> Map.take([:page_number, :page_size])
+       |> Map.merge(%{page_number: page})
+       |> Keyword.new()
+       |> Keyword.get(:page_size)

+     page_number =
+       socket.assigns
+       |> Map.get(:users)
+       |> Map.take([:page_number, :page_size])
+       |> Map.merge(%{page_number: page})
+       |> Keyword.new()
+       |> Keyword.get(:page_number)

-     {:noreply,
-      push_redirect(socket,
-        to: Routes.user_index_path(socket, :index, params)
-      )}

+    {:noreply,
+     push_navigate(socket, to: ~p"/users?page_number=#{page_number}&page_size=#{page_size}")}
+  end

  @impl true
  def handle_event("update_page_size", %{"page_size" => page_size}, socket) do
-     params =
-       socket.assigns
-       |> Map.get(:users)
-       |> Map.take([:page_number, :page_size])
-       |> Map.merge(%{page_size: page_size})
-       |> Keyword.new()

+    page_size =
+      socket.assigns
+      |> Map.get(:users)
+      |> Map.take([:page_number, :page_size])
+      |> Map.put(:page_size, page_size)
+      |> Map.get(:page_size)

+    page_number =
+      socket.assigns
+      |> Map.get(:users)
+      |> Map.take([:page_number, :page_size])
+      |> Map.get(:page_number)

-    {:noreply,
-      push_redirect(socket,
-        to: Routes.user_index_path(socket, :index, params)
-      )}

+    {:noreply,
+    push_navigate(socket, to: ~p"/users?page_number=#{page_number}&page_size=#{page_size}")}
+  end

  defp list_users(%{"page_number" => page, "page_size" => page_size}) do
    Users.list_users(page, page_size)
  end

  defp list_users(%{"page_number" => page}) do
    Users.list_users(page, @default_page_size)
  end

  defp list_users(%{"page_size" => page_size}) do
    Users.list_users(@default_page, page_size)
  end

  defp list_users(%{}) do
    Users.list_users()
  end
end

不明点(解決出来たら別記事化します)

・クエリパラメータは渡せたが画面上での更新がされません
 →クエリパラメータを受け取る方法が分かっていません
image.png

下記検証の結果、list_users(page, page_size)で%Scrivener.Page{}内のpage_numberとpage_sizeの値が更新されることが分かっています。
では、クエリパラメータをどうやって受け取って上記の変数page, page_sizeにそれぞれ当てはめる?...ここが分かっていない箇所です。

bash
iex -S mix
iex
iex(1)> Nov11.Users.list_users()    
%Scrivener.Page{
  page_number: 1,
  page_size: 5,
  total_entries: 23,
  total_pages: 5,
  entries: [
    %Nov11.Users.User{
    ...
iex
iex(2)> Nov11.Users.list_users(2, 3)
%Scrivener.Page{
  page_number: 2,
  page_size: 3,
  total_entries: 23,
  total_pages: 8,
  entries: [
    %Nov11.Users.User{
    ...

現時点で学んだこと

1.Scrivenerを導入すること自体はできました(画面側がダメなので追って理解を深めたいと思います)

2.Phoenix1.7.10/LiveView v0.20.1の環境ではpush_redirectが廃止されており、リダイレクトの書き方が変わっているということを学びました。

~Elixirの国のご案内~

↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。

We Are The Alchemists, my friends!:bouquet:1
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。2

  1. @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。

  2. @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中!

9
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?