LoginSignup
40
20

More than 1 year has passed since last update.

ExcelからElixir入門⑥:SPAからPhoenix製APIを呼び出す(表示編)【Vue.js版】

Last updated at Posted at 2018-10-21

【本コラムは、3分で読めて、10分くらいでお試しいただけます】
piacereです、ご覧いただいてありがとございます :bow:

前回までは、シンプルな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アプリをリリース


:stars::stars::stars: お知らせ:Elixir MeetUpを来週末10/26(金)に開催します(リモート視聴も) :stars::stars::stars:

「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
image.png

一応、リモート視聴にも対応していますが、福岡近辺にお住まいの方であれば、遊びに来ていただいて、参加型コンテンツを120%楽しんでいただくのが絶対オススメです

PhoenixでScaffoldを使わずにデータ一覧APIを作る

通常、PhoenixでAPIを作る場合、mix phx.gen.json によるDB CRUD付きREST API、いわゆる「Scaffold」で生成するのが一般的ですが、別に使わなくてもPhoenixでのAPI作成はカンタンですので、早速作ってみましょう

まず 「Sqlex」をインストールすることで、DbMnesiaDb モジュールを導入します(このシリーズの第3回まではこの2つの実装も扱っていましたが、入門編を超えた範囲になるのでOSS化しました)

mix.exsの def deps do 配下の :phoenix の直上に追記します

mix.exs
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 というファイルを追加し、下記のように indexmembers テーブルの一覧を返すようにします

Phoenixは、マップやリスト、リストマップを json に渡すと、自動的にJSON化してくれるのです

前回までで、DB(Mnesia)を使うPhoenix PJを作りましたが、そこに下記ファイルを追加しましょう

lib/basic_web/controllers/member_controller.ex
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", ~ ブロックを追加します

lib/vue_sample_web/router.ex
defmodule BasicWeb.Router do
  use BasicWeb, :router

+ scope "/members", BasicWeb do
+   pipe_through :api
+ 
+   resources "/", MemberController
+ end
  

http://localhost:4000/members にブラウザでアクセスすると、下記のように返ってくれば成功です
image.png

データ一覧APIをREST APIクライアントで直接叩いて確認

REST APIの確認には、「REST APIクライアント」が便利ですが、REST APIクライアントは下記が代表的です

Chrome向け「Postman」

Firefox向け「RESTClient」

VScode向け「REST Client」

ここではPostmanで説明しますが、下記のように「Headers」に Content-Typeapplication/json で設定してから、URLを設定し、「Send」ボタンをクリックしてください
image.png

すると、下記のように返ってきます
image.png

このように、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.【フィールド名】}} で取り出して表示します

lib/basic_web/templates/page/index.html.heex
<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が有効です

lib/basic_web/templates/page/index.html.heex
<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>

APIで取得したデータがテーブル表示されます
image.png

見た目が、SSR版と変わらないので、ちゃんとSPAとして動いているか分からないかも知れませんが、ブラウザの「開発者モード」の「Network」で確認すると、APIが呼ばれていることが分かります
image.png

Phoenixを起動したコンソールでも下記の通り、API呼出である MemberController.index の実行が、ページ呼出である PageController.index の実行の後に行われていることが確認できます
image.png

データ追加APIを実装する

APIによるデータ参照はできたので、次は、データ追加APIを実装してみます

MemberController モジュールに create を追加し、members テーブルへのinsert文を Db.query に渡し、その後、ステータスコードとして 201 Created を返却します

lib/basic_web/controllers/member_controller.ex
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": "竜王"
}

入力後は、こんな感じになります
image.png

「Send」ボタンをクリックすると、「Status」に 201 Created が返ってきます
image.png

データ一覧APIを叩くと、藤井名人が追加されているのが確認できます
image.png

ブラウザをリロードすると、藤井名人がチームにjoinしたことが確認できました :wink:
image.png

【参考】本コラムの検証環境

本コラムは、以下環境で検証しています(恐らく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
  • Windows11

    • WSL2/Ubuntu 22.04+Docker Compose+Elixir 1.13.4 (Erlang/OTP 25)
      • Phoenix 1.6.15

終わり

今回は、Vue.jsによるフロントサイドと、Phoenixによる内部APIを組み合わせを用いて、APIで取得したデータをWeb表示してみました

次回は、画面入力と、APIによるデータ更新 を行ってみます

40
20
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
40
20