ラボ > 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);
});