POST通信と書きつつ多分GET通信もいけるはず。
SpringBootでオブジェクト指向プログラミング中、どうしても通常の送信方法(htmlファイルでformで囲ってsubmitで送信)が取れなかったため、JavaScriptで操作した備忘録。
先に結果を書いておきますが、奮闘したのがなんだかもったいないので試したことも残しておく。
初心者がゴリ押して作ったコードなので参考程度にしてください。
#前提
使用OS-Windows10
使用ツール-Spring Tool Suite 4 4.6.2
テンプレートエンジン-Thymeleaf
フレームワーク-mybatis
- デモECサイトを作成中。
- チームで作業しているため、できるだけほかの人のファイルに影響を与えずに「index.htmlでAボタンを押した際に情報(パラメータ)がjavaファイル(DemoForm.java)へ渡る」機能を実装したい。
- Aボタンやパラメータはテーブル内に配置されていて、デザインに変化がないようhtmlでformタグを使いたくない。
※DemoForm.javaでは情報のバイト配列化のみ行い、実際の処理はDemoController.javaに記述されている。
#成功した結果
<table>
<tr>
<th>アイテム名</th><th>値段</th><th>個数</th><th>カート</th>
</tr>
<tr th:each="item: ${items}">
<td><a href="/sample/item/{id}" th:href="@{/sample/item/{id}(id=${item.id})}" th:text="${item.itemName}"></a>
</td>
<td class="price" th:text="${item.price}" />
<td>
<input type="number" name="itemCount" value="0" min="0" />
<input type="hidden" name="itemId" th:value="${item.id}" />
</td>
<td>
<!-- Aボタン押下とともにIDと個数をパラメータとして送信 -->
<button class="AButton">追加</button>
</td>
</tr>
</table>
$(() => {
$('.AButton').on('click', demo);
});
let demo = (event) =>{
let itemId = $(event.target).parents('tr').find("[name='itemId']").val();
let itemCount = $(event.target).parents('tr').find("[name='itemCount']").val();
if (itemCount == 0){
alert("個数が0です。");
return;
};
let f = document.createElement('form');
f.method = 'post';
f.action = '/sample/cart/add';
f.innerHTML = '<input name="itemCount" value='+ itemCount + '>' +
'<input name="itemId" value=' + itemId + '>';
document.body.append(form);
form.submit();
}
パラメータとして送信したいのは
<input type="number" name="itemCount" value="0" min="0" />
の値と
<input type="hidden" name="itemId" th:value="${item.id}" />
の値。
一番最後に解説をコメントで記したものをのせておくので、各コードで何をやっているかはそちらを見てください。
if文については試行コードでは省略します。
#試行1:ajaxで情報を送り、画面遷移のコードをくっつける
ajaxは本来非同期通信(画面遷移を伴わない通信)のための仕組みですが、情報を送るだけ送ってlocation.href
とかで画面が切り替わればformタグをsubmitした時と同じ動きをするのでは・・・?と思いやってみました。
(ajaxについては初心者目線でAjaxの説明がわかりやすかったです。)
$(() => {
$('.AButton').on('click', demo);
});
let demo = (event) =>{
let itemId = $(event.target).parents('tr').find("[name='itemId']").val();
let itemCount = $(event.target).parents('tr').find("[name='itemCount']").val();
let data = {
'itemtId': itemId,
'itemCount': itemCount
};
//非同期通信で情報送信
$.ajax({
type: 'POST',
url: '/sample/cart/add',
cache: false,
data: JSON.stringify(data),
datatype: 'json',
timeout: 10000,
contentType: 'application/json',
scriptCharset: 'utf-8'
})
.then((result) => {
console.log('ajax connection successed.');
}, () => {
console.error('Error:ajax connection failed.');
});
location.href='/sample/cart/add';
}
永遠にError:ajax connection failed.
が出続けました。
Controllerのアノテーションをいじったら情報自体は飛んだのですが、ページの表示ができずBadRequestと言われて諦めました。
多分ちゃんとajaxで情報を送信方法はあると思います・・・。
#試行2:jQueryのPOST()メソッドを使ってみる。
「POST通信」で調べていると出てきたので使ってみました。
が、うまくできないのでさらに調べてみるとpost()はajaxを簡略化したものだということが判明し、ajaxで無理だったことができるわけねー!と思いやめました。
#試行3:form部品をJavaScriptで生成してsubmit()メソッドで送信する
成功パターン。
方針を決めたときからこれが最後に残された方針だと思っていたのでうまくいって本当によかったです・・・。PHPだとPostLink()なんてメソッドもあるみたいですが。
とはいえJavaScriptの知識がすっからかんのため、半日かけて最終的に冒頭のコードにたどり着きました。
##解説コメントつきコード
<table> <!-- ECサイトの商品を一覧表示するテーブル -->
<tr>
<th>アイテム名</th><th>値段</th><th>個数</th><th>カート</th>
</tr>
<!-- タイムリーフ呼び出し。
itemsは別ファイルに記述してあり商品情報をリスト化してDBから取得している -->
<tr th:each="item: ${items}">
<!-- アイテム名を表示してあり、クリックするとIDに紐づいたアイテム紹介ページにリンク -->
<td><a href="/sample/item/{id}" th:href="@{/sample/item/{id}(id=${item.id})}" th:text="${item.itemName}"></a>
</td>
<!-- アイテムの値段を表示 -->
<td class="price" th:text="${item.price}" />
<td>
<!-- アイテムの購入個数カウンター。ここで選択された値をitemCountのパラメータとして渡す -->
<input type="number" name="itemCount" value="0" min="0" />
<!-- アイテム固有のID。値をitemIdのパラメータとして渡す -->
<input type="hidden" name="itemId" th:value="${item.id}" />
</td>
<td>
<!-- Aボタン押下とともにIDと個数をパラメータとして送信 -->
<button class="AButton">追加</button>
</td>
</tr>
</table>
$(() => {
//jQuery
//Abuttonクラスをクリックするとdemo関数が呼び出される
$('.AButton').on('click', demo);
});
//demo関数の内容
let demo = (event) =>{
//イベントが起きたtr要素に紐づけてitemIdとitemCountの値を取得し、変数に代入
let itemId = $(event.target).parents('tr').find("[name='itemId']").val();
let itemCount = $(event.target).parents('tr').find("[name='itemCount']").val();
//itemCountが0ならアラートを出す
if (itemCount == 0){
alert("個数が0です。");
return;
};
//DOMのcreateElementメソッドでformを生成
let f = document.createElement('form');
f.method = 'post'; //通信方法はPOST
f.action = '/sample/cart/add'; //情報の送り先URL
//innerHTMLメソッドは要素の内容を変更するプログラム。値を代入したり内容を空にすることができる。
//ここではそれぞれのvalueにさきほど定義した変数を指定
f.innerHTML = '<input name="itemCount" value='+ itemCount + '>' +
'<input name="itemId" value=' + itemId + '>';
//せっかく作ったform部品もhtmlのBodyに反映させなきゃ意味がないので
//appendでBody内に挿入
document.body.append(form);
//submit()で情報送信
form.submit();
}
終わり。
もっと簡潔にできるよ!ってのがありましたら教えてください。
余談ですがはじめはf.innerHTML
を2つ用意してしまっていたので、パラメータが一つしか飛ばない><なんで><となってました。
代入しなおしちゃったらそりゃそう。頑張ります。。。