はじめに
ひとりLiveView Advent Calendar の2日目の記事です
この記事はElixir Conf US 2021の発表したシステムの構築と関連技術の解説を目的とした記事です
今回は以下の4つの解説します
- phx.gen.live で作成されたもの
- LiveViewのRouting
- handle_params
- handle_event
phx.gen.live で作成されたもの
以下は phx.gen.live
を実行したときの端末ログです
* creating lib/live_logger_web/live/map_live/show.ex
* creating lib/live_logger_web/live/map_live/index.ex
* creating lib/live_logger_web/live/map_live/form_component.ex
* creating lib/live_logger_web/live/map_live/form_component.html.heex
* creating lib/live_logger_web/live/map_live/index.html.heex
* creating lib/live_logger_web/live/map_live/show.html.heex
* creating test/live_logger_web/live/map_live_test.exs
* creating lib/live_logger_web/live/modal_component.ex
* creating lib/live_logger_web/live/live_helpers.ex
* creating lib/live_logger/loggers/map.ex
* creating priv/repo/migrations/20211118095356_create_maps.exs
* creating lib/live_logger/loggers.ex
* injecting lib/live_logger/loggers.ex
* creating test/live_logger/loggers_test.exs
* injecting test/live_logger/loggers_test.exs
* creating test/support/fixtures/loggers_fixtures.ex
* injecting test/support/fixtures/loggers_fixtures.ex
* injecting lib/live_logger_web.ex
Add the live routes to your browser scope in lib/live_logger_web/router.ex:
live "/maps", MapLive.Index, :index
live "/maps/new", MapLive.Index, :new
live "/maps/:id/edit", MapLive.Index, :edit
live "/maps/:id", MapLive.Show, :show
live "/maps/:id/show/edit", MapLive.Show, :edit
creatingだけを分類するとこんな感じになります
Liveview
①一覧ページ
- creating lib/live_logger_web/live/map_live/index.ex
- creating lib/live_logger_web/live/map_live/index.html.heex
②詳細ページ
- creating lib/live_logger_web/live/map_live/show.ex
- creating lib/live_logger_web/live/map_live/show.html.heex
③モーダルで表示するform
- creating lib/live_logger_web/live/map_live/form_component.ex
- creating lib/live_logger_web/live/map_live/form_component.html.heex
④汎用modal
- creating lib/live_logger_web/live/modal_component.ex
- creating lib/live_logger_web/live/live_helpers.ex
コントローラーに当たる箇所が.exファイル、ビューに当たるのが.heexです
htmlがシンプルな場合は .ex内のrender関数内で~H
(シジル)で囲って記述することもできます
def render(assigns) do
~H"""
<h1>Title</h1>
"""
end
Model
- creating priv/repo/migrations/20211118095356_create_maps.exs ①
- creating lib/live_logger/loggers/map.ex ②
- creating lib/live_logger/loggers.ex ③
①migrationファイル
②スキーマ、バリデーション周り
③クエリ類
Test
- creating test/live_logger_web/live/map_live_test.exs
- creating test/live_logger/loggers_test.exs
- creating test/support/fixtures/loggers_fixtures.ex
テストもバックエンド、フロントエンド両方作成してくれます
本連載ではmodel,testのコードにはあまり触れずLiveViewを主に解説します
Router
Router周りの解説をしていきます
map_live/index.ex
を参考に見ていきましょう
defmodule LiveLoggerWeb.Router do
...
scope "/", LiveLoggerWeb do
live "/maps", MapLive.Index, :index
live "/maps/new", MapLive.Index, :new
live "/maps/:id/edit", MapLive.Index, :edit
live "/maps/:id", MapLive.Show, :show
live "/maps/:id/show/edit", MapLive.Show, :edit
end
end
defmodule LiveLoggerWeb.MapLive.Index do
def mount(_params, _session, socket) do
...
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
...
end
defp apply_action(socket, :new, _params) do
...
end
defp apply_action(socket, :index, _params) do
...
end
end
LiveViewのroutingは以下のように記述します。
SPA寄りな書き方になるで通常のCRUDとは違いますが似たように書くことができます
live "/maps", MapLive.Index, :index
/maps
-> url
MapLive.Index
-> 使用するLiveView Module
:index
-> socket.assigns.live_action
に入るパラメーター
live "/maps", MapLive.Index, :new
/maps/new
にアクセスした場合にsocket.assigns.live_action
に:new
を入れる
live "/maps/:id/edit", MapLive.Index, :edit
/maps/:id/edit
にアクセスした場合にsocket.assigns.live_action
に:edit
を入れる
:id
は以下paramsのMapのkeyになるので、
live_action
が:edit
でparamsに"id"
キーを持つ以下の関数としてパターンマッチする
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
...
end
なので
live "/maps/:id/edit", MapLive.Index, :edit
上記の:id
を:hoge
に変えるとパターンマッチが失敗してエラーになり
defp apply_action(socket, :edit, %{"hoge" => id})
で合わせると正常に動くようになります
handle_params
handle_params
はmount後に実行されるもので
params, url, socket
を引数に処理を実行し{:noreply, socket}
を返す関数です
今回はapply_action
という関数を実行して live_actionを元に:index :edit :new
に振り分けています
apply_action
はそれぞれ以下の処理を行っています
- :index -> formを初期化, titleを
Listing Map
に設定 - :new -> formに新しいchangesetを作成, titleを
New Map
に設定 - :edit -> formにparamsのidで取得したデータをセット, titleを
Edit Map
に設定
なのでrouterの
live "/maps", MapLive.Index, :new
を
live "/maps", MapLive.Index, :hoge
とかにするとマッチするapply_action
がなくエラーになります。
これに対してエラーで落とすのではなく、404ページにリダイレクトしたい場合は以下のような関数をindexの下に追加します。
一応警告は出ますが、editの上に追加するとaction, params
に関係なく実行されて他の関数にマッチしなくなるので関数の順番には注意しましょう
defp apply_action(socket, _action, _params) do
push_redirect(socket, to: "/404")
end
CRUD画面ではURLと画面を合わせるためにhandle_params
でlive_action
を元に振り分けていますが、単純なLiveViewではmount時に一覧を取得してsocket
にassign
することが一般的です。
handle_params
の他の用途はmaps/:id
でアクセスしたが自分が管理しているmap以外だった場合は403errorを返すとかが考えられます
handle_event
handle_eventはphx-click, phx-change, phx-submit
などで指定されたイベント名を引数として関数を実行します
CRUD画面では以下が実装されています
- 削除ボタンをクリック時 ->
handle_event("delete", params, socket)
- フォームの内容が変更された時 ->
handle_event("validate", params, socket)
- フォームがsubmitされたとき ->
handle_event("save", params, socket)
handle_event
内でDBへの保存や削除を行えるため新たにAPIを生やす必要がなくなります
また別の記事で解説しますが JS側からpushEvent
関数を実行することでLiveView側のhandle_event
を実行することも可能です
最後に
長くなってきたので一旦ここで切ろうと思います
次はmodalに関して解説します