Phoenix 1.4.16 の環境では、本家のインストールページで記載されている手順のいくつかが既に組み込まれています。そのためいくつかの手順が本記事の記述と異なります。
最新の Phoenix.LiveView の組み込み方法を紹介します。
直近3回の LiveView の記事では、LiveView のバージョンを0.8に固定してプロジェクトを構築していました。
それはバージョン0.9および0.10では、CSSファイルが読み込めない等の問題が発生していたからでした。
今回その問題を解消しましたので、現バージョンでのプロジェクト構築方法を記述します。
本記事に使用したリポジトリです。
0.9からLayoutの仕様が変わり、LiveViewからは templates/layout/app.html.eex
のファイルが使用できなくなった事に起因していました。
現在公開されている公式のドキュメントでは、その記述が無く仕様の変更を見落としていました。
私と同じ問題に出くわしている方が、issuesで質問してくれて、仕様変更の見落としに気づきました。
すぐにでも本家のページが更新される可能性がありますが、せっかくなので現時点での Phoenix.LiveView のインストール方法をまとめました。
ちなみに、本家のインストールページはこちらとなっております。
また、Phoenix 作者が記述しているサンプルプロジェクトのソースも参考にしました。
ただし、サンプルプロジェクトは0.10に対応していなかったので、そこのところはチェンジログを追いかけながら修正しました。
今までは以下の構造でした。
ファイル | 役割 |
---|---|
app.html.eex | 通常のViewとLiveViewの両方に対応。 |
それが、以下のように変更されました。
ファイル | 役割 | 今までとの違い |
---|---|---|
root.html.eex | 通常のViewとLiveViewの両方に対応 | app.html.eexの役割がroot.html.eexに変更。 |
app.html.eex | 通常のViewのみに対応 | 通常のViewで必要な部分のみ記述するように変更。 |
live.html.leex | LiveViewのみに対応 | LiveViewで必要な部分のみ記述するように変更。 |
そのためLayoutに関する記述方法が変わりました。
記事公開時点で使用しているバージョンは以下です。
このバージョンをもとに説明します。
項目 | バージョン |
---|---|
erlang | 22.3 |
elixir | 1.10.2-otp-22 |
Phoenix | 1.4.16 |
Phoenix.LliveView | 0.10.0 |
手順は以下のようになります。
説明のために、LiveViewページの追加も行います。
まずはプロジェクトを作成します。
プロジェクト名は hello_lv10 で、ectoを使用しない設定にします。
$ mix phx.new hello_lv10 --no-ecto
作成が終わったらプロジェクトのルートに移動します。
Phoenix.LiveViewをインストールする手順を記述します。
mix.exs
に以下の記述を追加します。
# mix.exs
def deps do
[
{:phoenix_live_view, "~> 0.10.0"},
{:floki, ">= 0.0.0", only: :test}
]
end
以下のコマンドによりモジュールを取得します。
$ mix deps.get
router.ex
にて行の先頭が +
で示されている箇所を追加し、-
で示されている箇所を削除します。
# lib/my_app_web/router.ex
+ import Phoenix.LiveView.Router
pipeline :browser do
...
plug :fetch_session
- plug :fetch_flash
+ plug :fetch_live_flash
...
+ plug :put_root_layout, {HelloLv10Web.LayoutView, :root}
end
ここで、以下の行が新しく加わった部分であり、新しい Layout に対応した部分でもあります。
plug :put_root_layout, {HelloLv10Web.LayoutView, :root}
hello_lv10_web.ex
でハイライトされている行を追加します。
# lib/hello_lv10_web.ex
def controller do
quote do
...
import Phoenix.LiveView.Controller end
end
def view do
quote do
...
import Phoenix.LiveView.Helpers end
end
def router do
quote do
...
import Phoenix.LiveView.Router end
end
endpoint.ex
にてハイライトされている行を追加します。
# lib/my_app_web/endpoint.ex
defmodule MyAppWeb.Endpoint do
use Phoenix.Endpoint
# ...
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] # ...
end
package.json
にてハイライトされている行を追加します。
// assets/package.json
{
"dependencies": {
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
"phoenix_live_view": "file:../deps/phoenix_live_view" }
}
プロジェクトルートのコマンドプロンプトから、以下コマンドでモジュールを追加します。
$ npm install --prefix assets
app.js
へ以下の記述を追加します。
// assets/js/app.js
import {Socket} from "phoenix"
import LiveSocket from "phoenix_live_view"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}});
// connect if there are any LiveViews on the page
liveSocket.connect();
これで LiveView のインストールは完了です。
動作確認のため LiveView のページを作成します。
以下のハイライトされている行にある3ファイルを作成します。
├── lib
│ ├── hello_lv10_web
│ │ ├── templates
│ │ │ ├── layout
│ │ │ │ ├── app.html.eex│ │ │ │ ├── live.html.leex│ │ │ │ └── root.html.eex
生成された app.html.eex
をもとに root.html.eex
を作ります。
以下はもともとの app.html.eex
です。
ハイライト箇所は通常のViewに固有のものなので削除します。
<!-- lib/hello_lv10_web/templates/layout/app.html.eex -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title><%= assigns[:page_title] || "HelloLv10 · Phoenix Framework" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<%= csrf_meta_tag() %>
</head>
<body>
<header>
<section class="container">
<nav role="navigation">
<ul>
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
</ul>
</nav>
<a href="https://phoenixframework.org/" class="phx-logo">
<img src="<%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
</a>
</section>
</header>
<main role="main" class="container"> <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p> <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> <%= render @view_module, @view_template, assigns %> </main> <script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>
削除した場所に以下の記述を追加します。
<%= @inner_content %>
この記述が、もともとの以下の記述のハイブリッド版になります。
<%= render @view_module, @view_template, assigns %>
app.js
の読み込み位置を head 要素内に移動して完成です。
最終的に root.html.eex
は以下のようになりました。
<!-- lib/hello_lv10_web/templates/layout/root.html.eex -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title><%= assigns[:page_title] || "HelloLv10 · Phoenix Framework" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<%= csrf_meta_tag() %>
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script> </head>
<body>
<header>
<section class="container">
<nav role="navigation">
<ul>
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
</ul>
</nav>
<a href="https://phoenixframework.org/" class="phx-logo">
<img src="<%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
</a>
</section>
</header>
<%= @inner_content %> </body>
</html>
通常のViewで必要なのは、もともとの app.html.eex
で記述していた以下になります。
この記述のみをそのまま残してこのファイルは終了です。
<!-- lib/hello_lv10_web/templates/layout/app.html.eex -->
<main role="main" class="container">
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<%= render @view_module, @view_template, assigns %>
</main>
このファイルだけ拡張子が eex
ではなく leex
となります。
LiveViewページ用のレイアウトですが、以下のようにしました。
root.html.eex
でのレイアウトが適用された上に、このレイアウトが適用されることになります。
<!-- lib/hello_lv10_web/templates/layout/live.html.eex -->
<h1>This is LiveView</h1>
<div id="main">
<%= @live_module.render(assigns) %>
</div>
LiveView のページとして、以下の2ページを作ります。
それぞれ内容を変えます。
リンクは特に作成せず、アクセスはダイレクトにURLを打ち込むものとします。
ページ | URL | 内容 |
---|---|---|
LivePage1 | /live1 | レイアウトファイルとしてroot.html.eex のみを使用する。 |
LivePage2 | /live2 | レイアウトファイルとしてroot.html.eex に加えて、live.html.leexも使用する。 |
それぞれのページ用に以下の2ファイルを作成します。
├── lib
│ ├── hello_lv10_web
│ │ ├── live
│ │ │ ├── live_page1.ex
│ │ │ └── live_page2.ex
ハイライトで示した2ページ分を追加します。
# lib/hello_lv10_web/router.ex
scope "/", HelloLv10Web do
pipe_through :browser
get "/", PageController, :index
live "/live1", LivePage1 live "/live2", LivePage2end
通常通りに LiveView を作成します。
必須である2つの関数のみ実装します。
# lib/hello_lv10_web/live/live_page1.ex
defmodule HelloLv10Web.LivePage1 do
use Phoenix.LiveView
def render(assigns) do
~L"""
<div>Hello LiveView Page1.</div>
<div>There is no layout.</div>
"""
end
def mount(_arg1, _session, socket) do
{:ok, socket}
end
end
LiveView 用のレイアウトを読み込む形で実装します。
ハイライトされている行でレイアウトファイルを指定しています。
それ以外は LivePage1
と本質的な違いはありません。
# lib/hello_lv10_web/live/live_page2.ex
defmodule HelloLv10Web.LivePage2 do use Phoenix.LiveView, layout: {HelloLv10Web.LayoutView, "live.html"}
def render(assigns) do
~L"""
<div>Hello LiveView Page2.</div>
<div>I'm in the live layout.</div>
"""
end
def mount(_arg1, _session, socket) do
{:ok, socket}
end
end
これで実装は完了です。
とりあえず、これで最新のLiveViewを使って開発を進められるようになりました。
過去のプロジェクトについても、適宜アップデートしていこうと思います。
本記事に使用したリポジトリです。