Find your people. Pick a challenge. Ship something real. The CreatorCon Hackathon is coming to the Community Pavilion for one epic night. Every skill level, every role welcome. Join us on May 5th and learn more here.

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

its_SumitNow
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