ラボ > Javascript関連:ajax、form、イベント関連
js FetchAPIのメモ
ajaxじゃなくFetchAPI / jqueryを使わずにネイティブのJavascriptでいけるのがFetchAPIらしい。
作成日:2023-08-07, 更新日:2023-09-27
メモ
jqueryを使わずにネイティブのJavascriptでいけるのがFetchAPIらしい。
…ajaxもいけなかったっけ?
サンプル: 叩き台 / 2023-09-26
ファイル投稿も対応
class FetchHandler { constructor() { this.send_type_json = 'json'; this.send_type_form = 'form'; this.send_type_file = 'form_withFile'; this.request_type = this.send_type_form; this.request_method = 'POST'; this.request_header = { 'Content-Type': 'application/json', }; this.request_body = ''; this.timeout = 15 * 1000; /* その他、初期化 */ } connectionError(http_code) { if (http_code === 200) { return; } let message = ''; if (http_code === 404) { message = 'Not Found'; } else if (http_code === 500) { message = 'Internal Server Error'; } else { message = 'Unknown Error'; } // alert(message); throw new Error(message); } handleError(status) { /* エラー処理 */ } handleSuccess(data) { /* 成功時の処理 */ } convertRequestData_fromJson(request_data) { return JSON.stringify(request_data); } setRequest(request_data) { switch (this.request_type ) { case this.send_type_json: this.request_headers = { 'Content-Type': 'application/json', }; this.request_body = this.convertRequestData_fromJson(request_data); break; case this.send_type_form: this.request_headers = { 'Content-Type': 'application/x-www-form-urlencoded', // 'Content-Type': 'multipart/form-data', }; this.request_body = request_data; break; case this.send_type_file: this.request_headers = { 'Content-Type': 'multipart/form-data', }; this.request_body = request_data; break; default: throw new Error('Unknown Error'); } } getFormValues(formElement) { let flg_formData = false; let formData; if ( this.request_type != this.send_type_json ) { flg_formData = true; formData = new FormData(formElement); } else { formData = {}; } formElement.querySelectorAll('input, select, textarea').forEach(inputElement => { const name = inputElement.name; let value; if (inputElement.type === 'checkbox') { const checkboxValues = []; formElement.querySelectorAll('input[name="${name}"]:checked').forEach(checkbox => { checkboxValues.push(checkbox.value); }); value = checkboxValues; } else if (inputElement.type === 'radio') { if (inputElement.checked) { value = inputElement.value; } } else if (inputElement.type === 'file') { if ( flg_formData ) { const fileInput = inputElement; const file = fileInput.files[0]; formData.append(name, file, file.name); } } else { value = inputElement.value; } if (value !== undefined) { if ( flg_formData ) { formData.append(name, value); } else { formData[name] = value; } } }); return formData; } async run(url, request_data) { try { const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => reject(new Error('Request timed out')), this.timeout); }); this.setRequest(request_data); const response = await Promise.race([fetch(url, { method: this.request_method, // headers: this.request_headers, // ← コイツがいるとPHPの$_POSTが配列じゃなく文字列になってしまう…fetchに任せるのが良さげっぽい body: this.request_body, }), timeoutPromise]); this.connectionError(response.status); const data = await response.json(); if (data.message !== undefined && data.message !== '') { this.handleError(data.message); } else { this.handleSuccess(data); } } catch (error) { console.error('ERROR:', error); } } } document.addEventListener('DOMContentLoaded', function() { const fetchHandler = new FetchHandler(); // インスタンス化 document.querySelector('#form .submit').addEventListener('click', function() { // メソッド書換え fetchHandler.request_type = this.send_type_file; fetchHandler.request_method = 'POST'; // fetchHandler.convertRequestData_fromForm = function(request_data) { // const formData = new FormData(request_data); // formData.append('abc', 'いろは'); // formData.append('def', 'にほへ'); // return formData; // } const url = 'your_php_file.php'; const formElement = document.getElementById('form'); const request_data = fetchHandler.getFormValues(formElement); // request_data.abc = 'いろは'; // request_data.def = 'にほへ'; fetchHandler.run(url, request_data); }); });
サンプル: 叩き台 / 2023-08-07
各メソッドを必要に応じて変更したいので、色々としておく。実際に動かしていないので動作するか不明。
class FetchHandler { constructor() { this.request_type = 'json'; this.request_method = 'POST'; this.request_header = { 'Content-Type': 'application/json', }; this.request_body = ''; this.timeout = 15 * 1000; /* その他、初期化 */ } connectionError(http_code) { if (http_code === 200) { return; } const message; if (http_code === 404) { message = 'Not Found'; } else if (http_code === 500) { message = 'Internal Server Error'; } else { message = 'Unknown Error'; } // alert(message); throw new Error(message); } handleError(status) { /* エラー処理 */ } handleSuccess(data) { /* 成功時の処理 */ } convertRequestData_fromJson(request_data) { return JSON.stringify(request_data); } convertRequestData_fromForm(request_data) { return new FormData(request_data); } setRequest(request_data) { if (this.request_type == 'json') { this.request_headers = { 'Content-Type': 'application/json', }; this.request_body = this.convertRequestData_fromJson(request_data); } else if (this.request_type == 'form') { this.request_headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; this.request_body = this.convertRequestData_fromForm(request_data) } else { throw new Error('Unknown Error'); } } getFormValues(formElement) { const formValues = {}; formElement.querySelectorAll('input, select, textarea').forEach(inputElement => { const name = inputElement.name; let value; if (inputElement.type === 'checkbox') { const checkboxValues = []; formElement.querySelectorAll('input[name="${name}"]:checked').forEach(checkbox => { checkboxValues.push(checkbox.value); }); value = checkboxValues; } else if (inputElement.type === 'radio') { if (inputElement.checked) { value = inputElement.value; } } else { value = inputElement.value; } if (value !== undefined) { formValues[name] = value; } }); return formValues; } async run(url, request_data) { try { const timeoutPromise = new Promise((resolve, reject) => { setTimeout(() => reject(new Error('Request timed out')), this.timeout); }); this.setRequest(request_data); const response = await Promise.race([fetch(url, { method: this.request_method, headers: this.request_headers, body: this.request_body, }), timeoutPromise]); this.connectionError(response.code); const data = await response.json(); if (data.status === 'error') { this.handleError(data.error); } else { this.handleSuccess(data); } } catch (error) { console.error('エラー:', error); } } } const fetchHandler = new FetchHandler(); // インスタンス化 // イベント処理 document.querySelector('#form .submit').addEventListener('click', function() { // メソッド書換え // fetchHandler.request_type = 'form'; // fetchHandler.request_method = 'GET'; // fetchHandler.convertRequestData_fromForm = function(request_data) { // const formData = new FormData(request_data); // formData.append('abc', 'いろは'); // formData.append('def', 'にほへ'); // return formData; // } const url = 'your_php_file.php'; const formElement = document.getElementById('form'); const request_data = this.getFormValues(formElement); // request_data.abc = 'いろは'; // request_data.def = 'にほへ'; fetchHandler.run(url, request_data); });