Code Examples

If actions combine database functions with user interface, window interaction, and status functions, you must separate them into client-side and server-side actions. See the following example:

Original Action

New Actions

                    // A. Loads the file
loadFile('MyFolder/myFile.txt').done(function(data) {
    // B. Shows the message
    showMessage(data);
}); 

                  

Server-Side Action

                    // A. Server-side action (Check the Run on server box)
// Loads the file
loadFile('MyFolder/myFile.txt').done(function(data) {
    // Saves file data to the scratchpad variable
    scratchpad.fileContent = data;
}); 

                  

Client-Side Action

                    // B. Client-side action
// Shows the message stored in the scratchpad variable
showMessage(scratchpad.fileContent); 

                  

The original action loads a file from the File Cabinet and shows its content in a popup window on the user interface. The function loadFile() interacts with the database and must be stored in a server-side action, while showMessage() works with the user interface and requires a client-side action.

After checking the Run on server box on the action record, edit the action by adding the scratchpad variable (scratchpad.fileContent) where you save the file data to be sent to the client. Then, remove the showMessage() function and create a client-side action to store it. The client-side action obtains data from the scratchpad variable and shows the file content in a popup window on the user interface.

The previous example shows a simple use case. The following code examples help you implement client-side and server-side actions for other common scenarios:

Transferring Data Using Questions and Answers

Client-side and server-side actions can share information using questions and answers. With questions and answers, you can share information from the server to the client and the other way around. During implementation, you may need to create the questions and answers that will store the data to be transferred. For more information about questions and answers, see Gathering Information with Questions and Answers.

Transferring Data from Server to Client Using Questions and Answers

The following example shows how to transfer data from the server to the client with questions and answers.

Original Action

New Actions

                        // A. Obtains customer data
var currentCustomer = getValue("NS", "ENTITYID");
getData({
    type: 'customer',
    filter: ['internalid', 'is', currentCustomer],
    fields: ['companyname']
}).done(function(data) {
    var customer = data[0];
    //B. Displays customer data on the user interface
    showMessage(customer.companyname);
}); 

                      

Server-Side Action

                        // A. Server-side action (Check Run on server box) 
// Obtains customer data 
var currentCustomer = getValue("NS", "ENTITYID");
getData({
    type: 'customer',
    filter: ['internalid', 'is', currentCustomer],
    fields: ['companyname']
}).done(function(data) {
    var customer = data[0];
    // Saves customer data in an answer
    setValue('DATA', 'CUSTOMER', customer.companyname);
}); 

                      

Client-Side Action

                        // B. Client-side action 
// Obtains customer data from the answer
var companyname = getValue('DATA', 'CUSTOMER');
// Displays customer data on the user interface
showMessage(companyname); 

                      

The original action obtains customer information using the getData() function—a server-side function. Then, the customer information is displayed on the user interface in a popup window using the showMessage() function—a client-side function.

Because the original action combines a server-side function and a client-side function, you must separate it into a server-side and a client-side action. After obtaining the customer data with the getData() function, the new server-side action A saves the information to the DATA/CUSTOMER answer using the universal function setValue(). Storing the customer information in an answer makes it available for the client-side action. The new client-side action B retrieves the customer information from the DATA/CUSTOMER answer using the universal function getValue() and displays it in a popup window.

Transferring Data from Client to Server Using Questions and Answers

The following example shows how to transfer data from client to server with questions and answers.

Original Action

New Actions

                        // A. Asks users to provide the item SKU
var sku = prompt('Enter item SKU');
// B. Searches for items with the provided item SKU
getData({
    type: 'item',
    filter: ['itemid', 'is', sku],
    fields: ['itemid', 'displayname']
}).done(function(data) {
    setValue('DATA', 'ITEMS', data);
}); 

                      

Client-Side Action

                        // A. Client-side action
// Asks users to provide the item SKU
var sku = prompt('Enter item SKU');
// Saves the item SKU in an answer
setValue('ITEM', 'SKU', sku); 

                      

Server-Side Action

                        // B. Server-side action(Check Run on server box)
// Obtains the item SKU from the answer
var sku = getValue('ITEM', 'SKU', sku);
// Searches for items with the provided item SKU
getData({
    type: 'item',
    filter: ['itemid', 'is', sku],
    fields: ['itemid', 'displayname']
}).done(function(data) {
    setValue('DATA', 'ITEMS', data);
}); 

                      

The original action asks users to provide the item SKU using the prompt() function—a JavaScript client-side function. Then, the original action searches for the items with the provided SKU using getData()—a server-side function. Finally, the item SKU is saved to the answer DATA/ITEMS.

Because the original action combines a client-side function and a server-side function, you must separate it into a server-side action and a client-side action. After prompting the user to provide the item SKU, the new client-side action A saves the information to the answer ITEM/SKU using the universal function setValue(). Storing the item SKU in an answer makes it available for the server-side action. The new server-side action B retrieves the item SKU from the answer using the universal function getValue(), searches for the item SKU, and saves it to an answer.

Transferring Data Using the Scratchpad Variable

Client-side and server-side actions can share information using the scratchpad variable. With the scratchpad variable, you can share information from the server to the client and the other way around.

Transferring Data from Server to Client Using the Scratchpad Variable

The following example shows how to transfer data from the server to the client using the scratchpad variable.

Original Action

New Actions

                        // A. Obtains customer data 
var currentCustomer = getValue("NS", "ENTITYID");
getData({
    type: 'customer',
    filter: ['internalid', 'is', currentCustomer],
    fields: ['companyname']
}).done(function(data) {
    var customer = data[0];
    // B. Displays customer data on the user interface
    showMessage(customer.companyname);
}); 

                      

Server-Side Action

                        // A. Server-side  action (Check Run on server box) 
// Obtains customer data
var currentCustomer = getValue("NS", "ENTITYID");
getData({
    type: 'customer',
    filter: ['internalid', 'is', currentCustomer],
    fields: ['companyname']
}).done(function(data) {
    var customer = data[0];
    //Saves customer data to the scratchpad variable
    scratchpad.companyname = customer.companyname;
}); 

                      

Client-Side Action

                        // B. Client-side action
// Obtains data from the scratchpad variable and displays it on the user interface
showMessage(scratchpad.companyname); 

                      

The original action obtains customer information using getData()—a server-side function. Then, customer information is displayed on the user interface in a popup window using showMessage()—a client-side function.

Because the original action combines a server-side function and a client-side function, you must separate it into a server-side and a client-side action. After obtaining the customer data, the new server-side action A saves the information to the scratchpad variable (scratchpad.companyname). The customer information becomes available for the new client-side action B. The client-side action B retrieves the customer information from the scratchpad variable and displays it in a popup window on the user interface.

Transferring Data from Client to Server Using the Scratchpad Variable

The following example shows how to transfer data from client to server using the scratchpad variable.

Original Action

New Actions

                        // A. Asks users to provide the item SKU
var sku = prompt('Enter item SKU');
// B. Searches for items with the provided item SKU
getData({
    type: 'item',
    filter: ['itemid', 'is', sku],
    fields: ['companyname']
}).done(function(data) {
    setValue('DATA', 'ITEMS', data);
}); 

                      

Client-Side Action

                        // A. Client-side action
// Asks users to provide the item SKU and saves data to the scratchpad variable
scratchpad.sku = prompt('Enter item SKU'); 

                      

Server-Side Action

                        // B. Server-side action (Check Run on server box)
// Searches for items by otbtaining the item SKU from the scratchpad variable
getData({
    type: 'item',
    filter: ['itemid', 'is', scratchpad.sku],
    fields: ['companyname']
}).done(function(data) {
    setValue('DATA', 'ITEMS', data);
}); 

                      

The original action asks users for the item SKU using the prompt() function—a JavaScript client-side function. Then, the original action searches for the items with the provided SKU using getData()—a server-side function. Finally, the item SKU is saved to the answer DATA/ITEMS.

Because the original action combines a client-side function and a server-side function, you must separate it into a server-side action and a client-side action. After prompting users for the item SKU, the new client-side action A saves the information to the scratchpad variable (scratchpad.sku). The item SKU becomes available for the server-side action B. The new server-side action B retrieves the item SKU from the scratchpad variable and searches for the item SKU.

Transferring Data from Server to Client with QTables

You can transfer data from server to client using qTables. See the following example:

Original Action

New Actions

                      // A. Asks users to provide the beginning of the item SKU
var sku = prompt('Enter the beginning of the item SKU');
// B. Searches for items by the begining of the item SKU
getData({
    type: 'item',
    filter: ['itemid', 'startswith', sku],
    fields: ['itemid']
}).done(function(data) {
    //B1. Stores the found items in a qTable and displays it on the user interface
    var t = getQTable('ITEMS');
    t.setRowCount(data.length);
    for (var i = 0; i < data.length; i++) {
        t.set('ITEM', i + 1, data[i].itemid);
    }
}); 

                    

Client-Side Action

                      // A. Client-side action
//Asks users to provide the beginning of the item SKU and stores the user input in the scratchpad variable
scratchpad.sku = prompt('Enter the beginning of the item SKU'); 

                    

Server-Side Action

                      // B. Server-side action (Check Run on server box)
// Searches for items by the begining of the item SKU
getData({
    type: 'item',
    filter: ['itemid', 'startswith', scratchpad.sku],
    fields: ['itemid']
}).done(function(data) {
    //B1. Stores the found items in a qTable
    var t = getQTable('ITEMS');
    t.setRowCount(data.length);
    for (var i = 0; i < data.length; i++) {
        t.set('ITEM', i + 1, data[i].itemid);
    }
}); 

                    

Client-Side Action

                      // C. Client-side action
//Refreshes the user interface to display the found items in the qTable
renderView(); 

                    

For more information about qTables, see QTable Questions.

The original action asks users to provide the beginning of the item SKU using the prompt() function—a JavaScript client-side function. Then, the original action searches for items that match those characters using getData()—a server-side function. Finally, the found item SKUs are saved to a qTable and displayed on the user interface.

Because the original action combines a client-side function, a server-side function, and updates the qTable on the user interface, you must separate it into a server-side action and multiple client-side actions. After prompting the user for the beginning of item SKU, the client-side action A saves the information to the scratchpad variable ( scratchpad.sku).

The information saved to the scratchpad variable becomes available for the server-side action. The new server-side action B obtains the information from the scratchpad variable and searches for item SKUs that match the user input. The found items are saved to a qTable. To update the qTable, the client-side action C refreshes the user interface using the renderView() function and displays the found item SKUs in the qTable.

Transferring Data from Server to Client with Function-Generated Tables

You can transfer data from the server to the client using function-generated tables automatically filled in with data from your account. See the following example:

Original Action

New Actions

                      // A. Asks users to provide the beginning of the item SKU 
var sku = prompt('Enter the beginning of the item SKU');
// B. Searches for items by the beginning of the item SKU
getData({
    type: 'item',
    filter: ['itemid', 'startswith', sku],
    fields: ['itemid']
}).done(function(data) {
    //B1. Stores the found items in a function-generated table
    var t = getTable('ITEMS');
    t.set(data);
    t.setStatus('ready');
}); 

                    

Client-Side Action

                      // A. Client-side action 
// Asks users to provide the beginning of the item SKU and stores the user input in the scratchpad variable
scratchpad.sku = prompt('Enter the beginning of the item SKU'); 

                    

Server-Side Action

                      // B. Server-side action (Check Run on server box)
// Searches for items by the beginning of the item SKU
getData({
    type: 'item',
    filter: ['itemid', 'startswith', scratchpad.sku],
    fields: ['itemid']
}).done(function(data) {
    var t = getTable('ITEMS');
    t.set(data);
    t.setStatus('ready');
}); 

                    

For more information about function-generated tables, see Creating Tables Using Functions.

The original action asks users to provide the beginning of the item SKU using the prompt() function—a JavaScript client-side function. Then, the original action searches for items that start with the provided characters using getData()—a server-side function. Finally, the found item SKUs are saved to a function-generated table and can be used, for example, in an input box popup table answer.

Because the original action combines a client-side function and a server-side function, you must separate it into a server-side action and a client-side action. After prompting the user to provide the beginning of the item SKU, the new client-side action A saves the information to the scratchpad variable ( scratchpad.sku). This information becomes available for the server-side action. The new server-side action B obtains the information from the scratchpad variable and searches for item SKUs that match the user input. The found items are saved in a function-generated table, which is available for use in client-side actions. For example, you can use it to provide data for input box popup table answers.

Performing Server Requests in Batches

Your current implementation may include actions where database functions perform multiple operations of the same type and a separate server request is sent for each operation. For example, if an action creates 20 records, the server receives 20 requests—one for each record. However, the new architecture requires database functions to be stored in server-side actions. Server-side actions have limits on the number of server requests and operations they can process. To overcome this limitation, you can batch server requests so that a single server request processes multiple operations.

To implement this solution, you can create loops in both the client-side and the server-side actions. The client-side action keeps track of the operations to be processed. Its loop calls the server-side action until all operations are completed.

The server-side action keeps track of the governance usage units. Its loop processes multiple operations in a single server request. If it runs out of usage units, the server-side action exits the loop. The client-side action relaunches the server-side action if there are still operations to be processed. See the following example:

Original Action

New Actions

                      // A. Creates multiple records
(async function() {
    var records = getValue('DATA', 'RECORDS'),
        created = [];
    for (var i = 0; i < records.length; i++) {
        var newRecord = await createRecord(records[i]);
        created.push(newRecord.id);
    }
    setValue('DATA', 'CREATED', created);
})(); 

                    

Client-Side Action

                      // A. Client-side action
(async function() {
    scratchpad.output = [];
    scratchpad.records = getValue('DATA', 'RECORDS'); // Array of records to be created
    scratchpad.index = 0;
    let count = scratchpad.records.length;
    do {
        await runAction('CREATE_RECORDS');
    } while (scratchpad.index < count);
    console.log('FINISH', scratchpad.output);
})(); 

                    

Server-Side Action

                      // B. Server-side action (Check Run on server box)
let records = scratchpad.records,
    count = records.length,
    weight = 0;
do {
    let remaining = getRemainingUnits();
    if (remaining < weight) {
        let unprocessed = count-scratchpad.index; // records to be created on the following requests
        console.log('Operation completed. Records left:', unprocessed);
        restartAction();
    }
    let record = records[scratchpad.index];
    createRecord(record).done((data) => {
        scratchpad.output.push(data);
    });
    if (!weight) {
        weight = remaining - getRemainingUnits(); // how many units are consumed by a single iteration
    }
    scratchpad.index++;
} while (scratchpad.index < count); 

                    

The original action stores the list of records to be created in the answer DATA/RECORDS. Then, the action loops through the list and creates the records one by one. The createRecord() function sends a server request for every record to be created.

There is no information about how many records the answer stores. If you implement a for loop in a server-side action, the action may fail because if the required operations exceed the usage unit limit.

In the new implementation, the client-side function A stores the list of records to be created in the scratchpad variable. Then, it starts a loop that calls the server-side action using the runAction() function until all records are created. As you can see from the example, client-side actions can use asynchronous functions, which are supported in JavaScript ES6.

The server-side action B retrieves the list of records to be created from the scratchpad variable. Then, it dynamically calculates the usage units at the beginning and the end of each iteration through the remainingUsageUnits() function.

If there are enough usage units, the server-side action runs the loop to create the records. If there aren't enough usage units, restartAction() stops the loop and sends information to the client-side action about whether to restart the server-side action. Then, the server-side action sends information about the created records to the client-side action using the scratchpad variable. The client-side action verifies the number of created records against the total number of records required. If there are still records left, the client-side action relaunches the server-side action until all records are created.

Calling Actions from Event Handlers Attached to User Interface Elements

Custom event handlers attached to user interface elements can't directly call server-side functions. Instead, you must:

  • Store the server-side function in a server-side action.

  • Call the server-side action using the runAction() function from the custom script.

  • Use questions and answers to share information between the client and server because the scratchpad variable isn't available in custom scripts.

  • In the rule of the server-side action, enter X(*/*)—the always false operator—because the action isn't activated by answers users select on the product interface.

The following example shows how to implement runAction() in an event handler attached to a user interface element.

Original Custom Script

New Implementation

                      // Custom script
// A. Adds an onclick event handler to a user interface button
jQuery('#my-button').click(function() {
    var currentCustomer = getValue('NS', 'ENTITYID');
    // B. Obtains the customer name
    getData({
        type: 'customer',
        filter: ['internalid', 'is', currentCustomer],
        fields: ['companyname']
    }).done(function(data) {
        var customer = data[0];
        //C. Displays the customer name on the user interface (if users click the button)
        showMessage(customer.companyname);
    });
}); 

                    

Updated Custom Script

                      // Custom script
// A. Adds an onclick event handler to a user interface button
jQuery('#my-button').click(function() {
    // Calls the server-side action
    runAction('LOAD_DATA').done(function() {
        var companyname = getValue('DATA', 'CUSTOMER');
        // C. Displays the customer name on the user interface (if users click the button)
        showMessage(companyname);
    });
}); 

                    

Server-Side Action

                      // Server-side action
// Check the Run on server box
// B. Obtains customer data
var currentCustomer = getValue('NS', 'ENTITYID');
getData({
    type: 'customer',
    filter: ['internalid', 'is', currentCustomer],
    fields: ['companyname']
}).done(function(data) {
    var customer = data[0];
    // Stores customer data in an answer
    setValue('DATA', 'CUSTOMER', customer.companyname);
}); 

                    

The original custom script adds an event handler to a user interface button. The custom script retrieves customer data using getData()—a server-side function. When users click the button, the customer name is displayed in a popup window using showMessage()—a client-side function.

In the new implementation, the custom script still adds the event handler and uses showMessage() to display the information. However, instead of calling getData() directly, the new version retrieves the customer information by calling a server-side action using runAction(). The server-side action B loads the customer data and stores it in an answer to make it available on client.

Calling Actions from CPQ Modules

CPQ modules are client scripts and can't directly call server-side functions. Instead, you must:

  • Store server-side functions in a server-side action.

  • Call the server-side action from the module using the runAction() function.

  • Use questions and answers to share information between modules and server-side actions, because modules can't access the scratchpad variable.

  • In the rule of the server-side action, enter X(*/*)—the always false operator—because the action isn't activated by answers users select on the product interface.

The following example shows how to implement runAction() in a module.

Original Module

New Implementation

                      <script script-type="module" type="text/javascript">
  var app = angular.module('formApp');
  app.modController('modctrl', [function () {
    this.getCost = function() {
      var sku = getValue('ITEMS','SKU');
      if (sku) {
// Loads the item cost
        getData({
          type: 'item',
          filter: ['itemid','is', sku],
          fields: ['cost']
        }).done(function(data) {
          var cost = data[0].cost;
          showMessage(sku+' cost is '+cost);
        });
      } else {
        alert('Please enter the item SKU');
      }
    };
  }]);
</script>
<div ng-controller="modctrl as mc">
   <div style="margin:10px;">
      <div>Load item cost</div>
      <button style="margin:10px;padding:5px 15px;" type="button" ng-click="mc.getCost();">Show cost</button>
   </div>
</div> 

                    

Updated Module

                      <script script-type="module" type="text/javascript">
  var app = angular.module('formApp');
  app.modController('modctrl', [function () {
    this.getCost = function() {
      var sku = getValue('ITEMS','SKU');
      if (sku) {
        runAction('ITEM_COST').done(function(data) {
          var cost = getValue('MISC','COST');
          showMessage(sku+' cost is '+cost);
        });
      } else {
        alert('Please enter the item SKU');
      }
    };
  }]);
</script>
<div ng-controller="modctrl as mc">
    <div style="margin:10px;">
        <div>Load item cost</div>
        <button style="margin:10px;padding:5px 15px;" type="button" ng-click="mc.getCost();">Show cost</button>
    </div>
</div> 

                    

Server-Side Action

                      // Server-side action
// Check the Run on server box
var sku = getValue('ITEMS', 'SKU');
getData({
    type: 'item',
    filter: ['itemid', 'is', sku],
    fields: ['cost']
}).done(function(data) {
    setValue('MISC', 'COST', data[0].cost);
}); 

                    

When users click the button on the user interface, the original module retrieves the item SKU from the answer ITEMS/SKU. Then, the module searches the item cost for that item by directly calling the getData() function—a server-side function. Finally, the item cost is displayed in a popup window on the user interface using the showMessage() function—a client-side function.

In the new implementation, a server-side action has been created to store the getData() function. The updated module calls the server-side action using the runAction() function and shares the item SKU through the answer ITEMS/SKU. The server-side action uses the getData() function to obtain the item SKU from the answer ITEMS/SKU, searches for the item cost, and saves it to the answer MISC/COST to make it available for the module. Then, the module retrieves the item cost from the answer and displays it.

Related Topics

General Notices