背景
- Service Catalog で使用できる Multi-row variable set (以降MRVS) が Order Guide 経由だと正常に動かない。
- Desktop UIで事象が出る。Service Portalでは問題なし。
- Order Guide 経由ではなく、Service catalog 単体で試すと正常に動作する。
- 事象が出るのは Madrid Patch6。
- NewYorkにUpgrade済みのPersonal instanceでは再現しない。なので将来のリリースでは直っているはず。
問題の詳細
-
MRVSの "Add" ボタン押下後、"Add Row" ポップアップ画面にて値を入力、ポップアップ画面の "Add" ボタンを押下しても、元画面に反映されない。ポップアップが閉じない。
-
より詳細に見ていくと、Order guide 経由の場合と Service Catalog 単体の場合で、ポップアップ画面内の Variable の ID が異なることが分かる。(サーバサイドでの処理ミス?)
-
IDのPrefixが異なるせいで、
getVariablesFromDialog
メソッドで要素とその値を正しく取れていないことが分かる。(これを無理やり直す)
無理やり直す
-
getVariablesFromDialog
メソッドがいけてないのが分かったので(本当はポップアップ画面がいけてないんだけど)、無理やり以下の様に上書きする。 -
getVariablesFromDialog
の処理修正版を作り、TableVariableService
のrenderTableDialogForm
メソッド内でiFrameElement.getVariablesFromDialog
を上書きしてしまう。 -
getVariablesFromDialog
の修正内容は「ここから~ここまで」参照。 - onLoad の Catalog Client Script、Desktop UI 二のみ適用、そして"Isolate script" のチェックは外しています。
function onLoad() {
if(!gel('sysparm_guide')) {
return;
}
TableVariableService.renderTableDialogForm = function(tableVariable, action, nodeId) {
var dialog = new CatalogModalForm(tableVariable.getLabel(), 'catalog_table_variable_dialog');
dialog.setSize(900);
var dialogTitle = (action == "create") ? getMessage("Add Row") : getMessage("Edit Row");
dialog.setTitle(dialogTitle);
dialog.setPreference("sysparm_table_dialog_action", action);
dialog.setPreference("sysparm_table_variable_id", tableVariable.getId());
dialog.setPreference("sysparm_catalog_item_id", tableVariable.getCatalogItem());
dialog.setPreference("sysparm_source_table", tableVariable.getSourceTable());
dialog.setPreference("sysparm_source_id", tableVariable.getSourceId());
if (action == 'update') {
dialog.setPreference("sysparm_active_node_id", nodeId ? nodeId : "");
dialog.setPreference("sysparm_active_node_data", tableVariable.getRowData(nodeId));
}
dialog.setBackdropStatic('true');
dialog.setPreference('autoFocus', true);
dialog.setPreference('focusTrap', true);
var callback = function(dialogObject) {
var iFrameElement = gel('dialog_frame') ? gel('dialog_frame').contentWindow : null;
if (iFrameElement) {
iFrameElement.catalog_table_dialog = dialogObject;
iFrameElement.TableVariableService = this.TableVariableService;
iFrameElement.TableVariable = this.TableVariable;
iFrameElement.TableVariableRow = this.TableVariableRow;
iFrameElement.tableVariableCache = this.tableVariableCache;
// inline frame の Global メソッドを上書きしてしまう。
// iFrameElement を bind するのをお忘れなく。
iFrameElement.getVariablesFromDialog = getVariablesFromDialogUpdated.bind(iFrameElement);
iFrameElement.g_form.setReadOnly('varCityNameAdding', true);
iFrameElement.g_form.setReadOnly('varFloorAdding', true);
iFrameElement.g_form.setReadOnly('varCompanyAdding', true);
iFrameElement.g_form.setReadOnly('varDepartmentAdding', true);
iFrameElement.g_form.setReadOnly('varRemarksAdding', true);
} else {}
}.bind(window);
dialog.setOnloadCallback(callback);
dialog.render();
return dialog;
}.bind(window);
function getVariablesFromDialogUpdated( /* TableVariable */ tableVariable) {
if (!tableVariable) return;
var namespace = tableVariable.getNameSpace();
var optionsElements = this.document.getElementById('table_variable_sequence').value.split(',');
var variables = {};
for (i = 0; i < optionsElements.length; i++) {
var id = optionsElements[i];
var nameSpaceId = namespace + id;
// for reference displayBox contains displayValue
var displayBox = this.document.getElementById('sys_display.' + nameSpaceId);
var hiddenBox = this.document.getElementById(nameSpaceId);
// ここから
// displayBoxが取れてなかったら、IDを変えて再取得 (reference field などの display valueっぽい)
if (!displayBox){
displayBox = this.document.getElementById('sys_display.ni.QS' + id);
}
// hiddenBoxが取れてなかったら、IDを変えて再取得 (input の value っぽい)
if (!hiddenBox) {
hiddenBox = this.document.getElementById('ni.QS' + id);
}
// ここまで
if (!hiddenBox) continue;
var colElement = {
value: ''
};
if (hiddenBox.type == 'radio' || (hiddenBox.hasAttribute('type') && hiddenBox.getAttribute('type') == 'radio')) {
var selectedOption = $(nameSpaceId).querySelector("input[name='" + nameSpaceId + "']:checked");
if (selectedOption) {
colElement.value = selectedOption.value;
// If value is empty don't honor labels
if (colElement.value != '' && selectedOption.labels) {
colElement.displayValue = selectedOption.labels[0] ? selectedOption.labels[0].innerText : "";
}
}
} else if (hiddenBox.type != "undefined" && hiddenBox.type == 'select-one') {
var options = hiddenBox.options;
var selectedIndex = hiddenBox.selectedIndex;
if (selectedIndex > -1 && options.length > selectedIndex) {
colElement.value = options[selectedIndex].value;
// If the value is empty, don't render the Label.
// Even for lookup selectBox, we map label & value to two different columns (value being mandatory & label is not), if there is no value associated with the label, we are not honoring that, its upto them to configure those entries.
if (colElement.value != '') colElement.displayValue = options[selectedIndex].label;
}
} else if (hiddenBox.hasAttribute("data-type") && hiddenBox.getAttribute("data-type") == 'duration') {
var value = hiddenBox.value;
var displayValue = hiddenBox.value;
// check whether Duration API is available
if (GlideDuration) {
var displayBoxParts = GlideDuration.parseDurationToParts(value);
if (displayBoxParts && displayBoxParts.size() == 4) {
// ignore seconds, we are not supporting
var d = displayBoxParts[0];
var h = displayBoxParts[1];
var m = displayBoxParts[2];
var s = displayBoxParts[3];
displayValue = ((d > 0) ? d + ' ' : '') + h + ':' + m + ':' + s;
}
}
colElement.value = value;
colElement.displayValue = displayValue;
} else {
if (displayBox) {
colElement.displayValue = displayBox.value;
if (displayBox.hasAttribute("data-type") && displayBox.getAttribute("data-type") == 'catalog_masked') colElement.displayValue = "********";
}
colElement.value = hiddenBox.value;
}
variables[id] = colElement;
}
return variables;
}
}
最後に
- 本来ならばPatchなどの将来のリリースで直るのを待つべき。
- 仕方なく直すときにのみ、この様に一時的に直す。