記事の内容
非同期での送信ボタン制御を実装する中で色々と躓いたため、備忘録も兼ねて記録として投稿しました。
今回は、XMLHttpRequesを使用して JavaScript から非同期的にデータを送信する方法をご紹介いたします。
完成形
完成形のコード
modelオプションに設定している@pdf
は、コントローラで定義しているインスタンス変数になります。
gem
はwicked_pdf
を使用しており、ここではPDFファイルを生成する処理を記述しております。
※ コントローラ、フォーム部分は一部割愛しております。
<%= form_with model: @pdf, url: "#{mypage_history_path}?order_id=#{@order.id}", local: true do |f| %>
<button type="submit", id='pdf-btn', class="btn btn-primary btn-lg btn-receipt">領収書</button>
<% end %>
<script>
$('form').submit(function(){
element = $("#pdf-btn")[0]
element.disabled = true;
element.innerText = "ダウンロード中";
const xhr = new XMLHttpRequest();
xhr.open('GET', this.action);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
if (this.status === 200) {
const blob = new Blob([this.response], {type:"application/pdf"});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "<%="ryoshusyo-No#{@order.id}.pdf"%>";
link.click();
}
element.disabled = false;
element.innerText = "領収書";
};
xhr.send(f);
return false;
});
</script>
実装開始
ここから実装内容を説明していきます。
①多重送信防止の対処
まず初めに、送信ボタンに付与したIDとマッチする要素を取得して変数に格納しています。
その後、多重送信防止のため、disabled
を付与して非活性にし、ボタンの表記も変更しています。
element = $("#ryosyusyo-android-pdf-btn")[0]
element.disabled = true;
element.innerText = "ダウンロード中";
②XMLHttpRequest
オブジェクトの生成
次にブラウザとサーバ間で通信を行うためにXMLHttpRequest
オブジェクトを生成しています。
const xhr = new XMLHttpRequest();
③リクエスト方法を設定
HTTPメソッドとアクセスする場所を指定します。
xhr.open('GET', this.action);
第1引数には、使用する Httpリクエストメソッドを指定します。
今回は、データを取得するだけなのでGET
メソッドを指定しています。
第2引数には、リクエスト先のURLアドレスを指定します。
ここではthis.action
でform
のaction
属性値であるURLアドレスを指定しています。
④データ型を指定
レスポンスに含まれているデータの型を指定します。
xhr.responseType = 'arraybuffer';
今回は、バイナリデータを含むJavaScript
のArrayBuffer
をデータ型として指定しています。
⑤PDFファイルのダウンロード
ダウンロードするための処理は下記コードになります。
xhr.onload = function() {
if (this.status === 200) {
const blob = new Blob([this.response], {type:"application/pdf"});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "<%="ryoshusyo-No#{@order.id}.pdf"%>";
link.click();
}
element.disabled = false;
element.innerText = "領収書";
};
1, onload
イベント
onload
メソッドを使用してレスポンスを受け取った後に function 内の処理を実行させています。
xhr.onload = function() {
2, Blob
オブジェクトを生成
Blob
とは、Binary Large Object
の略でバイナリデータを扱うためのクラスです。
そのBlob
を使用するとユーザーに動的にファイルをダウンロードさせることが可能になります。
そのため、まず初めにBlob
オブジェクトを生成しています。
if (this.status == 200) {
const blob = new Blob([this.response], {type:"application/pdf"});
第1引数には、データの配列を設定します。
今回は、xhr
変数のresponse
に格納されているArrayBuffer
の配列データを渡しています。
第2引数には、ファイルタイプを指定します。
今回は、PDF形式のファイルをダウンロードするため、type:"application/pdf"
としています。
3, ダウンロードリンクを作成
次にBlob
データをダウンロードする処理を実装していきます。
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "<%="ryoshusyo-No#{@order.id}.pdf"%>";
link.click();
-
createElement('a')
でリンクタグを生成し、link
変数に格納しています。 -
URL.createObjectURL()
を使用し、引数に先ほど生成したblob
オブジェクトを渡すことでオブジェクトURLを生成し、href属性値に格納しています。 - ダウンロード属性にファイル名を設定しています。
今回の場合、ファイル名は「ryoshusyo-No{注文番号}.pdf」となります。 - 最後に先ほど生成したリンクタグを
click
イベントで発火させることで、PDFファイルをダウンロードしています。
4, 元の状態に戻す
ダウンロードボタンをデフォルトの状態に戻すための記述を行っています。
element.disabled = false;
element.innerText = "領収書";
⑥HTTPリクエストを送信
XMLHttpRequest
のsend
メソッドを使用してajax
通信を実行しています。
xhr.send();
ここで、③で設定したHTTPメソッドとリクエスト先URLをもとに、リクエスト送信を行っています。
設定内容 => xhr.open('GET', this.action);
⑦イベント伝播を制御
最後に親要素へのイベント伝播を止めるための記述を行っています。
return false;
終わりに
恥ずかしながら今までXMLHttpReques
を利用したことがなく、初見は何がなんだか分からない状態でした、、
今回記事にしたことで理解は深まりましたが、不明点も多々あり、、。
今後も学びを大切に日々アウトプットしていきます!
参考記事