4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【旧版】ExcelからElixirマスター⑦:Vue.js+内部API (更新編)

Last updated at Posted at 2023-01-01

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

前回は、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メソッドを追加します

lib/vue_sample_web/templates/page/index.html.eex
<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で取得したデータを画面に反映します

lib/vue_sample_web/templates/page/index.html.eex
<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が暗黙の引数として渡されます

lib/vue_sample_web/templates/page/index.html.eex
<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オブジェクト形式で書きます

lib/vue_sample_web/templates/page/index.html.eex
<script>

		methods: 
		{
			onUpdate: async function( evt )
			{
				this.results.forEach( ( result, i ) => 
				{
					axios.put( '/posts/' + result.id, 
						{ 
							'post':
							{
								'title': result.title, 
								'body':  result.body
							} 
						} )
				} )
			}, 
		}, 

</script>

画面表示/入力の確認

では、上記ファイルを保存し、以下のような画面がブラウザ表示されることを確認しましょう
image.png

入力フィールドの値を書き換え、「全件更新」ボタンをクリックすると、APIが呼び出され、データが更新されます

ただし、更新がかかっても、ページ遷移等が無いため、画面上は、何も起きなかったように見えるので、更新後、ブラウザをリロードして、更新したデータが維持されていることを確認してください(もし、更新に失敗していたら、リロードすると、入力したデータが元に戻るはずです)

データ削除

データ削除は、各データ毎に行えるようにします

「削除」ボタンは、データのidを引数指定して、onDeleteに渡すことで、該当データを削除できるようにします

onDeleteは、DELETEメソッドで削除APIをid指定付きで呼び出し、その後、データ取得し直します

resultsが更新されると、自動的にv-modelで定義したHTML側が表示更新され、削除されたデータが表示されなくなります

ここで、メソッドに「async」、削除API呼出のaxios.deleteに「await」が付いているのは、削除が完全に終わってから、データ取得するよう、「同期処理」にするためです

ここを同期にせず、「非同期処理」で走らせると、データ削除が完了しない状態で、データ取得を行ってしまう可能性があるため、そうならないよう、同期処理化しています

lib/vue_sample_web/templates/page/index.html.eex
<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>

上記ファイルを保存すると、以下のような画面がブラウザ表示されます
image.png

データ追加

最後に、データ追加です

「追加」ボタンでonCreateを呼び出しますが、追加用フィールドは、resultsとは別のモデルを用意します

onCreateは、POSTメソッドで追加APIを呼び出し、その後、データ取得し直します(ここも同期処理化しています)

lib/vue_sample_web/templates/page/index.html.eex
<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>

上記ファイルを保存すると、以下のような画面がブラウザ表示されます
image.png

さて、これで、データの追加/更新/削除ができるようになるので、色々遊んでみてください

終わり

今回は、Web入力をできるようにし、Vue.js+内部APIによるデータ更新/削除/追加を実装しました

この内容は、Vue.js+REST APIによるSPA(Single Page Application)データ操作の基本になりますので、覚えておくと、モダンWebアプリ開発の強力な武器になります

さて、ここまでで、Elixir/Phoenix+Vue.jsによるSPA開発の基本は終わりです

次回は、Phoenixアプリを**「Gigalixir」というPaaSにリリース** して、公開するためのやり方を解説したいと思います

4
0
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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?