LoginSignup
0
1

More than 3 years have passed since last update.

[ServiceNow] [Madrid Patch6] Multi-row variable set の問題を無理やり解決する

Last updated at Posted at 2019-12-03

背景

  • 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" ボタンを押下しても、元画面に反映されない。ポップアップが閉じない。
  • コンソールでは TableVariableRow クラスの getJSON メソッドでエラーが出ているのが分かる。
    image.png
    image.png

  • より詳細に見ていくと、Order guide 経由の場合と Service Catalog 単体の場合で、ポップアップ画面内の Variable の ID が異なることが分かる。(サーバサイドでの処理ミス?)
    catalog 単体.png

  • IDのPrefixが異なるせいで、getVariablesFromDialog メソッドで要素とその値を正しく取れていないことが分かる。(これを無理やり直す)

無理やり直す

  • getVariablesFromDialog メソッドがいけてないのが分かったので(本当はポップアップ画面がいけてないんだけど)、無理やり以下の様に上書きする。
  • getVariablesFromDialog の処理修正版を作り、TableVariableServicerenderTableDialogForm メソッド内で 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などの将来のリリースで直るのを待つべき。
  • 仕方なく直すときにのみ、この様に一時的に直す。
0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1