Multi row variable set

HARSHA GOWDA R
Tera Contributor

I added the following script in the client controller to capture MRVS values in the custom widget; however, the values entered by the user are not being captured upon submission of the catalog item.

function ($scope, $http, spUtil, nowAttachmentHandler, $rootScope, $sanitize, $window, $sce, i18n, $timeout, $log, spAriaUtil, $location, spModal) {
    var c = this;
    c.quantity = 1;
    if ($scope.data.sc_cat_item)
        $scope.data.sc_cat_item.description = $sce.trustAsHtml($scope.data.sc_cat_item.description);
    c.widget._debugContextMenu = [
        [ i18n.getMessage("Open") + " sc_cat_item"function(){
            var item = c.data.sc_cat_item;
            $window.open("/sp_config?id=form&table=" + item.table + "&sys_id=" + item.sys_id); }]
    ];
    c.showAddCartBtn = function() {
        return c.options.show_add_cart_button &&
            c.data.sc_cat_item.sys_class_name !== "sc_cat_item_producer" &&
            !c.data.sc_cat_item.no_cart;
    }

    c.showQuantitySelector = function() {
        return c.data.sc_cat_item.sys_class_name !== "sc_cat_item_producer" &&
            !c.data.sc_cat_item.no_quantity &&
            (!c.data.sc_cat_item.no_order_now || !c.data.sc_cat_item.no_cart);
    }

    c.showOrderNowButton = function() {
        return c.data.sc_cat_item.use_sc_layout || !c.data.sc_cat_item.no_order_now;
    }

    c.allowOrder = function() {
        if (c.data.sc_cat_item.use_sc_layout)
            return true;

        if (c.data.sc_cat_item.no_order)
            return false;

        if (c.data.sc_cat_item.no_order_now && c.data.sc_cat_item.no_cart)
            return false;

        return true;
    }

    $scope.$on('dialog.upload_too_large.show'function(e){
        $log.error($scope.m.largeAttachmentMsg);
        spUtil.addErrorMessage($scope.m.largeAttachmentMsg);
    });

    $scope.m = $scope.data.msgs;
    $scope.submitButtonMsg = $scope.m.submitMsg;
    var ah = $scope.attachmentHandler = new nowAttachmentHandler(setAttachments, appendError);
    function appendError(error) {
        spUtil.addErrorMessage(error.msg + error.fileName);
    }
    ah.setParams('sp_portal', $scope.data._attachmentGUID, 1024 * 1024 * $scope.data.maxAttachmentSize);
    function setAttachments(attachments, action) {
        $scope.attachments = attachments;
        if (action === "added")
            $scope.setFocusToAttachment();
        if (action === "renamed")
            spAriaUtil.sendLiveMessage($scope.m.renameSuccessMsg);
        if (action === "deleted")
            spAriaUtil.sendLiveMessage($scope.m.deleteSuccessMsg);
    }
    $scope.attachmentHandler.getAttachmentList();

    $scope.confirmDeleteAttachment = function(attachment) {
        spModal.confirm($scope.data.msgs.delete_attachment).then(function() {
          $scope.attachmentHandler.deleteAttachment(attachment);
          $scope.setFocusToAttachmentButton();
        });
  }

    // Breadcrumbs
    if ($scope.data.sc_cat_item) {
        if (!$scope.data.categories)
            $scope.data.categories = [];
//      $scope.data.categories.unshift({label: $scope.data.msgs.serviceCatalogMsg, url: '?id=' + $scope.data.sc_catalog_page});
        $scope.data.categories.push({label: $scope.data.sc_cat_item.name, url: '#'});
        $timeout(function() {
            $scope.$emit('sp.update.breadcrumbs', $scope.data.categories);
        });
        spUtil.setSearchPage('sc');
    }
    else {
        var notFoundBC = [{label: $scope.data.msgs.serviceCatalogMsg, url: '?id=' + $scope.data.sc_catalog_page}];
        $timeout(function() {
            $scope.$emit('sp.update.breadcrumbs', notFoundBC);
        });
        spUtil.setSearchPage('sc');
    }

    c.getItemId = function () {
        return $scope.data.sc_cat_item ? $scope.data.sc_cat_item.sys_id : -1;
    };

    var g_form;
    $scope.$on('spModel.gForm.initialized'function(e, gFormInstance){
        if (gFormInstance.getSysId() != -1 && gFormInstance.getSysId() != c.getItemId())
            return;
        g_form = gFormInstance;

        // This runs after all onSubmit scripts have executed
        g_form.$private.events.on('submitted'function(){
            if ($scope.data.sc_cat_item.item_action === "order")
                getOne();
            else if ($scope.data.sc_cat_item.item_action === "add_to_cart")
                addToCart();
        });
    });

    $scope.triggerAddToCart = function() {
        $scope.data.sc_cat_item.item_action = "add_to_cart";
        $scope.data.sc_cat_item.quantity = c.quantity;
        if (g_form)
            g_form.submit();
    }

    $scope.triggerOnSubmit = function(){
        $scope.data.sc_cat_item.item_action = "order";
        $scope.data.sc_cat_item.quantity = c.quantity;
        if (g_form){
            g_form.submit();
           
        }

    //  $window.location.reload();
       
    }

    // order / create request
    function getOne() {
        postCatalogFormRequest().success(function(response) {
            var a = response.result ? response.result : response.answer;
            var n = a.number;
            $scope.$emit("$$uiNotification", response.$$uiNotification ? response.$$uiNotification : a.$$uiNotification);
            $scope.$emit("$sp.sc_cat_item.submitted", a);
            if (n)
                issueMessage(n, a.table, a.sys_id, a.redirect_to, a.redirect_portal_url);
            $scope.submitting = false;
            $scope.submitButtonMsg = $scope.m.submittedMsg;
                //if($scope.m.submittedMsg){
               // alert('Password reset successfully completed');
                    //$window.location.reload();
               
        //Commented the below code, so that the request can follow the submit process as other catalog items
//                  if( $scope.submitButtonMsg=="Submitted" ){
//                      if(c.data.sc_cat_item.sys_id=='fd2bf35c1bffd410bab14005bd4bcb69'){
//                           myVar = setTimeout(function(){
//                               var alertmsg=c.data.sc_cat_item.name+' successfully completed';
//                        // alert("Password Reset successfully submitted");
//                      //  alert(alertmsg);
//                               $window.location.reload();
//                           }, 1000);
//                      }
//              //alert('Password reset successfully completed');
//                  //$window.location.reload();
//                  
                   
//      }
        }).error(function(response) {
            jslog(response);
        });
   
    }

    function addToCart() {
        postCatalogFormRequest().success(function(response) {
            $rootScope.$broadcast("$sp.service_catalog.cart.add_item");
            $rootScope.$broadcast("$sp.service_catalog.cart.update");
            //spUtil.addInfoMessage("Added item to shopping cart");
            $scope.submitting = false;
            c.status = "Added item to shopping cart";
        });
    }

    function postCatalogFormRequest() {
        setFieldsReadonly();
        $scope.submitted = true;
        $scope.submitting = true;
        var requestUrl;
        var reqData = {};
        if ($scope.data.sc_cat_item.sys_class_name === "sc_cat_item_producer") {
            requestUrl = '/api/sn_sc/v1/servicecatalog/items/' + $scope.data.sc_cat_item.sys_id + '/submit_producer';
            for(var obj in $scope.data.sc_cat_item._fields)
                reqData[$scope.data.sc_cat_item._fields[obj].name] = $scope.data.sc_cat_item._fields[obj].value;
            reqData = {'variables' : reqData, 'attachment_id' : $scope.data._attachmentGUID, 'attachment_table''sp_portal''get_portal_messages''true'};
        }
        else {
            requestUrl = spUtil.getURL('sc_cat_item');
            reqData = $scope.data.sc_cat_item;
            reqData._attachmentGUID = $scope.data._attachmentGUID;
        }
        return $http.post(requestUrl, reqData);
    }

    // spModel populates mandatory - hasMandatory is called by the submit button
    $scope.hasMandatory = function(mandatory) {
        return mandatory && mandatory.length > 0;
    };

    // switch catalog items
    var unregister = $scope.$on('$sp.list.click', onListClick);
    $scope.$on("$destroy"function() {
        unregister();
    });

    function onListClick(evt, arg) {
        $scope.data.sys_id = arg.sys_id;
        spUtil.update($scope);
    }

    function setFieldsReadonly(){
        var allFields = g_form.getFieldNames();
        for(var fieldName in allFields){
            g_form.setReadOnly(allFields[fieldName], true);
        }
    }

    function issueMessage(n, table, sys_id, redirectTo, redirectUrl) {
        var page = table == 'sc_request' ? 'sc_request' : 'ticket';
        if (c.options.page) {page = c.options.page;}
        if (c.options.table) {table = c.options.table;}
        if (redirectTo == 'catalog_home') page = 'sc_home';
        var url = spUtil.format(c.options.url, {page: page, table: table, sys_id: sys_id});
        if (c.options.auto_redirect == "true") {
            $timeout(function() {
                if (redirectUrl)
                    $location.url(redirectUrl);
                else
                    $location.url(url);
            }, 1000);
            return;
        }

        var t = $scope.m.createdMsg + " " + n + " - ";
        t += $scope.m.trackMsg;
        t += ' <a href="' + url + '">' + $scope.m.clickMsg + '</a>';
        spUtil.addInfoMessage(t);
    }
}
1 REPLY 1

sumityadav8
Mega Sage

Hi @HARSHA GOWDA R 

Looking at your code, the issue is in the postCatalogFormRequest function. When the item is not a sc_cat_item_producer, it posts $scope.data.sc_cat_item directly — but your MRVS (Multi-Row Variable Set) values live in a separate scope managed by the form model, and they aren't automatically merged back into $scope.data.sc_cat_item before submission.

Here's what's happening and how to fix it:

Root Cause

For standard catalog items, the request payload is built like this:

reqData = $scope.data.sc_cat_item;
reqData._attachmentGUID = $scope.data._attachmentGUID;

MRVS data is tracked separately by g_form (the spModel form instance) and is not reflected in $scope.data.sc_cat_item._fields the way regular variables are. So it gets silently dropped.

Fix: Extract MRVS values from g_form before posting

In postCatalogFormRequest, before building reqData, pull the current variable values (including MRVS rows) from g_form and merge them in:

function postCatalogFormRequest() {
    setFieldsReadonly();
    $scope.submitted = true;
    $scope.submitting = true;
    var requestUrl;
    var reqData = {};

    if ($scope.data.sc_cat_item.sys_class_name === "sc_cat_item_producer") {
        requestUrl = '/api/sn_sc/v1/servicecatalog/items/' + $scope.data.sc_cat_item.sys_id + '/submit_producer';
        for (var obj in $scope.data.sc_cat_item._fields)
            reqData[$scope.data.sc_cat_item._fields[obj].name] = $scope.data.sc_cat_item._fields[obj].value;
        reqData = {
            'variables': reqData,
            'attachment_id': $scope.data._attachmentGUID,
            'attachment_table': 'sp_portal',
            'get_portal_messages': 'true'
        };
    } else {
        requestUrl = spUtil.getURL('sc_cat_item');

        // Sync g_form field values (including MRVS) back into sc_cat_item._fields
        if (g_form) {
            var fieldNames = g_form.getFieldNames();
            for (var i = 0; i < fieldNames.length; i++) {
                var name = fieldNames[i];
                if ($scope.data.sc_cat_item._fields[name]) {
                    $scope.data.sc_cat_item._fields[name].value = g_form.getValue(name);
                }
            }
        }

        reqData = $scope.data.sc_cat_item;
        reqData._attachmentGUID = $scope.data._attachmentGUID;
    }

    return $http.post(requestUrl, reqData);
}

However, for MRVS specifically, g_form.getValue() won't return row data — MRVS rows are stored as a nested structure. The proper way to capture them is to ensure your _fields object on sc_cat_item stays in sync with the MRVS widget's model. You should listen for changes from the MRVS child widget and write them back:

$scope.$on('spModel.mrvs.updated', function(e, mrvsData) {
    if ($scope.data.sc_cat_item && $scope.data.sc_cat_item._fields) {
        angular.merge($scope.data.sc_cat_item._fields, mrvsData);
    }
});

The exact event name may differ depending on your ServiceNow version — check the MRVS widget's client script for what event it broadcasts when rows are added/changed

 

If my response helped ,Kindly Mark Helpful & Accept as Solution 🙂

 

Regards

Sumit