【本コラムは、5分で読めて、15分くらいでお試しいただけます】
piacereです、ご覧いただいてありがとございます
前回は、Vue.jsからPhoenix内部APIを呼び出し、Web表示しました
今回は、Web入力をできるようにし、APIによるデータ更新を行う … いわゆるSPA(Single Page Application)を実装します
■「ExcelからElixirマスター」シリーズの目次
①データ並替え/絞り込み
|> ②データ列抽出、Web表示
|> ③WebにDBデータ表示
|> ④Webに外部APIデータ表示
|> ⑤Webにグラフ表示
|> ⑥Vue.js+内部API(表示編)
|> ⑦Vue.js+内部API(更新編)
|> ⑧Gigalixirに本番リリース
|> ⑨Elixir/PhoenixのCRUD Webアプリをリリース
|> ⑩「LiveView」ElixirサーバサイドのみでReact的SPA/リアルタイムUIが作れる
|> ⑪LiveView製Qiita検索SPAをフォームsubmitスタイルに換装
|> ⑫LiveViewのコード内HTMLをテンプレートファイルに分離
|> ⑬ElixirサーバサイドSPAをスマホで見るためにGigalixirリリース
|> ⑭Gigalixir上のLiveViewアプリに独自ドメイン名を付与して正式なアプリ公開
データ入力可能にし、全件更新できるようにする
まずは、現在のデータ表示から作りやすい、データ更新に対応してみましょう
これまでのデータ表示部分を、入力フィールドに変更し、「全件更新」ボタンを追加した上で、データ更新APIを全データ件数分、繰り返すonUpdateメソッドを追加します
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div id="app">
<h1>Posts</h1>
<table border="1">
<tr v-for="result in results">
<td style="padding: 10px;"><input type="text" v-model="result.title"></td>
<td style="padding: 10px;"><input type="text" v-model="result.body"></td>
</tr>
</table>
<button v-on:click="onUpdate">全件更新</button>
</div>
<script>
var app = new Vue
( {
el: '#app',
data:
{
results: [],
},
mounted()
{
axios.get( '/posts' )
.then( response => { this.results = response.data.data } )
},
methods:
{
onUpdate: async function( evt )
{
this.results.forEach( ( result, i ) =>
{
axios.put( '/posts/' + result.id,
{
'post':
{
'title': result.title,
'body': result.body
}
} )
} )
},
},
} )
</script>
「v-model」を使い、入力データをVue.js側に反映
コードの解説ですが、入力フィールドで変更されたデータをVue.js側に反映するには、「v-model」という属性を使います
v-modelの中に書くのは、{{~}}で表示をしたのと同様、Vue.jsの「data:」部で定義したモデルとなります(実際には、v-forでバラされたモデル断片)
なお、v-modelは、HTMLからVue.jsへの一方通行の反映では無く、Vue.jsからHTMLへの反映も兼ねているので、Vue.js側で「data:」部のモデルを書き換えると、画面表示に自動反映されます(双方向バインディングと呼ばれます)
mountedでこれが使われ、初期表示では、APIで取得したデータを画面に反映します
…
<tr v-for="result in results">
<td style="padding: 10px;"><input type="text" v-model="result.title"></td>
<td style="padding: 10px;"><input type="text" v-model="result.body"></td>
</tr>
…
「v-on:click」でクリック時のハンドラ呼出
「全件更新」ボタンは、クリックされたときに、onUpdateを呼び出します
「v-on:click」という属性を使うことで、クリック時のハンドラメソッドを指定できます
他にも、「v-on:change」や「v-on:focus」、「v-on:blur」等、JavaScriptで利用可能なハンドラと同様のものをVue.jsでも利用できます
なお、引数を指定することもでき、引数指定無の場合は、クリックイベントが発生したパーツ(今回だとbutton)のDOMが暗黙の引数として渡されます
…
<button v-on:click="onUpdate">全件更新</button>
…
「methods:」部にハンドラを定義
onUpdateの中身は、v-modelで更新されたresultsを全件、PUTメソッドで更新APIを呼び出し続ける処理です
results全件を更新に回す部分は、forEachを使うと、JavaScriptでも、関数型っぽく書けます
第1引数に配列の要素(resultの部分)、第2引数に配列の要素の添字(iの部分)が返りますので、それらを使って全件処理を書けます(今回は、要素のみ利用)
PUTメソッドで送信する内容は、axios.putの第1引数であるURLには、更新対象となるデータのidを指定します
第2引数は、更新内容をJavaScriptオブジェクト形式で書きます
…
<script>
…
methods:
{
onUpdate: async function( evt )
{
this.results.forEach( ( result, i ) =>
{
axios.put( '/posts/' + result.id,
{
'post':
{
'title': result.title,
'body': result.body
}
} )
} )
},
},
…
</script>
…
画面表示/入力の確認
では、上記ファイルを保存し、以下のような画面がブラウザ表示されることを確認しましょう
入力フィールドの値を書き換え、「全件更新」ボタンをクリックすると、APIが呼び出され、データが更新されます
ただし、更新がかかっても、ページ遷移等が無いため、画面上は、何も起きなかったように見えるので、更新後、ブラウザをリロードして、更新したデータが維持されていることを確認してください(もし、更新に失敗していたら、リロードすると、入力したデータが元に戻るはずです)
データ削除
データ削除は、各データ毎に行えるようにします
「削除」ボタンは、データのidを引数指定して、onDeleteに渡すことで、該当データを削除できるようにします
onDeleteは、DELETEメソッドで削除APIをid指定付きで呼び出し、その後、データ取得し直します
resultsが更新されると、自動的にv-modelで定義したHTML側が表示更新され、削除されたデータが表示されなくなります
ここで、メソッドに「async」、削除API呼出のaxios.deleteに「await」が付いているのは、削除が完全に終わってから、データ取得するよう、「同期処理」にするためです
ここを同期にせず、「非同期処理」で走らせると、データ削除が完了しない状態で、データ取得を行ってしまう可能性があるため、そうならないよう、同期処理化しています
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div id="app">
<h1>Posts</h1>
<table border="1">
<tr v-for="result in results">
<td style="padding: 10px;"><input type="text" v-model="result.title"></td>
<td style="padding: 10px;"><input type="text" v-model="result.body"></td>
<td><button v-on:click="onDelete( result.id )">削除</button></td>
</tr>
</table>
<button v-on:click="onUpdate">全件更新</button>
</div>
<script>
var app = new Vue
( {
el: '#app',
data:
{
results: [],
},
mounted()
{
axios.get( '/posts' )
.then( response => { this.results = response.data.data } )
},
methods:
{
onUpdate: function( evt )
{
this.results.forEach( ( result, i ) =>
{
axios.put( '/posts/' + result.id,
{
'post':
{
'title': result.title,
'body': result.body
}
} )
} )
},
onDelete: async function( id )
{
await axios.delete( '/posts/' + id )
axios.get( '/posts' )
.then( response => { this.results = response.data.data } )
},
},
} )
</script>
上記ファイルを保存すると、以下のような画面がブラウザ表示されます
データ追加
最後に、データ追加です
「追加」ボタンでonCreateを呼び出しますが、追加用フィールドは、resultsとは別のモデルを用意します
onCreateは、POSTメソッドで追加APIを呼び出し、その後、データ取得し直します(ここも同期処理化しています)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div id="app">
<h1>Posts</h1>
<table border="1">
<tr v-for="result in results">
<td style="padding: 10px;"><input type="text" v-model="result.title"></td>
<td style="padding: 10px;"><input type="text" v-model="result.body"></td>
<td><button v-on:click="onDelete( result.id )">削除</button></td>
</tr>
<tr>
<td style="padding: 10px;"><input type="text" v-model="new_title"></td>
<td style="padding: 10px;"><input type="text" v-model="new_body"></td>
<td><button v-on:click="onCreate">追加</button></td>
</tr>
</table>
<button v-on:click="onUpdate">全件更新</button>
</div>
<script>
var app = new Vue
( {
el: '#app',
data:
{
results: [],
new_title: '',
new_body: '',
},
mounted()
{
axios.get( '/posts' )
.then( response => { this.results = response.data.data } )
},
methods:
{
onUpdate: function( evt )
{
this.results.forEach( ( result, i ) =>
{
axios.put( '/posts/' + result.id,
{
'post':
{
'title': result.title,
'body': result.body,
}
} )
} )
},
onDelete: async function( id )
{
await axios.delete( '/posts/' + id )
axios.get( '/posts' )
.then( response => { this.results = response.data.data } )
},
onCreate: async function( evt )
{
await axios.post( '/posts/',
{
'post':
{
'title': this.new_title,
'body': this.new_body,
}
} )
this.new_title = ''
this.new_body = ''
axios.get( '/posts' )
.then( response => { this.results = response.data.data } )
},
},
} )
</script>
上記ファイルを保存すると、以下のような画面がブラウザ表示されます
さて、これで、データの追加/更新/削除ができるようになるので、色々遊んでみてください
終わり
今回は、Web入力をできるようにし、Vue.js+内部APIによるデータ更新/削除/追加を実装しました
この内容は、Vue.js+REST APIによるSPA(Single Page Application)データ操作の基本になりますので、覚えておくと、モダンWebアプリ開発の強力な武器になります
さて、ここまでで、Elixir/Phoenix+Vue.jsによるSPA開発の基本は終わりです
次回は、Phoenixアプリを**「Gigalixir」というPaaSにリリース** して、公開するためのやり方を解説したいと思います