You can enable users to select individual items in a result matrix dashboard portlet and run an action (trigger an event) on a button click.

In this example, we have a Dashboard with a result matrix portlet listing LPG items. Users can select any of these items and run their recalculation.

Under the hood, there are two logics associated with this Dashboard. One logic (main) builds the Dashboard content. An Option Input allows the user to select one of the Live Price Grids that exist in the partition. When the Dashboard is refreshed, a result matrix displays the LPG Items, one row per item, with SKU and lastUpdateDate.

The user can select one or multiple rows, and click on the “Calc PGI” button. When the user clicks the button, we run a different logic (action), which reads the list of the selected SKUs (i.e. the LPG Items) and triggers the calculation of each of them.

After about one minute, to allow time for the backend processing, if you refresh the dashboard, you’ll see that the lastUpdateDate of the selected rows have changes, proving that the calculation has been done.

The “action” logic can do anything we allow in the Groovy Sandbox. Here we just recalculate the LPG items. But it could as easily create a new Live Price Grid or Price List with the selected SKUs.

Dashboard Logic

The Dashboard logic contains the following elements:

api.findPriceGrids(null, null)

def labels = api.getElement("pg_list")*.label.sort()

api.option("Pick an LPG", labels)

api.booleanUserEntry("Activate Embedded")

api.booleanUserEntry("Activate Select")

def label = api.getElement("input_pg_label")

if (label == null || api.syntaxCheck) return

api.getElement("pg_list").find { label.equals(it.label) }

def pg = api.getElement("pg")

if (pg == null || api.syntaxCheck) return

def fields = ["sku", "activePrice", "submitDate", "lastUpdateDate"]
def sortField = "sku"
def filter = Filter.equal("priceGridId",

api.find("PGI", 0, 200, sortField, /* fields, */ filter)

def pg = api.getElement("pg")

if (pg == null || api.syntaxCheck) return

def matrix = api.newMatrix([
  "sku", "activePrice", "lastUpdateDate", "download", "action"

matrix.setColumnFormat("lastUpdateDate", FieldFormatType.DATETIME)
matrix.addColumnTranslation("sku", [en: "sku (click leads to Master Data)"])

api.getElement("pg_items").each { pgi ->
  def row = [:]
  row.sku = matrix.linkCell(pgi.sku, AppPages.MD_PRODUCTS_PAGE, pgi.sku)
  row.activePrice = pgi.activePrice
  row.lastUpdateDate = pgi.lastUpdateDate = createDownloadLink(matrix, pg, pgi)
  row.action = createCalcPgiAction(matrix, pg, pgi)

if (api.getElement("input_activate_click")) {

if (api.getElement("input_activate_select")) {
  addRowSelectionBackEndAction(matrix, pg)

return matrix

// helper methods (they make the above code more readable)

def createDownloadLink(def matrix, def pg, def pgi) {
  matrix.downloadButton("Download (whole PG)",
                        "/pricegridmanager.fetchpdf/" +,
                        null /*payload*/)

def createCalcPgiAction(def matrix, def pg, def pgi) {
  matrix.backEndAction("Recalculate (this item)",
                       "/pricegridmanager.update/" +,
                       api.toJson([data:[typedId: pgi.typedId]]),

def addClickEvent(def matrix) {
      .withColValueAsEventDataAttr("sku", "sku_attribute")

def addRowSelectionBackEndAction(def matrix, def pg) {
      .addFormulaInput("PG_label", pg.label)
      .withButtonLabel("Calc PGI")
      .withButtonTooltip("(Re)Calculate the selected Price Grid Items (SKUs)")

if (!api.getElement("input_activate_click")) return


	// Show the dashboard embedded ...
	// ... and reevaluate it on "myEvent"
	// Note: "dashboardWideEvent()" fce makes the event local
	// to the containing dashboard instance

	// Pull the "customerId" attribute out of the event payload and expose it
	// as the "Customer Id" input to the embedded dashboard

Action Logic

The action logic contains the following elements:

def pg_id = api.decimalUserEntry('PG_id')

// test value
if (pg_id == null) {
  pg_id = 5 // "Fourth Live Price Grid"

pg_id as Long

def matrix = api.inputMatrix("SKUs", "sku")

// Front End payload validation
if (!api.syntaxCheck) {
  validate(matrix) // throws exception if invalid - this validation is totally optional, feel free to drop or tailor to your own needs

// test value
if (matrix == null) {
  matrix = [[sku: "MB-0007"]]


def validate(def matrix) {
  if (matrix == null) {
    exception(matrix, "is missing or null")
  if (matrix.size() == 0) {
    exception(matrix, "should not be empty")
  if (! (matrix instanceof List) || ! (matrix[0] instanceof Map)) {
    exception(matrix, "should be an Array of Maps")
  if (matrix[0].keySet() == ["sku"] as Set) {
    // that's expected for backend action
  else if (matrix[0].keySet() == ["sku", "selected"] as Set) {
    // that's expected for UI InputMatrix
  else {
    exception(matrix, "should have only 'sku' values")
  if (matrix*.sku.find {!it instanceof String}) {
     exception(matrix, "should have'sku' values of type String")

def exception(def matrix, def msg) {
  api.throwException("'SKUs' InputMatrix " + msg + " - " + api.toJson(matrix))

def pg_id = api.getElement("input_pgid")
def sku_list = api.getElement("input_skus")

api.triggerPriceGridCalculation(pg_id, [skusToRecalc: sku_list])

return null