以前はFetch APIを使って外部のJSONデータを読み込みました。先日、それをAsync/Await構文に書き換えてみました。これらはあくまでデータの読み込みだけでしたが、今回はFetch APIを使ってデータをPOST送信をしてみます。Formタグからの送信との挙動の比較もしてみました。それではいきましょう!
【目次】
※参考:前回記事
【JSON】外部JSONファイル化したはっぴいえんどアルバム情報をFetch APIでDOMに読み込む - クモのようにコツコツと
※参考:※参考:ネイティブJSでいろいろやってみたシリーズ
qiita.com
今回作ったもの
今回作ったものはこちら。
上がformタグから送信するフォーム、下がFetch APIから送信するコード
※参考:プレビュー
FetchでGET送信
※参考:ソースコード
GitHub - ryo-i/postFetch: Fetch APIでGET送信してみる
Fetch APIからデータが送れる!
ふと、Fetch APIから外部からのデータを読み込むだけでなく、こちらからデータを送信できないかな、と気になった。
調べたところfetch()
メソッドの第二引数にoptionを設定し、そこにbody
キーとして送信することができそう。
fetch(url , { method: "POST", body: data }).then( // 続き処理…
- 第一引数に送信先のURLを入れる
- 第二引数は連想配列でオプションを設定する
method
オプションの値はPOST
body
オプションの値に送信したいデータを入れる
GETとPOSTの違いはこちら。GETの場合はURLの後ろにパラメータとして付く。POSTは配列に入れて内部的に送信する。
※参考:フォームPHPはじめの一歩($_GETと$_POSTの違い) - クモのようにコツコツと
body
に設定するデータはformのinput
タグに入力した値にしたい。FormData()
メソッドを使うとformのデータを取得することができるようだ。
let form = new FormData(form);
new
でインスタンスを生成し、引数で紐付けるフォームを指定する。
※参考:FormData() - Web API | MDN
インスタンスについてはこちら。組み込みメソッドの中身を直接修正させないために生成する。
※参考:【JS】newとプロトタイプとクラス - クモのようにコツコツと
なお、送信先のURLはJSON PlaceHolderのURLにしてみた。*1
※参考:JSONPlaceholderを試してみた - A1 Blog
こちらの「post」ページがPOST送信専用に作られているページで、エラーにならなかった。
※参考:https://jsonplaceholder.typicode.com/posts/
HTMLコード
上記の方法で作ってみたコードがこちら。
※参考:ソースコード
GitHub - ryo-i/postFetch: Fetch APIでGET送信してみる
まずはHTMLコード
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>FetchでPOST送信</title> </head> <body> <section> <h1>あなたのお名前なんて〜の?</h1> <p>Formで答える</p> <form method="post" action="https://jsonplaceholder.typicode.com/posts/"> <input type="text" name="name" size="30" maxlength="30"> <input type="submit" value="送信"> </form> <p>Fetchで答える</p> <form class="fetchForm"> <input type="text" name="name" size="30" maxlength="30" class="name"> <input type="button" value="送信" class="btn"> </form> </section> <script src="js/post_fetch.js"></script> </body> </html>
- 上のフォームは
method
をpost
に、action
はJSON PlaceHolderのPOST用のURLに - 上のフォームの送信ボタンは
type
をsubmit
に - 下のフォームはclass名
fetchForm
のみ - 下のフォームの送信ボタンは
type
をbutton
に
上のフォームはsubmit
ボタンを押すとformタグのpost
メソッドが動いてaction
のURLに入力データが送られる。
下のフォームは送信ボタンのtype
がボタンだし、form
タグにもclass名しかないので何も起こらない。
JSコード
JSコード全体
下のフォームはJSコードで下記のFetch APIの処理によって入力データが送られる。
post_fetch.js
const fetchForm = document.querySelector('.fetchForm'); const btn = document.querySelector('.btn'); const url = 'https://jsonplaceholder.typicode.com/posts/'; const postFetch = () => { let formData = new FormData(fetchForm); for (let value of formData.entries()) { console.log(value); } fetch(url, { method: 'POST', body: formData }).then((response) => { if(!response.ok) { console.log('error!'); } console.log('ok!'); return response.json(); }).then((data) => { console.log(data); }).catch((error) => { console.log(error); }); }; btn.addEventListener('click', postFetch, false);
ボリュームがあるのでブロックごとに解説する。
DOM取得とURL指定
const fetchForm = document.querySelector('.fetchForm'); const btn = document.querySelector('.btn'); const url = 'https://jsonplaceholder.typicode.com/posts/';
- 変数
fetchForm
で下のformタグ.fetchForm
のDOMを取得 - 変数
btn
で下の送信ボタン. btn
のDOMを取得 - 変数
url
でJSON PlaceHolderのPOST用のURLを設定
postFetch()の大枠
変数postFetch
はアロー関数でpostFetch()
の処理が定義されている。
大枠としては下記の様な内容
const postFetch = () => { let formData = // フォームの入力データを取得 fetch( // APIを叩いてデータを送信 ); };
- 変数
formData
でフォームの入力データを取得 fetch()
メソッドでAPIを叩いてデータを送信
フォームデータを取得
let formData = new FormData(fetchForm); for (let value of formData.entries()) { console.log(value); }
formData
でFormData()
インスタンスを生成しfetchForm
の入力データを取得- for-of文でフォームデータの配列を
value
に取得してループし、コンソールに表示する
今回、APIがJSON PlaceHolderのダミーページのため、フォームデータを送っても正しいレスポンスを得られなかった。
そのため、FormData()
でフォームデータを取得した時点でどんな値が取れたかをコンソールで確認した。
コンソールはconsole.log(fetchForm)
と単純に入れても複雑な階層のオブジェクトがまるまる入ってしまい、値の確認が困難だった。
こちらの記事を参考にfor-of文を使って連想配列形式で表示した。
※参考:FormData オブジェクトの中身が見たい! - Qiita
for-of文は連想配列形式をループする制御構造。
※参考:for...of - JavaScript | MDN
FormData()
につなげているentries()
メソッドはフォームデータを連想配列形式に変換する。
The FormData.entries() method returns an iterator allowing to go through all key/value pairs contained in this object. The key of each pair is a USVString object; the value either a USVString, or a Blob.
FormData.entries()メソッドは、このオブジェクトに含まれるすべてのキーと値のペアを通過できるイテレータを返します。 各ペアのキーはUSVStringオブジェクトです。 USVStringまたはBlobのいずれかの値。
※参考:FormData.entries() - Web APIs | MDN
APIを叩いてデータを送信
次にFetch APIでAPIにデータを送信する。
fetch(url, { method: 'POST', body: formData }).then((response) => { if(!response.ok) { console.log('error!'); } console.log('ok!'); return response.json(); }).then((data) => { console.log(data); }).catch((error) => { console.log(error); });
fetch()
の第一引数でurl
のAPIにアクセスfetch()
の第二引数でオプションを設定
method
をPOST
にしてPOST送信
body
にformData
を送るthen()
でつなげてresponse
を取得
もしレスポンスが得られなかったらコンソールにerror!
と表示
レスポンスが得られたらコンソールにok!
と表示
response.json()
でレスポンスをJSON形式にthen()
でつなげてdata
を取得し、コンソールに表示catch()
でAPIのアクセスがエラーの時にコンソールにエラーコードを表示
catch()
はAPIのアクセスエラーしか感知しないので、それとは別に!response.ok
でレスポンスが得られなかった場合のエラーを感知する。
これでフォームデータ送信の通信結果がコンソールで一通り確認できる。
クリックイベント
最後にクリックイベントを設定
btn.addEventListener('click', postFetch, false);
btn
をクリックした時にpostFetch()
関数を実行する
ブラウザ動作確認
FormでPOST送信
こちらのGitHub Pagesで動作確認ができる。
※参考:FetchでGET送信
デベロッパーツールの「Network」タブを開いておく。
まずは上のフォームに値を入力する。上のフォームはForm送信される方。「孫悟空」と入れて送信ボタンを押すと…
別ページに遷移する。JSON PlaceHolderのPOST用のダミーページ。
デベロッパーツール「Network」タブで「posts」を選択。Headers情報が見れる。
Headers情報の詳細についてはこちらを参照
※参考:ExpressとJSフレームワーク(React、Vue、Angularなど)との関係について調べたこと - クモのようにコツコツと
Headers情報の一番下までスクロールすると…
おお、「Form Data」で「name」キーの「孫悟空」という値が送信されている!
Fetch APIでPOST送信
今度は下のフォームでFetch APIから送信してみる。「ベジータ」と入れて送信ボタンを押すと…
今後はページが遷移しない!「Network」タブには「posts」が増えている。
「posts」を開くとHeaders情報が見れる
下にスクロールすると「Form Data」で「name」キーの「ベジータ」が送信されている!
コンソールパネルを開く。
コンソールが3行表示されている!
- 1行目:
FormData
を取得した直後のJSON形式の値。name
キーのベジータ
と正しく表示されている。 - 2行目:
ok!
はレスポンスの取得に成功! - 3行目:レスポンスのJSON形式のJSON形式の値。
id
キーの101
とJSON PlaceHolderの初期値しか取れていない。
Fetch APIで再送信
再度、下のフォームからFetch APIで送信してみる。今後は「フリーザ」と入れてみる。
Headers情報。Form Dataでnameキーから「フリーザ」が送信されている。
コンソール。「フリーザ」のフォームデータが表示されている。
最後に:次はDB、API側を体験したい!
ということでFetch APIでformタグと同じPOST送信に成功しました!ページ遷移やリロードしないのでユーザーのストレスが少ないのがいいですね。うまく使えばformタグやinputタグを使わなくてもPOST送信ができそうな気がします♪いいねボタンのオンオフとかね。
今回は送信先がJSON PlaceHolderのダミーページだったため、正しいレスポンス値を得ることができませんでした。
Node.jsやPHPなどを使ってバックエンド側のAPI部分を自作できると、意図通りのレスポンスを実現できそうです。
また、Firebaseを使うとAPI側の設定をブラウザ上で実現できるそうにも思っています。
次回はその前段階として、データベース自体の値の変更処理を体験したく思います。
それではまた!
※参考:※参考:ネイティブJSでいろいろやってみたシリーズ
qiita.com
*1:GitHub pagesのセキュリティ設定上、送信先を表示しているページ自体にしてもうまく動かなかった