【本コラムは、3分で読めて、10分くらいでお試しいただけます】
piacereです、ご覧いただいてありがとございます
前回までは、シンプルなPhoenixのサーバサイドレンダリング(SSR)により、Web上にDBデータや外部APIデータを表示してきました
今回からは、Vue.jsを使ったフロントサイドと、Phoenixによる内部APIを組み合わせを用いて、よりElixir/Phoenixらしさを実感できるWeb開発へとステージアップします
なお、本コラムと全く同じ内容のVue.js版とReact版もあります
LiveView版
https://qiita.com/piacerex/items/64e77b85bfb4c6832662
React版
https://qiita.com/piacerex/items/27052ac628ec394c7560
■「ExcelからElixir入門」シリーズの目次
①データ並替え/絞り込み
|> ②データ列抽出、Web表示
|> ③WebにDBデータ表示
|> ④Webに外部APIデータ表示
|> ⑤Webにグラフ表示
|> ⑥SPAからPhoenix製APIを呼び出す(表示編)【Vue.js版】
|> ⑦SPAからPhoenix製APIを呼び出す(更新編)【Vue.js版】
|> ⑧Gigalixirに本番リリース
|> ⑨「LiveView」ElixirサーバサイドのみでReact的SPA/リアルタイムUIが作れる
|> ⑩ElixirサーバサイドSPAをスマホで見るためにGigalixirリリース
|> ⑪Gigalixir上のLiveViewアプリに独自ドメイン名を付与して正式なアプリ公開
|> ⑫Elixir/PhoenixのCRUD Webアプリをリリース
お知らせ:Elixir MeetUpを来週末10/26(金)に開催します(リモート視聴も)
「fukuoka.ex#16:蔵出し始めました!Elixir実践テクニック公開します① 」を10/26(金)19時に開催します
開催終了しました
今回のMeetUpから、「座談会+モブプログラミング」という形で、fukuoka.exアドバイザーズ/キャストが、過去会で登壇したり、日頃研鑽している数々のElixir実践テクニックをダイレクトに伝えていきます
今回は、「Elixir+Vue.jsで2倍速開発」と「ElixirでElixirコンパイラ開発」の2トラックにて、集まった皆さまと一緒にElixir実践テクニックをシェアしたいと思います
https://fukuokaex.connpass.com/event/105154
一応、リモート視聴にも対応していますが、福岡近辺にお住まいの方であれば、遊びに来ていただいて、参加型コンテンツを120%楽しんでいただくのが絶対オススメです
PhoenixでScaffoldを使わずにデータ一覧APIを作る
通常、PhoenixでAPIを作る場合、mix phx.gen.json
によるDB CRUD付きREST API、いわゆる「Scaffold」で生成するのが一般的ですが、別に使わなくてもPhoenixでのAPI作成はカンタンですので、早速作ってみましょう
まず 「Sqlex」をインストールすることで、DbMnesia
/Db
モジュールを導入します(このシリーズの第3回まではこの2つの実装も扱っていましたが、入門編を超えた範囲になるのでOSS化しました)
mix.exsの def deps do
配下の :phoenix
の直上に追記します
defmodule Basic.Mixfile do
use Mix.Project
…
defp deps do
[
{:req, "~> 0.3"},
+ {:sqlex, "~> 0.1.0"},
{:phoenix, "~> 1.6.15"},
…
]
end
…
PhoenixをCtrl+C2回で止めて、ライブラリを取得(要ネット接続)し、Phoenixを起動します
mix deps.get
iex -S mix phx.server
次に、controllers
フォルダ配下に member_controller.ex
というファイルを追加し、下記のように index
で members
テーブルの一覧を返すようにします
Phoenixは、マップやリスト、リストマップを json
に渡すと、自動的にJSON化してくれるのです
前回までで、DB(Mnesia)を使うPhoenix PJを作りましたが、そこに下記ファイルを追加しましょう
defmodule BasicDbWeb.MemberController do
use BasicDbWeb, :controller
def index(conn, _p) do
conn
|> json(
"select * from members"
|> Db.query
|> Db.columns_rows
|> Enum.sort(fn current, next -> current["id"] < next["id"] end)
)
end
end
次に、ルーティングにAPI用エントリーとして、CRUD全部(GET/POST/PUT/DELETE)のエンドポイントを一括生成する resources "/", MemberController
を含む scope "/members", ~
ブロックを追加します
defmodule BasicWeb.Router do
use BasicWeb, :router
…
+ scope "/members", BasicWeb do
+ pipe_through :api
+
+ resources "/", MemberController
+ end
…
http://localhost:4000/members
にブラウザでアクセスすると、下記のように返ってくれば成功です
データ一覧APIをREST APIクライアントで直接叩いて確認
REST APIの確認には、「REST APIクライアント」が便利ですが、REST APIクライアントは下記が代表的です
Chrome向け「Postman」
Firefox向け「RESTClient」
VScode向け「REST Client」
ここではPostmanで説明しますが、下記のように「Headers」に Content-Type
を application/json
で設定してから、URLを設定し、「Send」ボタンをクリックしてください
このように、PhoenixでAPIを作成するのは、とてもカンタンです
SPAからデータ一覧APIを呼んだ後、表示する
Vue.jsとaxiosを使って、先ほど作ったAPIを呼ぶSPA(Single Page Application)を作ってみましょう
Vue.jsとaxiosは、<script src=~>
でロードします(「CDN(Content Delivery Network)」という形式です)
import
で指定した createApp
関数が、Vue.jsのメイン処理です
Vue.jsで初期表示時に実行される mounted
ハンドラでは、get
を呼び出すことでaxiosによるAPI呼出を行い、deta()
部で定義された members
変数にAPIで取得したデータを格納します
それを tr
タグに指定した v-for
で1件ずつ取り出し、各フィールドを {{n.【フィールド名】}}
で取り出して表示します
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.global.js"></script>
<script type="importmap">
{
"imports": {
"vue": "https://cdn.jsdelivr.net/npm/vue/dist/vue.esm-browser.prod.js"
}
}
</script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
<h1>Members</h1>
<table>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>team</th>
<th>position</th>
</tr>
<tr v-for="n in members">
<td>{{n.id}}</td>
<td>{{n.name}}</td>
<td>{{n.age}}</td>
<td>{{n.team}}</td>
<td>{{n.position}}</td>
</tr>
</table>
</div>
<script type="module">
import {createApp} from 'vue'
createApp
({
data()
{
return {
members: [],
}
},
mounted(){
this.get()
},
methods:
{
get: function() {
axios.get('/members').then(response => {this.members = response.data})
},
},
}).mount('#app')
</script>
なお、Vue.js 2系だと以下の通りです
el
で指定したidと、HTML中の div
タグが一致した div
タグ内でVue.jsが有効です
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.14/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.2.2/axios.min.js"></script>
<div id="app">
<h1>Members</h1>
<table>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>team</th>
<th>position</th>
</tr>
<tr v-for="n in members">
<td>{{n.id}}</td>
<td>{{n.name}}</td>
<td>{{n.age}}</td>
<td>{{n.team}}</td>
<td>{{n.position}}</td>
</tr>
</table>
</div>
<script>
var app = new Vue
({
el: '#app',
data:
{
members: [],
},
mounted()
{
axios.get('/members')
.then(response => {this.members = response.data})
},
})
</script>
見た目が、SSR版と変わらないので、ちゃんとSPAとして動いているか分からないかも知れませんが、ブラウザの「開発者モード」の「Network」で確認すると、APIが呼ばれていることが分かります
Phoenixを起動したコンソールでも下記の通り、API呼出である MemberController.index
の実行が、ページ呼出である PageController.index
の実行の後に行われていることが確認できます
データ追加APIを実装する
APIによるデータ参照はできたので、次は、データ追加APIを実装してみます
MemberController
モジュールに create
を追加し、members
テーブルへのinsert文を Db.query
に渡し、その後、ステータスコードとして 201 Created
を返却します
defmodule BasicWeb.MemberController do
use BasicWeb, :controller
def index(conn, _p) do
conn
|> json(
"select * from members"
|> Db.query
|> Db.columns_rows
|> Enum.sort(fn current, next -> current["id"] < next["id"] end)
)
end
+ def create(conn, p) do
+ "insert into members values('#{p["name"]}', #{p["age"]}, '#{p["team"]}', '#{p["position"]}')"
+ |> Db.query
+ send_resp(conn, :created, "")
+ end
end
データ追加APIから直接データを追加し、Web上で確認
では、APIでデータ追加してみましょう
「GET」を「POST」に変更し、「Body」を「raw」で設定した後、下記内容をbodyに入力して、藤井名人をチームにjoinしてもらい、大幅戦力増強しましょうw
{
"name": "藤井 聡太",
"age": 20,
"team": "杉本昌隆八段門下",
"position": "竜王"
}
「Send」ボタンをクリックすると、「Status」に 201 Created
が返ってきます
データ一覧APIを叩くと、藤井名人が追加されているのが確認できます
ブラウザをリロードすると、藤井名人がチームにjoinしたことが確認できました
【参考】本コラムの検証環境
本コラムは、以下環境で検証しています(恐らくUbuntu実機やMacでも動きます)
-
Windows 10
- 実機+Elixir 1.14.2 (Erlang/OTP 25)
- Phoenix 1.6.15
- WSL2/Ubuntu 20.04+Elixir 1.14.2 (Erlang/OTP 25) ※最新版のインストール手順はコチラ
- Phoenix 1.6.15
- Docker/Debian 11.6+Elixir 1.14.2 (Erlang/OTP 25)
- Phoenix 1.6.15
- 実機+Elixir 1.14.2 (Erlang/OTP 25)
-
Windows11
- WSL2/Ubuntu 22.04+Docker Compose+Elixir 1.13.4 (Erlang/OTP 25)
- Phoenix 1.6.15
- WSL2/Ubuntu 22.04+Docker Compose+Elixir 1.13.4 (Erlang/OTP 25)
終わり
今回は、Vue.jsによるフロントサイドと、Phoenixによる内部APIを組み合わせを用いて、APIで取得したデータをWeb表示してみました