JavaScriptを記述しないでSPA(シングルページアプリケーション)のような操作ができるPhoenixのライブラリ「 Phoenix LiveView 」ですが、GitHubページのCHANGELOGを見ていると2019年8月末あたりからバージョニングされていたのに気づきました。
せっかくバージョンが0.1系から0.2系に変わっていたのに気づいたので、興味本位で少し触ってみました。
はじめに
Phoenix LiveViewを利用して、ボタンクリックによって数値numの増減とともに、その数値をベースに計算結果(FizzBuzzの判定とフィボナッチ数の算出)を返す画面を作成しました。
外側のボタンでは値を増減した後で計算結果も算出します。
一方、内側のボタンは計算をしないでベースになる値だけを増減させます。
真ん中のボタンは全ての数値を0に戻します。
ベースになる実装内容については、こちらの記事を参考にしています。
https://qiita.com/kikuyuta/items/c5b0788ad5d09c210c0a
補足事項
Phoenix LiveViewはバージョンアップも頻繁に行われていますので、もしかしたら今回の内容で動作できなくなる場合もあります。
修正箇所に気づいたら、できる限り記事の更新は行う予定です。
環境構築について
Phoenixのプロジェクト名については「 hiyoko 」としています。
そのため、パス名やモジュール名は「 hiyoko_web 」や「 HiyokoWeb 」のような名称になっています。
もし、ご自身のプロジェクトで実施させる際には、プロジェクト名に由来する箇所は読み替えてください。
環境構築についても、先ほど挙げたこちらの記事を参考にしています。
ただし、0.2.0-devへのバージョンアップに伴い、「assets/js/app.js」に対して 後方互換のない変更 が発生していました。以前の内容と比べて、以下の点で変更が必要です。
- 「
import {Socket} from "phoenix"
」 の追記 - 「
LiveSocket("/live", Socket)
」のように第二引数へ「Socket」を追加
import {Socket} from "phoenix"
import LiveSocket from "phoenix_live_view"
let liveSocket = new LiveSocket("/live", Socket)
liveSocket.connect()
コードの記述
Elixir側で記述したコードは、以下の内容です。
ボタンクリック(phx-click)の種類別に、 handle_event 関数を定義しています。
特定の値だけを更新したい場合には updateを、更新したい値を多く渡したい場合は assign を呼び出しています。
なお「 phx-click 」についてですが、こちらもLiveViewのバージョン0.1.2で 後方互換のない変更 が発生しています。
0.1.1までは「phx-value=1
」のような形式でしたが、0.1.2以降では「phx-value-*
」形式になっています。handle_event内での値の取り出し方も「*」に当たる部分を指定(例えば、「phx-value-decv
」の場合、v["decv"]
のように指定)して値を取り出すようになりました。
〜(略)〜
scope "/", HiyokoWeb do
pipe_through :browser
get "/", PageController, :index
live "/exercises", Exercises #<- ここを追記
end
〜(略)〜
defmodule HiyokoWeb.Exercises do
use Phoenix.LiveView
def render( assigns ) do
~L"""
<h2>FizzBuzz Fibonacci</h2>
<p>
<%= @message %>
</p>
<div>
<h3>The count is: <%= @num %></h1>
<button phx-click="deccalc" phx-value-decv=1>-(calc)</button>
<button phx-click="dec" phx-value-decv=3>-</button>
<button phx-click="clear" phx-value-clear=0>clear</button>
<button phx-click="inc" phx-value-incv=4>+</button>
<button phx-click="inccalc" phx-value-incv=1>+(calc)</button>
</div>
<table>
<tr>
<th>num</th>
<th>FizzBuzz</th>
<th>Fibonacci</th>
</tr>
<tr>
<td><%= @num %></td>
<td><%= @fzbz %></td>
<td><%= @fibo %></td>
</tr>
</table>
"""
end
def mount( _session, socket ) do
{:ok, assign( socket, message: "", num: 0, fzbz: 0, fibo: 0 ) }
end
def handle_event("inc", _, socket) do
{:noreply, update(socket, :num, &(&1 + 1))}
end
def handle_event("dec", v, socket) do
{:noreply, update(socket, :num, &(&1 - String.to_integer(v["decv"])))}
end
def handle_event("inccalc", v, socket) do
add = fn(x, y) -> x + y end
v1 = socket.assigns.num |> add.(String.to_integer(v["incv"]))
{:noreply, assign(socket, message: "Calculation(add)", num: v1, fzbz: fizzbuzz(v1), fibo: fib(v1))}
end
def handle_event("clear", _, socket) do
{:noreply, assign(socket, message: "Clear", num: 0, fzbz: 0, fibo: 0)}
end
def handle_event("deccalc", v, socket) do
subtract = fn(x, y) -> x - y end
v1 = socket.assigns.num |> subtract.(String.to_integer(v["decv"]))
{:noreply, assign(socket, message: "Calculation(subtract)", num: v1, fzbz: fizzbuzz(v1), fibo: fib(v1))}
end
defp fizzbuzz(x) do
fb = fn
0, 0, _ -> "FizzBuzz"
0, _, _ -> "Fizz"
_, 0, _ -> "Buzz"
_, _, i -> Integer.to_string(i)
end
fb.(rem(x,3), rem(x,5), x)
end
defp fib(0), do: 0
defp fib(1), do: 1
defp fib(x) when (x >=2) do
fib(x-1) + fib(x-2)
end
defp fib(x) when (x < 0) do
ax = abs(x)
:math.pow(-1, ax+1) * fib(ax)
end
end
実際に書いてみるとわかりますが、JavaScriptのコードを直接書かずにSPAのようなことができるのは、ちょっとした感動を与えてくれます。
フロントエンドのコードを意識せずに、サーバサイドだけで完結できるのは随分とお手軽です。
最後に宣伝
技術書典7(2019月9月22日)で、「 PhoenixLiveViewとNervesをさわるElixirへのいざない 」という本を頒布します。
タイトルの通り、Elixir界隈では最近注目の的である「 Phoenix LiveView 」と「 Nerves 」という二つのフレームワークについてさわってみようという趣旨の入門本となっています。
興味を持たれたら、ぜひお立ち寄りください。
執筆時期の関係上、LiveViewのバージョンについて「 0.1.1 」ベースの内容になっています。
LiveViewのバージョンを明示的に 0.1.1 に指定したい場合は、以下のようにすれば良いです。
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view"},
の箇所を
{:phoenix_live_view, "0.1.1"},
に修正。