はじめに
プリザンターの管理画面にある「計算式」タブでは、計算式を入力欄へ直接記述できます。しかし、この入力欄は単一行のテキストボックスなので、拡張計算式のように処理が長くなると全体を見渡しにくくなります。
この記事では、本体を改修せずに拡張スクリプトと拡張スタイルだけで計算式の入力欄を複数行化する方法を紹介します。通常計算式は改行を含む式を正しく分割できないため、計算方法が「拡張」の場合だけ複数行に切り替えます。
バージョン 1.5.7.0 を対象にしています
現在の実装を見てみる
計算式欄は単一行のテキストボックス
計算式の設定ダイアログは SiteUtilities.FormulaDialog で生成されています。計算式と条件外の計算式には、どちらも FieldTextBox が使用されています。
.FieldTextBox(
controlId: "Formula",
controlCss: " always-send",
fieldCss: "field-wide",
labelText: Displays.Formulas(context: context),
text: (formulaSet.CalculationMethod == FormulaSet.CalculationMethods.Default.ToString()
|| string.IsNullOrEmpty(formulaSet.CalculationMethod))
? formulaSet.Formula?.ToString(ss, notUseDisplayName: formulaSet.NotUseDisplayName)
: FormulaBuilder.UpdateColumnDisplayText(
ss: ss,
formulaSet: formulaSet).FormulaScript,
validateRequired: true)
FieldTextBox は内部で TextBox を呼び出すため、最終的には <input type="text"> が出力されます。CSSで高さを広げても、<input> 自体は改行を入力できません。
そこで、ダイアログを開いたタイミングで次のように要素を置き換えます。
置き換えても保存できる理由
計算式欄には always-send クラスが設定されています。プリザンターは送信時に .always-send を持つコントロールを走査し、$p.setData で値を収集します。
} else {
$form.find('.always-send,[data-always-send="1"]').each(function () {
var $control = $(this);
if (
!(
$control.attr('data-readonly') === '1' &&
$control.attr('id').indexOf($p.tableName() + '_') === 0
)
) {
$p.setData($(this));
}
});
}
そのため、元の id、class、data-* 属性を <textarea> に引き継げば、保存処理を追加実装する必要はありません。
拡張機能を実装してみよう
今回は次の2ファイルを使用します。
| ファイル | 役割 |
|---|---|
ExtendedScripts/FormulaMultiline.js |
<input> を <textarea> へ置換 |
ExtendedStyles/FormulaMultiline.css |
高さやリサイズなどの見た目を調整 |
拡張スクリプト
次のファイルを App_Data/Parameters/ExtendedScripts/ に配置します。
$(function () {
$(document).on('dialogopen', function (e) {
if (e.target.id !== 'FormulaDialog') return;
updateFormulaControls();
});
$(document).on('change', '#FormulaCalculationMethod', function () {
updateFormulaControls();
});
function updateFormulaControls() {
var tagName = $('#FormulaCalculationMethod').val() === 'Extended'
? 'textarea'
: 'input';
$('#FormulaDialog #Formula, #FormulaDialog #FormulaOutOfCondition')
.each(function () {
replaceControl(this, tagName);
});
}
function replaceControl(control, tagName) {
if (control.tagName.toLowerCase() === tagName) return;
var replacement = document.createElement(tagName);
Array.prototype.forEach.call(control.attributes, function (attr) {
if (attr.name !== 'type' && attr.name !== 'value') {
replacement.setAttribute(attr.name, attr.value);
}
});
replacement.value = control.value;
if (tagName === 'textarea') {
replacement.rows = 10;
replacement.classList.add('formula-multiline');
} else {
replacement.type = 'text';
replacement.classList.remove('formula-multiline');
}
control.replaceWith(replacement);
}
});
計算式ダイアログはサーバから動的に内容を取得して表示します。そのため、ページ読み込み時ではなく、jQuery UIの dialogopen イベントで置換処理を実行しています。また、#FormulaCalculationMethod の変更も監視し、「拡張」なら <textarea>、「通常」なら <input> に切り替えます。
対象となる入力欄は次の2つです。
| ID | 内容 |
|---|---|
#Formula |
条件に一致した場合の計算式 |
#FormulaOutOfCondition |
条件に一致しなかった場合の計算式 |
type と value 以外の属性をそのまま複製し、現在の値は replacement.value に設定します。これにより、入力必須の検証属性や always-send クラスも維持されます。
また、既に目的の要素へ置換されている場合は処理をスキップします。ダイアログを繰り返し開いたり、計算方法を切り替えたりしても、要素が二重に生成されることはありません。
拡張スタイル
次のファイルを App_Data/Parameters/ExtendedStyles/ に配置します。
#FormulaDialog .formula-multiline {
box-sizing: border-box;
width: 100%;
min-height: 12rem;
resize: vertical;
overflow-x: auto;
overflow-wrap: normal;
white-space: pre;
line-height: 1.5;
font-family: Consolas, "Courier New", monospace;
tab-size: 4;
}
resize: vertical を指定しているため、入力欄の下端をドラッグして好みの高さに変更できます。white-space: pre と overflow-wrap: normal により、長い行を勝手に折り返さず横スクロールで確認できます。
ファイルを配置したら、プリザンターを再起動して拡張機能を読み込みます。
動作を確認してみる
サイトの管理画面から「計算式」タブを開き、「新規」または既存の計算式を選択します。計算方法を「拡張」にすると計算式欄が複数行のテキストエリアになり、Enter キーで改行できれば成功です。「通常」に戻すと、元の単一行入力へ戻ります。
例えば、拡張計算式を次のように記述できます。
(function () {
const unitPrice = model.NumA || 0;
const quantity = model.NumB || 0;
const discountRate = model.NumC || 0;
return unitPrice * quantity * (1 - discountRate);
})()
入力欄を縦方向に広げられるため、変数の準備、条件分岐、戻り値を分けて記述する計算式も確認しやすくなります。
通常計算式と拡張計算式の違い
今回の実装で複数行化するのは拡張計算式だけです。通常計算式を対象外にしている理由を確認してみましょう。
| 計算方法 | 今回の表示 | 改行を含む式の保存 |
|---|---|---|
| 通常 | 単一行 | 非対応 |
| 拡張 | 複数行 | 対応 |
通常計算式は、保存時に文字列を分割して Formula オブジェクトへ変換します。
private static IEnumerable<string> Parts(string formula)
{
return formula?
.Replace("(", "( ")
.Replace(")", " )")
.Split(' ')
.Select(o => o.Trim())
.Where(o => o != string.Empty);
}
この処理は半角スペースで式を分割してから各要素を Trim しています。改行そのものを区切りとして扱わないため、改行位置によっては演算子と項目名が同じ要素に入り、無効な計算式になります。また、保存できた場合も、再度ダイアログを開くと Formula.ToString で組み立て直された一行の式が表示されます。
一方、拡張計算式は入力文字列を FormulaScript に保存します。
else
{
formulaSet.Formula = null;
formulaSet.OutOfCondition = null;
formulaSet.FormulaScript = formula;
formulaSet.FormulaScriptOutOfCondition = formulaSet.Condition != null
? outOfCondition
: null;
formulaSet = UpdateColumnDisplayText(
ss: ss,
formulaSet: formulaSet);
}
そのため、入力した改行を安全に保存後も維持できる計算方法は「拡張」です。
カスタマイズ
初期表示の高さを変更する
初期状態では10行分の高さにしています。もう少し広くしたい場合は rows を変更します。
- textarea.rows = 10;
+ textarea.rows = 16;
CSSの最小高さも合わせて変更できます。
- min-height: 12rem;
+ min-height: 20rem;
長い行を折り返して表示する
横スクロールではなく、入力欄の幅に合わせて折り返したい場合は次のように変更します。
- overflow-wrap: normal;
- white-space: pre;
+ overflow-wrap: anywhere;
+ white-space: pre-wrap;
まとめ
プリザンターの管理画面にある計算式入力欄を、拡張スクリプトと拡張スタイルだけで複数行化しました。
-
dialogopenイベントで計算式ダイアログの表示を検知 - 計算方法が「拡張」の場合だけ、
#Formulaと#FormulaOutOfConditionを<textarea>へ置換 - 計算方法を「通常」に戻した場合は、単一行の
<input>へ復元 - 元の属性を引き継ぐことで、入力検証と保存処理を維持
- 拡張スタイルで高さ変更、等幅フォント、横スクロールに対応
- 通常計算式のパーサーへ改行を渡さず、拡張計算式だけで改行を保持
計算式が長くなって横スクロールでの編集がつらくなってきた場合は、試してみてください。