---
category: [Administration & Integrations, Platform and Product Extensions, Platform and Product Extensions/Integration, Administration & Integrations/Custom Integrations & Apps, Platform and Product Extensions/Workday Extend]
keyword: [Extend, Presentation Components, widgets, tags, UI components]
nav: wcp_docs
parent_url: /wcp_docs/
title: >- 
  Concept: grid
layout: subsection
---

<a id="section_ms2_np4_ytb"></a>
The `grid` widget displays data in a table format. This topic describes how to use the `grid` in a PMD, highlighting the attributes and tags you need to configure for each usage. For reference information about `grid` attributes, PMD Scripting, and mobile support, see [grid](/wcp_docs/zmz1518211268000.html).

## <a id="section_populateGrid"></a>Populating the Grid Rows

To populate the grid rows with data, set these `grid` attributes:

-   <b>rows</b>

    Specify a list containing the data for the grid rows. You can use an endpoint, script, or function that returns a list. If you're using endpoint data, specify `theEndpointName.data`, where:

    -   `theEndpointName` - The name of the inbound endpoint defined in `endPoints[]`. This endpoint must return a list containing the data elements you want to display as rows on the grid.
    -   `data` - The name of the list that contains the data elements to populate on the grid.

-   <b>rowVariableName</b>

    Specify a variable name that represents the data element on the row.

    When you define the grid columns, use `rowVariableName` to reference the fields within the data element. To reference a field value to display on a `cellTemplate`, specify `theRowVariableName.theFieldName`.



This simple grid displays the `descriptor` and `primaryJob.supervisoryOrganization.descriptor` fields from `workersEndpoint.data`:

```
{
 "type": "grid",
 "label": "Simple View Grid",
 "id": "simpleGridWorker",
 "rows": "<% workersEndpoint.data %>",
 "rowVariableName": "thisRow",
 "columns": [
   {
     "type": "column",
     "columnId": "col1",
     "label": "Worker",
     "cellTemplate": {
       "type": "text",
       "value": "<% thisRow.descriptor %>"
     }
   },
   {
     "type": "column",
     "columnId": "col2",
     "label": "Supervisory Organization",
     "cellTemplate": {
       "type": "text",
       "value": "<% thisRow.primaryJob.supervisoryOrganization.descriptor %>"
     }
   }
 ]
},
```

The `rows` attribute in the sample grid uses this sample endpoint:

```
{
 "baseUrlType": "workday-staffing",
 "name": "workersEndpoint",
 "authType": "sso",
 "url": "<% '/workers?search=' + queryParams.workerName %>"
}
```

This data snippet from the sample `workersEndpoint` displays the fields referenced by the sample grid `simpleGridWorker`. For brevity, this snippet displays only the fields used by `simpleGridWorker`. Assume that `queryParams.workerName` contains "Johnson".

```
{
 "data": [
   {
     "descriptor": "Dylan Johnson",
     "primaryJob": {
       "supervisoryOrganization": {
         "descriptor": "Global Support - Canada Group",
         . . .
       },
       . . .
     },
     . . .
   },
   {
     "descriptor": "Elizabeth Johnson",
     "primaryJob": {
       "supervisoryOrganization": {
         "descriptor": "Payroll Department",
         . . .
       },
       . . .
     },
     . . .
   }
 ],
 "total": 2
}
```

## <a id="section_staticColumns"></a>Defining Columns

To define the columns and how they display on a grid, set the `columns` attribute to a list of `column` objects.

`columns` and `dynamicColumns` are mutually exclusive.

For each `column` in `columns[]`, specify the `cellTemplate` attribute with a template tag that displays the widget on the column.

This sample grid on a view page contains columns that use `instanceList` and `text` as `cellTemplate` tags:

```
{
 "type": "grid",
 "label": "Grid with Static Columns",
 "id": "gridWithStaticColumns",
 "rows": "<% workersEndpoint.data %>",
 "rowVariableName": "workerRow",
 "columns": [
   {
     "type": "column",
     "columnId": "column1",
     "label": "Worker",
     "cellTemplate": {
       "type": "instanceList",
       "instanceList": [
         {
           "id": "<% workerRow.id %>",
           "descriptor": "<% workerRow.descriptor %>",
           "wid": "<% workerRow.id %>"
         }
       ]
     }
   },
   {
     "type": "column",
     "columnId": "column2",
     "label": "Business Title",
     "cellTemplate": {
       "type": "text",
       "value": "<% workerRow.primaryJob.businessTitle %>"
     }
   },
   {
     "type": "column",
     "columnId": "column3",
     "label": "Country",
     "cellTemplate": {
       "type": "text",
       "value": "<% workerRow.primaryJob.location.country.descriptor %>"
     }
   }
 ]
}
```

## <a id="section_dynamicColumns"></a>Defining Dynamic Columns

You can dynamically generate grid columns based on inbound data types and a set of cell templates. Example: Assume that a grid has multiple columns that contain text data. Instead of defining multiple template text tags, you can use dynamic columns to define a single template text tag with dynamic labels.

`columns` and `dynamicColumns` are mutually exclusive.

If the endpoint data changes based on user interactions with other widgets, you can also use dynamic columns to avoid hard-coding multiple grids with different sets of columns. Example: Assume that you want the user to see a dynamic set of columns based on a selected value on a dropdown widget. Instead of defining 2 grids and setting their `visible` attribute according to the user selection, you can define 1 grid with dynamic columns using a `fieldSet` as a template tag. You can use the `visible` attributes of the tags in the `fieldSet` to hide or display the field data.

To define dynamic columns, set these `grid` attributes:

-   <b>dynamicColumns</b>

    Specify the `dynamicColumns` tag with these required attributes:

    -   `columns` - A binding to a list containing the column metadata, which includes the column id, label, and type. The grid generates all columns found in the `columns`, and orders them using their list order in the column metadata.

        Derive the column metadata from the metadata of the endpoint data. If you're using a Workday REST API, see the response schema of the endpoint on the REST API Explorer. Ensure that you include the id and type of data you want to display on the grid.

        You can specify a list of JSON maps that define the column metadata. Example:

        ```
        [
           {'name': 'Column 1',
            'id': 'column1',
            'type': 'string',
            'dataField': 'someField'},
           {'name': 'Column 2',
            'id': 'column2',
            'type': 'boolean',
            'dataField': 'anotherField'}
        ]
        ```

        Alternatively, you can store the column metadata in a JSON file and securely host it as an endpoint. Specify the `columns` attribute as a binding to the inbound endpoint that returns the list. Example: `myColumnsMetadataEndpoint.columnsList`

    -   `columnVariableName` - A variable name that represents a column. The other attributes of `dynamicColumns` use `columnVariableName` to reference the column.
    -   `columnId` - A binding to the attribute in `columns` that uniquely identifies each column. Specify <i><% theColumnVariableName.theIdAttributeKey %></i>. The value of this attribute identifies the column.
    -   `label` - A binding to the attribute in `columns` that contains the column name. Specify <i><% theColumnVariableName.theLabelAttributeKey %></i>. The value of this attribute displays as the column title.
    -   `templateId` - A binding to the attribute in `columns` that specifies the data type of the column in `columns`. Specify <i><% theColumnVariableName.theTypeAttributeKey %></i>. The values of this attribute determine the `templateId` of each `cellTemplate` in `cellTemplates[]`.
    -   `attributeName` - A binding to the attribute in `columns` whose values correspond to the data fields in `rows`. The generated columns will display the data for these fields. Specify <i><% theColumnVariableName.theDataFieldAttributeKey %></i>. The value of this attribute specifies the field path in the row data.

-   <b>cellTemplates</b>

    Define an array of `cellTemplate` tags. Each `cellTemplate` specifies a template for a type of column.

    For each possible value of `templateId` in `dynamicColumns`, specify a `cellTemplate` tag with these required attributes:

    -   `templateId` - A unique value of the type attribute you specified as the `templateId` in `dynamicColumns`. For an example of `templateId`, see its usage in the sample grid `gridWithDynamicColumns`.
    -   `template` - The template tag. Example: If the `templateId` is `string`, specify a `text` tag as the template.



Example: Let's use the sample `workersEndpoint` to create a sample grid named `gridWithDynamicColumns`.

This snippet displays the data fields that we want to use as columns: descriptor, email, and supervisory organization.

```
{
 "data": [
   {
     "descriptor": "Dylan Johnson",
     "person": {
       "email": "djohnson@workday.net",
       . . .
     },
     "primaryJob": {
       "supervisoryOrganization": {
         "descriptor": "Global Support - Canada Group",
         . . .
       },
       . . .
     },
     . . .
   },
   {
     "descriptor": "Elizabeth Johnson",
     "person": {
       "email": "ejohnson@workday.net",
       . . .
     },
     "primaryJob": {
       "supervisoryOrganization": {
         "descriptor": "Payroll Department",
         . . .
       },
       . . .
     },
     . . .
   }
 ],
 "total": 2
}
```

Using the response schema from `workersEndpoint`, this column metadata is a list of the 3 columns. The column metadata includes the column name, id, type, and data field.

```
[
 {
   "name": "Worker Name",
   "id": "descriptor",
   "type": "string",
   "dataField": "descriptor"
 },
 {
   "name": "Email Address",
   "id": "email",
   "type": "string",
   "dataField": "person.email"
 },
 {
   "name": "Supervisory Organization",
   "id": "supervisoryOrganization",
   "type": "string",
   "dataField": "primaryJob.supervisoryOrganization.descriptor"
 }
]
```

This sample `gridWithDynamicColumns` uses the sample column metadata:

```
{
 "type": "grid",
 "id": "gridWithDynamicColumns",
 "label": "Grid with Dynamic Columns",
 "rowVariableName":"row",
 "rows": "<% workersEndpoint.data %>",
 "dynamicColumns" : {
   "type": "dynamicColumns",
   "columns": "<%  [
     {'name': 'Name',
      'id': 'descriptor',
      'type': 'string',
      'dataField': 'descriptor'},
     {'name': 'Email',
      'id': 'email',
      'type': 'string',
      'dataField': 'person.email'},
     {'name': 'Supervisory Organization',
      'id': 'supervisoryOrganization',
      'type': 'string',
      'dataField': 'primaryJob.supervisoryOrganization.descriptor'}
   ] %>",
   "columnVariableName": "aColumn",
   "columnId": "<% aColumn.id %>",
   "label": "<% aColumn.name %>",
   "templateId": "<% aColumn.type %>",
   "attributeName": "<% aColumn.dataField %>"
 },
 "cellTemplates": [
   {
     "type": "cellTemplate",
     "templateId": "string",
     "template": {
       "type": "text"
     }
   }
 ]
}
```

In the sample `gridWithDynamicColumns`, note these attributes:

-   In `dynamicColumns`, the `columns` attribute uses PMD functions that return a list containing the column metadata.

    The `templateId` attribute specifies `<% aColumn.type %>`. The `type` attributes in the column metadata contain `string`.

    The `attributeName` specifies `<% aColumn.dataField %>`. The `dataField` attributes in the sample column metadata contain the values descriptor, person.email, and primaryJob.supervisoryOrganization.descriptor. The resulting grid generates a column for each of these data fields.

-   The `cellTemplates[]` contains 1 `cellTemplate` tag: a `text` tag for the `string` type. All types in the column metadata are strings; hence only the text cellTemplate is necessary. If the column metadata contains other types, specify a cellTemplate for each of the other types.


## <a id="section_pagination"></a>Paging Data on a Grid

By default, the grid displays as much data as the client can handle, without pagination.

You can configure data paging on a grid by using either of these attributes:

-   `autoPaging`
-   `pagingInfo`


`autoPaging` and `pagingInfo` are mutually exclusive.

<b>Grid paging with non-paginated endpoints</b>

To improve grid performance, use `autoPaging` with fast endpoints that:

-   Don't support paging.
-   Return high volumes of data in a single call.


When you set `autoPaging` to "true" on a view page, the grid automatically paginates data when there are more than 50 rows of results. The grid displays the first 50 rows of results with the initial API call, then calls the endpoint again each time the user scrolls down to load more results. Because the endpoint could get called multiple times as the user scrolls down, Workday doesn't recommend enabling `autoPaging` for slow endpoints.

<b>Grid paging with paginated endpoints</b>

Use the `pagingInfo` attribute with endpoints that return paginated data. If an endpoint returns paginated data, you can enable the grid on a view page to display chunks of data as the user scrolls through the grid. To enable the grid to display paginated data on a view page, set the `pagingInfo` attribute with these required attributes:

-   <b>endPoint</b>

    Specify `<% endpoints.aPaginatedEndpoint %>`, where `aPaginatedEndpoint` is an inbound endpoint with paging parameters `limit` and `offset`.

    When you define the endpoint `url` in `endPoints[]`, specify the `limit` and `offset` parameters. Use the app variables `pagingVariables.limit` and `pagingVariables.offset` to pass the limit and offset values across multiple requests. To set their initial values, use the conditional operator to check their empty state and assign their values.

    <b>Note:</b> For paginated grids, we recommend that the `limit` value should be at least 50.

    Example: The `url` for this sample `workersPagedEndpoint` endpoint includes the `limit` and `offset` parameters. When the PMD initially calls the endpoint, `pagingVariables.limit` is null, so `pagingVariables.limit` initially sets to 100. Similarly, `pagingVariables.offset` initially sets to 0. When the user scrolls beyond the last fetched data, subsequent endpoint requests automatically set the `pagingVariables.offset` value.

    ```
    "endPoints": [
       {
         "baseUrlType": "workday-staffing",
         "name": "workersPagedEndpoint",
         "authType": "sso",
         "url": "<%
           var limit = pagingVariables.limit ?: 100;
           var offset = pagingVariables.offset ?: 0;
           '/workers?limit=' + limit + '&offset=' + offset;
         %>"
       }
    ],
    ```

    The `pagingVariables.limit` and `pagingVariables.offset` are string variables.

-   <b>rowCount</b>

    Specify an integer binding with the number of items returned by the endpoint, which is typically the `total` field. Example: `<% workersPagedEndpoint.total %>`.

    The grid displays the total at the top, and uses the total to allocate the spacing on the vertical scrollbar. The total number can be greater than the number of rows in the paginated data.

-   <b>data</b>

    Specify a list binding with the paginated data returned by the endpoint. This value is typically the `data` list returned by the endpoint. Example: `<% workersPagedEndpoint.data %>`.



This sample grid includes the `pagingInfo` that uses the sample `workersPagedEndpoint`:

```
{
 "type": "grid",
 "label": "Workers",
 "rows": "<% workersPagedEndpoint.data %>",
 "rowVariableName": "worker",
 "pagingInfo": {
   "endPoint": "<% endpoints.workersPagedEndpoint %>",
   "rowCount": "<% workersPagedEndpoint.total %>",
   "data": "<% workersPagedEndpoint.data %>"
 },
 "columns": [
   {
     "type": "column",
     "label": "Worker",
     "columnId": "descriptor",
     "cellTemplate": {
       "type": "text",
       "value": "<% worker.descriptor %>"
     }
   }
 ]
}
```

## <a id="section_updatingData"></a>Inserting and Updating Row Data

A grid on an edit page makes all widgets on the grid editable so users can add or modify row data. To enable users to insert rows on a grid, set these `grid` attributes:

-   <b>doNotAdd</b>

    The `doNotAdd` must be false, which is the default. If false, the user can add a row on top of the grid.

-   <b>showRowMover</b>

    You can optionally set `showRowMover` to true if you want users to add a row immediately below a specific row. For more details about the `showRowMover` attribute, see the `showRowMover` description in the Attributes section.



To submit new and updated data to outbound endpoints:

-   Define the POST, PUT, and PATCH outbound endpoints with the `exclude` attribute.

    Specify `exclude` with a condition logic on a POST endpoint to ensure that it submits only for new rows. Similarly, use `exclude` with a condition logic on PUT or PATCH endpoints to ensure that they submit only for updated rows.

    These sample outbound endpoints assume that the grid contains a column whose cellTemplate ID is `entryId`.

    ```
    {
     "name" : "postExpense",
     "url" : "/entries",
     "baseUrlType" : "workday-expenses",
     "exclude": "<% ! empty entryId.value %>"
    },
    {
     "name" : "putExpense",
     "url" : "<% '/entries/' + entryId.value %>",
     "httpMethod": "PUT",
     "baseUrlType" : "workday-expenses",
     "exclude": "<% empty entryId.value %>"
    }
    ```

    A row with a nonempty `entryId` value indicates that the row data is an existing expense entry, which a user can update. In this case, the `exclude` logic determines that `postExpense` won't execute, but `putExpense` executes to update the row data.

    A row with an empty `entryId` value means that the user added a new expense entry. In this case, the `exclude` logic determines that `putExpense` won't execute. , but `postExpense` will execute to add the new row.

-   In the column containing the row ID:

    -   Ensure that the `id` attribute in the `cellTemplate` matches the cellTemplate ID you specified in the `exclude` attribute of the outbound endpoints.
    -   Specify the `valueOutBinding` attribute with the `id` field of the PUT (or PATCH) outbound endpoint.

    This sample column, which contains the expense `id`, sets the `valueOutBinding` to `putExpense.id`:

    ```
    {
       "type": "column",
       "columnId": "expenseIdColumn",
       "label": "Expense ID",
       "cellTemplate": {
         "type": "text",
         "id": "entryId",
         "value": "<% rowdata.id %>",
         "valueOutBinding": "putExpense.id",
         "enabled": false
       }
    }
    ```

-   For all other non-ID columns, use `valuesOut` to specify `valueOutBinding` with data for both the POST and PUT (or PATCH) outbound endpoints. The `valuesOut` enables you to send the same data to multiple endpoints. The `exclude` attributes on the outbound endpoint definitions determine which endpoint executes.

    This sample date column sets the `valueOutBinding` to `postExpense.date` and `putExpense.date`:

    ```
    {
     "type": "column",
     "columnId": "dateColumm",
     "label": "Date",
     "cellTemplate": {
       "type": "text",
       "id": "expenseDate",
       "value": "<% rowdata.date ?? pageVariables.defaultDate %>",
       "valuesOut": [
         {
           "value": "<% self.value %>",
           "valueOutBinding": "postExpense.date"
         },
         {
           "value": "<% self.value %>",
           "valueOutBinding": "putExpense.date"
         }
       ]
     }
    }
    ```

    Note that the `value` attribute uses the null coalescing operator to set the default date if the date is null. Setting the default value ensures a non-null value in new rows.



## <a id="section_deletingData"></a>Deleting Row Data

A grid on an edit page enables users to delete rows from the grid. To enable users to delete rows from the grid, `doNotRemove` must be false, which is the default.

To submit an outbound endpoint that deletes row data:

-   Define the DELETE outbound endpoint.

    In the `url` attribute, specify the resource to delete. Use the column `cellTemplate` that contains the unique ID of the object to delete. Example: Use `aCellTemplateId.value` as a path parameter.

    This sample DELETE outbound endpoint deletes the row identified by `entryId.value`:

    ```
    {
     "name" : "deleteExpense",
     "authType" : "SSO",
     "url" : "<% '/entries/' + entryId.value %>",
     "httpMethod": "DELETE",
     "baseUrlType" : "workday-expenses"
    }
    ```

-   On the `grid` tag, set the `deleteEndPoint` attribute to the name of the DELETE outbound endpoint.

    Example:

    ```
    "deleteEndPoint": "deleteExpense"
    ```



## <a id="section_gridEventHandling"></a>Grid Event Handling

When a user adds, removes from, or changes values in a row, you can call event handlers to perform logic that meets your business needs. Consider these guidelines:

-   Set default values in a new row and avoid null values in the grid. PMD Scripting returns an error when an operation encounters a null value. You can use the null coalescing operator `??` to check for null and set the default value in a `cellTemplate`.

    This sample `cellTemplate` tag value sets a default value:

    ```
    "value": "<% rowdata.someTextField ?? 'someDefaultValue' %>"
    ```

-   Specify the `id` attribute in the `cellTemplate` of every column. An `onChange` event handler can use the cellTemplate `id` to reference the cells on the current row.

    This sample `onChange` event handler updates the value of another cell:

    ```
    "onChange": "<% someCellId.value = getSomeValue; %>"
    ```

-   An `onRowAdd` event handler can use the `rows[0].childrenMap.someColumnId` property to reference a cell on a new row at the top of a grid.

    This sample `onRowAdd` event handler sets the default date on a new row. Assuming that the `showRowMover` attribute of the grid is false, the new row is at the top of the grid.

    ```
    var newRow = expenseGrid.rows[0];
    newRow.childrenMap.dateColumm.value = date:getTodaysDateFormatted(date:getDateTimeZone(null), 'yyyy-MM-dd');
    ```



This sample `expenseGrid` demonstrates the `onRowAdd`, `onRowRemove`, and `onChange` events. This grid uses data from the endpoints defined in the preceding sections.

```
{
 "type": "grid",
 "id": "expenseGrid",
 "rows": "<% expenseEntries.data %>",
 "label": "Expense entries",
 "deleteEndPoint": "deleteExpense",
 "rowVariableName": "rowdata",
 "onRowAdd": "<% updateTotalQuantity() %>",
 "onRowRemove": "<% updateTotalQuantity() %>",
 "showRowMover": true,
 "columns": [
   {
     "type": "column",
     "columnId": "expenseIdColumn",
     "label": "Expense ID",
     "cellTemplate": {
       "type": "text",
       "id": "entryId",
       "value": "<% rowdata.id %>",
       "valueOutBinding": "putExpense.id",
       "enabled": false
     }
   },
   {
     "type": "column",
     "columnId": "dateColumm",
     "label": "Date",
     "cellTemplate": {
       "type": "text",
       "id": "expenseDate",
       "value": "<% rowdata.date ?? pageVariables.defaultDate %>",
       "valuesOut": [
         {
           "value": "<% self.value %>",
           "valueOutBinding": "postExpense.date"
         },
         {
           "value": "<% self.value %>",
           "valueOutBinding": "putExpense.date"
         }
       ]
     }
   },
   {
     "type": "column",
     "columnId": "memoColumn",
     "label": "Memo",
     "cellTemplate": {
       "type": "text",
       "id": "memoCell",
       "value": "<% rowdata.memo ?? pageVariables.defaultMemoText %>",
       "valuesOut": [
         {
           "value": "<% self.value %>",
           "valueOutBinding": "postExpense.memo"
         },
         {
           "value": "<% self.value %>",
           "valueOutBinding": "putExpense.memo"
         }
       ]
     }
   },
   {
     "type": "column",
     "columnId": "currencyColumn",
     "label": "Currency",
     "cellTemplate": {
       "type": "text",
       "id": "currencyText",
       "value": "<% rowdata.amount.currency ?? pageVariables.defaultCurrency %>",
       "valuesOut": [
         {
           "value": "<% self.value %>",
           "valueOutBinding": "postExpense.amount.currency"
         },
         {
           "value": "<% self.value %>",
           "valueOutBinding": "putExpense.amount.currency"
         }
       ]
     }
   },
   {
     "type": "column",
     "columnId": "amountColumn",
     "label": "Amount",
     "cellTemplate": {
       "type": "number",
       "id": "amountValue",
       "fractionDigits": 2,
       "value": "<%  number:toBigDecimal(rowdata.amount.value, 2, 'HALF_DOWN') ?? 0.00  %>",
       "valuesOut": [
         {
           "value": "<% string:toString(self.value) %>",
           "valueOutBinding": "postExpense.amount.value"
         },
         {
           "value": "<% string:toString(self.value) %>",
           "valueOutBinding": "putExpense.amount.value"
         }
       ],
       "onChange": "<%
         totalCostCell.value = quantityCell.value * amountValue.value;
       %>"
     }
   },
   {
     "type": "column",
     "id": "quantityColumn",
     "columnId": "quantityColumn",
     "label": "Quantity",
     "cellTemplate": {
       "id": "quantityCell",
       "type": "number",
       "value": "1",
       "fractionDigits": 2,
       "onChange": "<%
          memoCell.value = 'Please review this item: ' + memoCell.value;
          totalCostCell.value = quantityCell.value * amountValue.value;
          updateTotalQuantity();  
       %>"
     }
   },
   {
     "type": "column",
     "columnId": "totalCostColumn",
     "label": "Total Cost",
     "cellTemplate": {
       "id": "totalCostCell",
       "type": "number",
       "value": "<% quantityCell.value * amountValue.value ?? 0.00 %>",
       "fractionDigits": 2,
       "enabled": false
     }
   }
 ]
}
```

This script block contains the `updateTotalQuantity` event handler:

```
"script": "<%
    var updateTotalQuantity = function() {
        totalQuantity.value = expenseGrid.getSubtotal('quantityColumn');
    }
%>"
```

The `updateTotalQuantity` event handler displays the grid subtotal on this `totalQuantity` widget:

```

{
 "type": "number",
 "id": "totalQuantity",
 "label": "Total Quantity",
 "enabled": false,
 "fractionDigits": 2,
 "value": "<% expenseGrid.getSubtotal('quantityColumn') %>"
},
```

<b>Note:</b> When the page loads, the sample `totalQuantity` number widget also displays the grid subtotal. Although the `updateTotalQuantity` event handler uses the same logic, the `value` attribute of the number widget can't call `updateTotalQuantity`. The `updateTotalQuantity` function uses the `totalQuantity` widget reference, which is not accessible during the construction of the `totalQuantity` widget.



## <a id="section_hiddenValues"></a>Adding Hidden Values to a Column

In some cases, you might need to store hidden values in a column for calculation or data manipulation purposes. You might also need to use a hidden index if your grid data doesn't contain unique IDs. To store hidden values in a column, you can add the `hidden` tag to the `cellTemplate` of an existing column.

In the `cellTemplate` of a column, wrap the existing tag with a `fieldSet` and add the `hidden` tag within the `fieldSet`. You can add the `hidden` tag to any existing column in the grid; the column order doesn't matter.

This sample `column` includes a `fieldSet` with `text` and `hidden` tags:

```
{
 "type": "column",
 "columnId": "companyColumn",
 "label": "Company",
 "cellTemplate": {
 "type": "fieldSet",
   "children": [
     {
       "type": "readOnlyText",
       "value": "<% row.company.descriptor %>"
     },
     {
       "type": "hidden",
       "id": "rowId",
       "value": "<% row.id %>"
     }
   ]
 }
}
```

To reference the `hidden` tag within a cell `fieldSet`, specify `<aRowInstance>.childrenMap.<theColumnId>.childrenMap.<theHiddenId>.value`. Note that the `childrenMap.<theHiddenId>` is the `childrenMap` property of the `fieldSet`. Example:

```
sampleGrid.selectedRows[0].childrenMap.companyColumn.childrenMap.rowId.value
```

## <a id="section_gridArrayData"></a>Submitting Data as an Array

By default, the grid submits a separate outbound endpoint request for each row on the grid. To submit all grid rows as an array in a single outbound endpoint request:

-   Define the outbound endpoint that requires an array in the request body.

    This Procurement (POST) outbound endpoint includes the `requisitionLines[]` in its request body schema. (You can see the request body schema of this endpoint on the Workday Extend API Explorer.)

    ```
    {
           "name": "submitRequisition",
           "url": "/requisitions",
           "authType": "sso",
           "baseUrlType": "workday-procurement"
    }
    ```

-   On the `grid` tag, set the `isArrayOutBinding` to true.
-   For each column `cellTemplate`, set the `valueOutBinding` to `someOutboundEndpoint.someDataArray[].someField`.


This sample grid sets `isArrayOutBinding` to true. Its column `cellTemplate` tags set their `valueOutBinding` to the corresponding fields in the `requisitionLines[]`. For demonstration purposes, this simple 2-row grid displays a default item ID and enables a user to enter 2 quantities.

```
{
 "type": "grid",
 "id": "gridWithArrayOutBinding",
 "label": "Submitting Data as an Array",
 "rows": "<% [1, 2] %>",
 "isArrayOutBinding": "<% true %>",
 "columns": [
   {
     "type": "column",
     "columnId": "column1",
     "label": "Quantity",
     "cellTemplate": {
       "type": "text",
       "valueOutBinding": "submitRequisition.requisitionLines[].quantity"
     }
   },
   {
     "type": "column",
     "columnId": "column2",
     "label": "Item",
     "cellTemplate": {
       "type": "text",
       "value": "<% pageVariables.defaultItemId  %>",
       "valueOutBinding": "submitRequisition.requisitionLines[].item.id"
     }
   }
 ]
}
```

When a user submits the grid with 2 quantities, the grid sends the rows in a single request with this outbound data.

```
{
 "requisitionLines": [
   {
     "quantity": "11",
     "item": {
       "id": "52db5b83bc3d1074977076f8847b6f3c"
     }
   },
   {
     "quantity": "22",
     "item": {
       "id": "52db5b83bc3d1074977076f8847b6f3c"
     }
   }
 ]
}
```

If `isArrayOutBinding` is false, the grid sends the rows in separate requests with these separate outbound data:

```
{
 "requisitionLines": [
   {
     "quantity": "11",
     "item": {
       "id": "52db5b83bc3d1074977076f8847b6f3c"
     }
   }
 ]
}
```

```
{
 "requisitionLines": [
   {
     "quantity": "22",
     "item": {
       "id": "52db5b83bc3d1074977076f8847b6f3c"
     }
   }
 ]
}
```

## <a id="section_submittingDataOnSelectedRows"></a>Submitting Data on Selected Rows

Selection checkboxes on a grid enable users to select rows and update the row data. To display selection checkboxes, set these `grid` attributes:

-   <b>selectionEnabled</b>

    Set `selectionEnabled` to true.

-   <b>maxRowSelection</b>

    Specify the maximum number of selected rows.



This example demonstrates how to submit selected rows on a grid that contains non-unique IDs.

```
{
 "type": "grid",
 "id": "employeeGrid",
 "label": "Select and update skills",
 "rows": "<% [{'name': 'Logan McNeil', 'skill': 'Leadership'},
              {'name': 'Logan McNeil', 'skill': 'Administration'},
              {'name': 'Logan McNeil', 'skill': 'Project Management'}] %>",
 "rowVariableName": "employee",
 "selectionEnabled": true,
 "maxRowSelection": 2,
 "currentRowVariable": "currentRow",
 "columns": [
   {
     "type": "column",
     "columnId": "employeeName",
     "label": "Employee Name",
     "cellTemplate": {
       "type": "text",
       "id": "employeeName",
       "value": "<% employee.name %>",
       "valueOutBinding": "outboundEndpointName.name"
     }
   },
   {
     "type": "column",
     "label": "Employee Skill",
     "columnId": "employeeInfo",
     "cellTemplate": {
       "type": "text",
       "id": "employeeSkill",
       "value": "<% employee.skill %>",
       "valueOutBinding": "outboundEndpointName.skill"
     }
   }
 ]
}
```

To submit only the selected rows on the grid, use the `onSend` event handler of the outbound endpoint. Check whether the current row is a selected row by inspecting the `<currentRowVariable>.selected` property.

```
"outboundData": {
   "outboundEndPoints": [
     {
       "baseUrlType": "testServer",
       "authType": "noauth",
       "name": "outboundEndpointName",
       "url": "/post",
       "onSend": "<% currentRow.selected ? self.data : null %>"
     }
   ]
 }
```

<b>Notes</b>:

-   When you use `currentRowVariable` in an `onSend` event handler, set the `isArrayOutBinding` attribute of the grid to false. This setting calls the endpoint for each row object, which makes the row instance available in `currentRowVariable`.
-   The `self.data` variable in the `onSend` event handler contains the `valueOutBinding` data.
-   You can't use `currentRowVariable` in a `valueOutBinding` with PMD Scripting.


## <a id="section_subtotal"></a>Getting the Grid Size and the Column Subtotal

To get the number of rows on a grid, use the list `size` method on the `rows` property of a grid.

The sample `text` tag in this snippet displays the number of rows in the sample `grid`:

```
{
 "type": "text",
 "id": "gridSize",
 "label": "Grid Size",
 "enabled": false,
 "value": "<% expenseGrid.rows.size() %>"
}
```

To get the sum of the values in a numeric column, use the grid `getSubtotal` method, passing it the `columnId`.

Example:

```
{
 "type": "number",
 "id": "totalQuantity",
 "label": "Total Quantity",
 "enabled": false,
 "fractionDigits": 2,
 "value": "<% expenseGrid.getSubtotal('quantityColumn') %>"
}
```

## <a id="section_sortingFiltering"></a>Sorting and Filtering Columns

You can enable column sorting and filtering on grid data on view and edit pages. When you enable sorting and filtering on a column, users can click the column title to see the sort and filter options. Users can also click the filter icon on the grid to create filters.

To enable sorting and filtering on a column, set these column attributes:

-   <b>sortableAndFilterable</b>

    Set `sortableAndFilterable` to true.

-   <b>label</b>

    Set `label` to the column title.



Example:

```
{
 "type": "grid",
 "label": "Sortable and Filterable Grid",
 "id": "gridSortAndFilter",
 "rows": "<% workersEndpoint.data %>",
 "rowVariableName": "theRow",
 "columns": [
   {
     "type": "column",
     "columnId": "col-a",
     "label": "Worker",
     "cellTemplate": {
       "type": "text",
       "value": "<% theRow.descriptor %>"
     }
   },
   {
     "type": "column",
     "columnId": "col-b",
     "label": "Business Title",
     "sortableAndFilterable": "true",
     "cellTemplate": {
       "type": "text",
       "value": "<% theRow.primaryJob.businessTitle %>"
     }
   }
 ]
}
```

## <a id="section_freezingColumns"></a>Freezing Columns

You can freeze columns on the left side of a grid. Frozen columns enable users to see them constantly while horizontally scrolling through the rest of the columns. You can freeze multiple columns on both view and edit pages. However, you can only freeze 1 column on mobile.

To freeze columns, set the `frozen` attribute to true on each column you want to freeze. Example:

```
{
 "type": "grid",
 "label": "Grid with Frozen Columns",
 "id": "gridWithFrozenCols",
 "rows": "<% workersEndpoint.data %>",
 "rowVariableName": "aRow",
 "columns": [
   {
     "type": "column",
     "columnId": "col-1",
     "label": "Worker",
     "frozen": "true",
     "cellTemplate": {
       "type": "text",
       "value": "<% aRow.descriptor %>"
     }
   },
   {
     "type": "column",
     "columnId": "col-2",
     "label": "Business Title",
     "frozen": "true",
     "cellTemplate": {
       "type": "text",
       "value": "<% aRow.primaryJob.businessTitle %>"
     }
   },
   {
     "type": "column",
     "columnId": "col-3",
     "label": "Email",
     "cellTemplate": {
       "type": "text",
       "value": "<% aRow.person.email %>"
     }
   },
   {
     "type": "column",
     "columnId": "col-4",
     "label": "Work Email",
     "cellTemplate": {
       "type": "text",
       "value": "<% aRow.primaryJob.supervisoryOrganization.descriptor %>"
     }
   }
 ]
}
```

## <a id="section_exportExcel"></a>Exporting to Microsoft Excel

You can add the <b>Export to Excel</b> icon on a grid, enabling users to export a grid from a view page to an Excel spreadsheet. Users can download a grid and use Excel functionality to perform quick data analysis with your custom app. To enable the <b>Export to Excel</b> icon on a grid, set the `excelExportEnabled` attribute to true. Example:

```
"excelExportEnabled": true
```

<b>Note:</b> The Excel-enabled grid also displays the <b>Turn on the new tables view</b> switch, which enables features of the recent Workday-delivered table component.

Each time a user clicks the <b>Export to Excel</b> icon, we fetch the entire grid data and export the columns into an Excel file. The Excel filename uses the grid `label`. If the grid doesn't have a `label`, the filename uses the grid `id`. If the grid doesn't have a `label` and `id`, we use a default filename.

By default, the grid exports the cell values of all columns. We export number and currency values as numbers, date values as dates, and all other values as strings in Excel. If the cell tag specifies any format properties, we apply the specified format. To override the default export or format of a column, specify the `excel` attribute on the `column`.

-   To exclude a column from the grid export, set the `enabled` property to false. Example:

    ```
    "excel": {
      "enabled": false
    }
    ```

-   To customize the default format of a cell value in Excel, specify these `excel` properties:

    -   `value` - The value to export.

        You can reference any of the grid variables `rowVariableName`, `currentRowVariable`, `indexVariableName` to specify the appropriate value to export. Example:

        ```
        "excel": {
         "value": "<% currentRow.childrenMap.someColumnId.value  %>",
        }
        ```

        If the column contains images, container tags such as `fieldSet` and `section`, or other tags whose exportable format is unknown, you must specify the `value` attribute. Set the `value` to a script that returns the data.

        If you specify `value` without `valueType`, the default `valueType` is string. To customize the string format and apply Excel-specific formatting, set the `valueType` and `valueFormat` properties.

    -   `valueType` - The data type of the exported `value` in Excel.

        Valid values:

        -   `boolean`
        -   `date`
        -   `number`
        -   `string` (default)

        If you specify `valueType`, you must also specify the `value` property. The data type of `value` must match the specified `valueType`. Example:

        ```
        {
         "type": "column",
         "label": "Vote",
         "columnId": "voteColumn",
         "excel": {
           "value": "<% bool:toBoolean(currentRow.childrenMap.voteColumn.value) %>",
           "valueType": "boolean"
         },
         "cellTemplate": {
           "type": "text",
           "value": "<% rowData.voteYesOrNo %>"
         }
        }
        ```

    -   `valueFormat` - An Excel format to apply on the exported value. See the [Excel Number format codes](https://support.microsoft.com/en-us/office/number-format-codes-5026bbd6-04bc-48cd-bf33-80f18b4eae68). Example:

        ```
        {
         "type": "column",
         "label": "Reference Number",
         "columnId": "refColumn",
         "excel": {
           "value": "<% rowData.refNumber %>",
           "valueType": "number",
           "valueFormat": "<% '\\@\\_#0' %>"
         },
         "cellTemplate": {
           "type": "number",
           "value": "<% rowData.refNumber %>"
         }
        }
        ```




This sample grid enables the Export to Excel icon, excludes a column, and customizes the export value of a column containing a `fieldSet`:

```
{
 "type": "grid",
 "excelExportEnabled": true,
 "id": "requisitionGrid",
 "rows": "<% requisitions.data %>",
 "rowVariableName": "requisitionRow",
 "currentRowVariable": "currentRow",
 "label": "Requisitions",
 "columns": [
   {
     "type": "column",
     "columnId": "reqDescriptor",
     "label": "Description",
     "excel": {
       "enabled": false
     },
     "cellTemplate": {
       "id": "reqDescriptorCell",
       "type": "text",
       "value": "<% requisitionRow.descriptor  %>"
     }
   },
   {
     "type": "column",
     "columnId": "dateColumn",
     "label": "Requisition Date",
     "cellTemplate": {
       "type": "date",
       "value": "<% requisitionRow.requisitionDate %>",
       "dateDisplayPattern": "yyyy/mm/dd"
     }
   },
   {
     "type": "column",
     "columnId": "unitCostColumn",
     "label": "Unit Cost",
     "excel": {
       "value": "<% currentRow.childrenMap.unitCostColumn.childrenMap.unitCostCell.value  %>",
       "valueType": "number",
       "valueFormat": "<% '[Green] #,##0.00' %>"
     },
     "cellTemplate": {
       "id": "unitFieldSet",
       "type": "fieldSet",
       "horizontal": "true",
       "children": [
         {
           "id": "unitCostCell",
           "type": "number",
           "value": "<% requisitionRow.amount.value %>",
           "fractionDigits": 2,
           "enabled": false
         },
         {
           "id": "unitCurrencyCell",
           "type": "text",
           "value": "<% requisitionRow.amount.currency  %>"
         }
       ]
     }
   }
 ]
}
```

The grid Export to Excel feature doesn't support:

-   Grids on edit pages.
-   Grids containing dynamic columns.
-   Any sorting, filtering, and custom cell styling that a user has applied on the grid.
-   Security policies configured on the <i>Export to PDF and Excel</i> domain.
-   Mobile devices. The Workday mobile app doesn't support grid exports.
-   The ability to cancel the export process after submitting the request, which has a 60-second time limit.

    <b>Note:</b> Depending on the number of columns, grids containing more than 4000 rows might cause timeouts and performance issues. In this case, we recommend that you use Reports-as-a-Service custom reports for exporting data.



Note these Excel limitations:

-   Excel supports numbers up to 15 digits, and replaces digits past the 15th digit with zeros. See [Last digits are changed to zeros when you type long numbers in cells of Excel](https://docs.microsoft.com/en-us/office/troubleshoot/excel/last-digits-changed-to-zeros).
-   Excel obfuscates long numbers. See [Long numbers are displayed incorrectly in Excel](https://docs.microsoft.com/en-us/office/troubleshoot/excel/long-numbers-incorrectly-in-excel).
-   Excel supports strings with up to 32,767 characters. We truncate a text longer than the maximum length. See [Excel specifications and limits](https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3).


