LoginSignup
6
1

More than 1 year has passed since last update.

Phoenix LiveViewの基本設定

Last updated at Posted at 2022-10-11

Phoenix LiveViewの基本設定に関する備忘録です。phx.gen.htmlで生成したユーザ登録のHTMLをSPA化(LiveView化)したイメージです。ユーザ登録フォームとユーザ一覧からなるページです。ユーザ登録すると、ページ遷移することなしに、自動的にユーザ一覧が更新されます。
Phoenix1.6の基本的な仕組み - Qiita

【参考チュートリアル】
LiveView Chat Tutorial

【過去記事】

実行環境
Elixir 1.13.0
Phoenix 1.6.12
postgreSQL設定済み

1.LiveViewの基本設定

1-1.プロジェクト作成

まずはプロジェクトを作成します。

mix phx.new liveview_people --no-mailer --no-dashboard
cd liveview_people
mix ecto.setup

PostgreSQL のポートが正しくないと、mix ecto.setup でエラーになります。私の Windows の環境ではデフォルトの 5432 ではなく 5433 だったので以下のように変更する必要がありました。

config\dev.exs
config :liveview_people, LiveviewPeople.Repo,
  username: "postgres",
  password: "postgres",
  hostname: "localhost",
  database: "liveview_people_dev",
  port: 5433,
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

1-2.LiveView controller

LiveViewの慣例に従ってlib/liveview_people_web の下に liveディレクトリを作成します。次に LiveView controllerpeople_live.ex を作成します。基本的なLiveViewの設定はここで行います。

lib/liveview_people_web/live/people_live.ex
defmodule LiveviewPeopleWeb.PeopleLive do
  use LiveviewPeopleWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def render(assigns) do
    LiveviewPeopleWeb.UserView.render("users.html", assigns)
  end
end

通常 LiveView controller には以下の3つを定義します。

  • (1) mount関数
  • (2) render関数
  • (3) event handler

mount関数でviewの初期状態を定義します。今回の場合、後で見るように changesetusers の初期状態を設定することになります。
render関数では描画の仕方を指定します。ここではusers.html.heexを指定します。拡張子がheexであることに注意してください。

render関数を明示的に設定せずに、lib/liveview_people_web/live/people_live.html.heex というテンプレートファイルを作成し、暗示的に rennder関数を指定することも可能な場合もあります。--> 「Phoenix LiveView の JavaScript Hook」 しかし私のWindows環境では明示的に指定しないとエラーになりましたが、現在原因の特定はできていません。

event handlerはHTMLに組み込まれたボタンを押したときに発生するイベントなどのcallbackを定義します。今回の場合、changesetとusersを更新することで、HTMLの表示に変更を与えます。

viewの基本的な設定を行います。

lib/liveview_people_web/views/user_view.ex
defmodule LiveviewPeopleWeb.UserView do
  use LiveviewPeopleWeb, :view
end

ここでrender関数で指定した以下の外部ファイルを用意します。拡張子はheexです。

lib/liveview_people_web/templates/user/users.html.heex
<h1>LiveView People Page</h1>

またroot.html.heexの body部分を以下のものに置き換えます。

lib/liveview_people_web/templates/layout/root.html.heex
<body>
  <header>
    <section class="container">
      <h1>LiveView People Example</h1>
    </section>
  </header>
  <%= @inner_content %>
</body>

1-3.routerの修正

router.exの"/"パスを以下のようにLiveView controllerを指すように修正します。

lib/liveview_people_web/router.ex
  scope "/", LiveviewPeopleWeb do
    pipe_through :browser

    live "/", PeopleLive
  end

1-4.基本設定の確認

以上でLivwViewの基本設定が終わったので、サーバを起動し確認します。

mix phx.server

http://localhost:4000/ にアクセスして確認します。

image.png

無事ページの表示ができたので成功です。次のステップでLiveViewの肉付けを行っていきます。

2.次のステップ

2-1.Schema作成など

次のコマンドでSchemaを作成します。ユーザ登録用のテーブルを用意します。

mix phx.gen.schema User users first_name:string last_name:string age:integer
mix ecto.migrate

Schema モジュールを以下のように修正します。特別なcontext moduleは用意しませんので、ここにcreate_userとlist_usersを定義します。

lib/liveview_people/user.ex
defmodule LiveviewPeople.User do
  use Ecto.Schema
  import Ecto.Changeset
  import Ecto.Query
  alias LiveviewPeople.Repo
  alias __MODULE__


  schema "users" do
    field :age, :integer
    field :first_name, :string
    field :last_name, :string

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:first_name, :last_name, :age])
    |> validate_required([:first_name, :last_name, :age])
  end

  def create_user(attrs) do
    %User{}
    |> changeset(attrs)
    |> Repo.insert()
  end

  def list_users do
    User
    |> Repo.all()
  end
end

LiveView controller を以下のように修正します。

lib/liveview_people_web/live/people_live.ex
defmodule LiveviewPeopleWeb.PeopleLive do
  use LiveviewPeopleWeb, :live_view
  alias LiveviewPeople.User

  def mount(_params, _session, socket) do
    users = User.list_users() |> Enum.reverse()
    changeset = User.changeset(%User{}, %{})
    {:ok, assign(socket, changeset: changeset, users: users)}
  end

  def handle_event("new_user", %{"user" => params}, socket) do
    case User.create_user(params) do
      {:error, changeset} ->
        {:noreply, assign(socket, changeset: changeset)}

      {:ok, _user} ->
        changeset = User.changeset(%User{}, %{})
        users = User.list_users() |> Enum.reverse()
        {:noreply, assign(socket, changeset: changeset, users: users)}
      end
  end

  def render(assigns) do
    LiveviewPeopleWeb.UserView.render("users.html", assigns)
  end
end

mount では users と changeset の初期値を設定します。これが template に反映され初期画面となります。
またユーザ登録に対応した "new_user" イベントに対する event handler を定義します。エラー時にはchangeset にエラーの内容が渡されます。成功時は users を取得し直して template に反映させます。ページ遷移なしに反映されるのが LiveView の醍醐味ですね。

changeset = User.changeset(%User{}, %{})
この changeset は「validation: :required」エラーを含んだまま、template の formタグに渡されますが、エラー表示はされません。changeset.action=nil だからです。 ==> A note on :errors

templateを以下のように更新します。

lib/liveview_people_web/templates/user/users.html.leex
<.form let={f} for={@changeset}  id="form" phx-submit="new_user">
  <%= if @changeset.action do %>
    <div class="alert alert-danger">
      <p>Oops, something went wrong! Please check the errors below.</p>
    </div>
  <% end %>

  <%= label f, :first_name %>
  <%= text_input f, :first_name, id: "first_name", placeholder: "first_name"  %>
  <%= error_tag f, :first_name %>

  <%= label f, :last_name %>
  <%= text_input f, :last_name, id: "last_name", placeholder: "last_name"  %>
  <%= error_tag f, :last_name %>

  <%= label f, :age %>
  <%= number_input f, :age, id: "age", placeholder: "age"  %>
  <%= error_tag f, :age %>

  <div>
    <%= submit "Save" %>
  </div>
</.form>

<h1>Listing Users</h1>
<table>
  <thead>
    <tr>
      <th>First name</th>
      <th>Last name</th>
      <th>Age</th>
    </tr>
  </thead>
  <tbody>
<%= for user <- @users do %>
    <tr>
      <td><%= user.first_name %></td>
      <td><%= user.last_name %></td>
      <td><%= user.age %></td>
    </tr>
<% end %>
  </tbody>
</table>

これは基本的には 「mix phx.gen.html」 コマンドで生成したindex.html.heexとform.html.heexを合成したHTMLです。特にこのform構文は heex でないと使えないので注意が必要です。詳細は以下の過去記事を参照してください。
Phoenix1.6の基本的な仕組み - Qiita

初期画面です。changesもusersも空なので、空白のページとなっています。
image.png

3人のユーザを登録した画面です。usersには3人入っている状態です。
image.png

4人目を登録しようとして、Last Nameが未入力でエラーになった状態です。ちゃんとエラーがでて、エラーじゃないfieldは値が入ったままであることに注意してください。changeset.changes にはエラーでないfieldの値が保持され、changeset.errors にはエラーfieldに対するメッセージが入っています。エラーかどうかはchangeset.action がnilであるかどうかで判断します。成功時はnilですが、今回のエラーの場合、changeset.action に"insert"が入っているのでRepo.insert()で失敗したことがわかります。
image.png

今回は以上です。

6
1
0

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
6
1