How to react to changes of multi-row variable sets, like new row, removed row, updated row? Missing onChange Client script workaround.

Apparently if you have a multi-row variable set on a catalog item, there is no Out of box way to react to changes of rows; like row added, row removed or row updated from a client script, which is assigned to the catalog item.

We can define client scripts on the MRV itself, so we could react to field changes of a given row’s fields, but this is still not enough to handle row addition on removal.

My workaround provides an easy approach to overcome this limitation.

Steps

  1. create a new widget, for example “MRV Macro widget”
  2. add this widget to the catalog item as a Macro variable (so it won’t be visible on the form)
  3. set the script below as the client script of the widget and update according to your needs

Explanation

field.change event is emitted by the form component, for each and every field change of the form. The MRV basically is also a field, but it contains rows and cell values in a JSON-encoded array of objects.

Whenever the rows are changed within the MRV in any way (rows added / removed, or fields updated within the row) the same event will be broadcasted on $rootScope. So the widget will listen to this events, and whenever the event is broadcasted, with the name of the MRV, then we can do the required calculations.

This method requires no customisation to any OOTB widgets and in my opinion, yet it’s able to overcome on the missing support for MRV row-level onChange client script support. Note: because of the embedded widget approach, this will work only in service portals!

Client script of the widget

api.controller = function($scope, $rootScope) {

    var c = this;

    c.onFieldChange = function(event, data) {

        var form = $scope.page.g_form;

        /**
         * Name of the MRV.
         * Update this according your own MRV's internal name
         */
        var fieldName = "name_of_mrv";
        var total = 0; // TODO: delete this, just an example


        /**
         * If the function is executed for a real "field.change" event, 
         * let's react only if the MRV was changed
         */
        if (event && data) {
            if (data.field.name != fieldName) {
                return;
            }
        }


        try {
            /**
             * Let's try to get the field's value and parse it as JSON
             */
            var rows = JSON.parse(form.getValue(fieldName))

            /**
             * Rows here now, should be an array of objects, each object represents 1 row from the MRV,
             * and objects' properties are the variables of the MRV, so for example:
             * [
             *     { brand: 'Toyota',     qty: 1, year: 1985 },
             *     { brand: 'Mercedes',   qty: 3, year: 1999 },
             *     { brand: 'Volkswagen', qty: 6, year: 2010 }
             * ]
             */
            rows.map(function(row) {
                /**
                 * Calculate here what you need to calculate, based on each row.
                 */
                total += row.qty; // TODO: delete this, just an example
            });

            /**
             * Update the parent form based on your needs
             */
            form.setValue('number_of_rows', rows.length); // TODO: delete this, just an example


        } catch (e) {
            /**
             * If you reach this point, then MRV is empty
             */
            console.log('No rows yet in MRV...', e)
        }
    };


    /**
     * field.change event is emitted by the form component
     */
    $rootScope.$on('field.change', c.onFieldChange);

    /**
     * Let's execute this also immediately, in case if we have some pre-defined rows in the MRV
     */
    c.onFieldChange();

};

About the Author

Matyas Peto is a Lead ServiceNow Service Portal developer, who has successfully completed numerous ServiceNow Service Portal projects with unique features and custom Service Portal elements for enterprise customers in the EU and USA.

Updates

Blog