🌟
WebFOCUSのRESTful APIでカスタム変数入力フォームを作成する(実践コード)
RESTful APIを使ってカスタム変数入力フォームを作成する(実践コード)
はじめに
RESTful APIを使ってカスタム変数入力フォームを作成するの続編です。
IBIRS_action=describeFex
を利用して実際の画面を作る実際のコードを書いてみました。
まだデザインやバリデーションなど改善の余地はありますが、describeFexで何が出来るか参考にしてください。
プログラム構成
シンプルにHTML + JavaScript + cssで作成しました。
- describeFexForm.css
- describeFexForm.html
- describeFexForm.js
プログラム全文
CSS
describeFexForm.css
body {
font-family: Arial, sans-serif;
background-color: #f4f4f9;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
h2{
text-align: center;
margin-bottom: 20px;
/* 背景をグラデーションにする */
background: linear-gradient(to right, #4a90e2, #9013fe);
color: white;
padding: 10px;
}
#form-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 500px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.form-group input,
.form-group select {
width: 100%;
padding: 8px;
font-size: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
button {
width: 100%;
padding: 10px;
font-size: 1rem;
color: white;
background-color: #007bff;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #0056b3;
}
HTML
describeFexForm.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>変数入力フォーム</title>
<link rel="stylesheet" href="describeFexForm.css">
</head>
<body>
<div id="form-container">
<!-- フォームがここに動的に生成されます -->
</div>
<script src="describeFexForm.js"></script>
<script>
// ページロード時に変数情報を取得してフォームを生成
document.addEventListener('DOMContentLoaded', async () => {
try {
const variables = await fetchVariableInfo(fullPath);
createVariableInputForm(variables);
} catch (error) {
console.error('Error initializing form:', error);
const formContainer = document.getElementById('form-container');
formContainer.textContent = 'エラーが発生しました。変数情報を取得できませんでした。';
}
});
</script>
</body>
</html>
JavaScript
1行目でfullPathをconstで指定しています。
実際には、クエリとして受け取るかFEXから-HTMLFORMで呼んで変数を使うかしましょう!
describeFexForm.js
const fullPath = "IBFS:/WFC/Repository/test/amptest.fex"; // FEXファイルのフルパス
const rsUrl = "http://localhost:8080/ibi_apps/rs"; // REST APIのURL
// describeFex APIを使用して変数情報を取得する関数
async function fetchVariableInfo(fullPath) {
const params = new URLSearchParams({
IBIRS_action: 'describeFex',
IBIRS_service: 'ibfs',
IBIRS_path: fullPath,
IBIRS_random: Math.random().toString(36).substring(2) // キャッシュ防止
});
const url = `${rsUrl}?${params.toString()}`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`変数情報の取得に失敗しました: ${response.status} ${response.statusText}`);
}
const xmlText = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
const rootObject = {};
const rootElement = xmlDoc.querySelector('rootObject');
if (rootElement) {
Array.from(rootElement.attributes).forEach(attr => {
rootObject[attr.name] = attr.value;
});
}
const bindingInfo = {};
const bindingInfoElement = xmlDoc.querySelector('bindingInfo');
if (bindingInfoElement) {
bindingInfoElement.querySelectorAll('entry').forEach(entry => {
const key = entry.querySelector('key')?.getAttribute('value');
const value = entry.querySelector('value')?.getAttribute('value');
if (key && value) {
bindingInfo[key] = value;
}
});
}
const amperMap = [];
const amperMapElement = xmlDoc.querySelector('amperMap');
if (amperMapElement) {
amperMapElement.querySelectorAll('entry').forEach(entry => {
const key = entry.querySelector('key')?.getAttribute('value');
const value = entry.querySelector('value');
const type = value?.querySelector('type')?.getAttribute('name');
if (type === 'defaultType' || type === 'unresolved') {
const variable = {
name: key,
type,
format: value?.getAttribute('format') || '',
description: value?.getAttribute('description') || '',
defaultValue: value?.querySelector('userDefValues > item')?.getAttribute('value') || '',
options: Array.from(value?.querySelectorAll('values > entry') || []).map(option => ({
key: option.querySelector('key')?.getAttribute('value'),
value: option.querySelector('value')?.getAttribute('value'),
})),
};
amperMap.push(variable);
}
});
}
return { rootObject, bindingInfo, amperMap };
} catch (error) {
console.error('変数情報の取得中にエラーが発生しました:', error);
throw error;
}
}
/**
* 変数情報を基にフォームを動的に生成する関数
* @param {Object} param0 - 変数情報とバインディング情報を含むオブジェクト
* @param {Array} param0.amperMap - 変数情報の配列
* @param {Object} param0.bindingInfo - バインディング情報
*/
function createVariableInputForm({ amperMap, bindingInfo }) {
console.log('変数情報を基にフォームを生成中:', amperMap);
const formContainer = document.getElementById('form-container');
formContainer.innerHTML = ''; // 既存のコンテンツをクリア
// bindingInfoにIBFS_displayNameが含まれている場合、見出しとして表示
if (bindingInfo && bindingInfo.IBFS_displayName) {
const heading = document.createElement('h2');
heading.textContent = bindingInfo.IBFS_displayName;
formContainer.appendChild(heading);
}
const form = document.createElement('form');
form.id = 'variable-input-form';
// 変数名に"prompt_COUNTRY"が含まれる場合、特別な処理を行う
// 固定値として"JAPAN", "ENGLAND", "ITALY"から選択可能なドロップダウンを生成
const countryVariable = amperMap.find(variable => variable.name.includes('prompt_COUNTRY'));
if (countryVariable) {
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
const label = document.createElement('label');
label.htmlFor = countryVariable.name;
label.textContent = "国を選択してください"; // 固定ラベルを使用
formGroup.appendChild(label);
const select = document.createElement('select');
select.id = countryVariable.name;
select.name = countryVariable.name;
const countries = ['JAPAN', 'ENGLAND', 'ITALY'];
countries.forEach(country => {
const optionElement = document.createElement('option');
optionElement.value = country;
optionElement.textContent = country;
select.appendChild(optionElement);
});
formGroup.appendChild(select);
form.appendChild(formGroup);
}
// 変数名が"prompt_YYMD"の場合、日付選択用のカレンダーを生成
const dateVariable = amperMap.find(variable => variable.name === 'prompt_YYMD');
if (dateVariable) {
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
const label = document.createElement('label');
label.htmlFor = dateVariable.name;
// descriptionがあればそれをラベルに使用、なければ固定ラベルを使用
label.textContent = dateVariable.description || "年月日を選択してください";
formGroup.appendChild(label);
const input = document.createElement('input');
input.type = 'date';
input.id = dateVariable.name;
input.name = dateVariable.name;
input.value = new Date().toISOString().split('T')[0]; // デフォルト値を今日の日付に設定
formGroup.appendChild(input);
form.appendChild(formGroup);
}
// 変数名が"prompt_YYM"の場合、年月選択用のカレンダーを生成
const monthVariable = amperMap.find(variable => variable.name === 'prompt_YYM');
if (monthVariable) {
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
const label = document.createElement('label');
label.htmlFor = monthVariable.name;
// descriptionがあればそれをラベルに使用、なければ固定ラベルを使用
label.textContent = monthVariable.description || "年月を選択してください";
formGroup.appendChild(label);
const input = document.createElement('input');
input.type = 'month';
input.id = monthVariable.name;
input.name = monthVariable.name;
input.value = new Date().toISOString().split('-').slice(0, 2).join('-'); // デフォルト値を今月に設定
formGroup.appendChild(input);
form.appendChild(formGroup);
}
// すべての変数をループ処理
amperMap.forEach(variable => {
// 変数名が"PROMPT_"で始まる場合はスキップ
if (variable.name.startsWith('prompt_')) {
return;
}
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
const label = document.createElement('label');
label.htmlFor = variable.name;
// descriptionがあればそれをラベルに使用、なければ変数名を使用
label.textContent = variable.description || variable.name;
formGroup.appendChild(label);
if (variable.options.length > 0) {
// オプションがある変数の場合、ドロップダウンを生成
const select = document.createElement('select');
select.id = variable.name;
select.name = variable.name;
variable.options.forEach(option => {
const optionElement = document.createElement('option');
optionElement.value = option.key;
optionElement.textContent = option.value;
select.appendChild(optionElement);
});
formGroup.appendChild(select);
} else {
// オプションがない変数の場合、テキスト入力を生成
const input = document.createElement('input');
input.type = 'text';
input.id = variable.name;
input.name = variable.name;
input.value = variable.defaultValue;
// formatの一文字目が'A'の場合、maxLengthをAを除いた数値にする
// 例えば、formatが'A8'の場合、maxLengthは8になる
if (variable.format && variable.format.charAt(0) === 'A') {
const maxLength = parseInt(variable.format.substring(1), 10);
input.maxLength = maxLength;
}
// formatの一文字目が'I','P','D'の場合、typeをnumberにする
// 例えば、formatが'I8'の場合、typeはnumberになる
else if (variable.format && (variable.format.charAt(0) === 'I' || variable.format.charAt(0) === 'P' || variable.format.charAt(0) === 'D')) {
input.type = 'number';
input.step = 'any'; // 数値入力に小数点を許可
}
formGroup.appendChild(input);
}
form.appendChild(formGroup);
});
// 実行ボタンを追加
const submitButton = document.createElement('button');
submitButton.type = 'submit';
submitButton.textContent = '実行';
form.appendChild(submitButton);
// フォーム送信時のイベントリスナーを追加
form.addEventListener('submit', async (event) => {
event.preventDefault();
const formData = new FormData(form);
const params = new URLSearchParams();
for (const [key, value] of formData.entries()) {
params.append(key, value);
}
const reportUrl = `${rsUrl}?IBIRS_action=run&IBIRS_service=ibfs&IBIRS_path=${encodeURIComponent(fullPath)}&${params.toString()}`;
window.open(reportUrl, '_blank');
});
formContainer.appendChild(form);
}
FEX
amptest.fex
-DEFAULTS DEF_1='AAA',DEF_2='BBB'
-DEFAULTH DEFH_1='ABC',DEFH_2='DEF'
-TYPE &DEF_2
-TYPE &DEFH_2
-TYPE &UND_1.I8.数字を入力.
-TYPE &UND_2.A8.
-TYPE &UND_3.A8.テキスト.
-*-TYPE &prompt_COUNTRY
-TYPE &prompt_YYMD
-TYPE &prompt_YYM
実行イメージ
ポイント解説
prompt_
で始まる変数を、固有の変数として入力または選択欄を作成しています。
このように、特定の変数名がプロンプトの対象となった場合に、デザインした入力フォームを利用できます。
prompt_
で始まらない変数のみ、formatやdescriptionを読み取って入力欄を作成しています。
他のデザイン方法
- カスタムフォームを利用するシステム全体で、変数名を全て定義する
- すべての変数に対応するHTMLを作成する
- describeFexで利用されていない変数に対応する要素を非表示または削除する
- 定義された変数以外は利用しない
という引き算の発想であれば、HTML + CSSでデザインされたフォームを作成できます。
Discussion