Something I have really grown to love with Freedom UI lists is the ability to add row actions that can be performed for the selected row in a list. These quick actions make a nice way to allow users to perform quick actions without opening the full record page. Here’s a sample of a list with some row actions, these particular row actions are actions I’ve added on the Cases section list:

Adding actions to a list is pretty simple, but currently can’t be done in the page editor and you must modify the page code to add these. You’ll need to find the crt.DataGrid element in the viewConfigDiff. Then, you’ll need to add a node for rowToolbarItems to the values. Here’s a sample:
viewConfigDiff: /**SCHEMA_VIEW_CONFIG_DIFF*/[
{
"operation": "merge",
"name": "DataTable",
"values": {
...,
"rowToolbarItems": [{
"type": "crt.MenuItem",
"caption": "Do something",
"icon": "open-button-icon",
"clicked": {
"request": "cfx.SomeCustomHandler",
"params": {
"itemsAttributeName": "Items",
"recordId": "$Items.PDS_Id",
}
}
}]
}
...
},
...
]/**SCHEMA_VIEW_CONFIG_DIFF*/
This row action will execute a request named “cfx.SomeCustomHandler”, which I’ve added to the handlers on the page. It will also include the current record’s Id value as recordId, so I’ll access that in the request as request.recordId. There’s a few things to note about the params part. You’ll need to find a few things out about the crt.DataGrid element first. For section Lists, the Items collection attribute is named “Items” and the primary Id for the row data has an attribute named “PDS_Id” so the code above will work. For other lists you add to a page, you’ll need to find out what these attributes are named. The grid/list will have elements for the following:
"items": "$GridDetail_fei1jz3", "primaryColumnName": "GridDetail_fei1jz3DS_Id",
With these names, the rowToolbarItem would look as follows:
viewConfigDiff: /**SCHEMA_VIEW_CONFIG_DIFF*/[
{
"operation": "merge",
"name": "DataTable",
"values": {
...,
"rowToolbarItems": [{
"type": "crt.MenuItem",
"caption": "Do something",
"icon": "open-button-icon",
"clicked": {
"request": "cfx.SomeCustomHandler",
"params": {
"itemsAttributeName": "GridDetail_fei1jz3",
"recordId": "$GridDetail_fei1jz3.GridDetail_fei1jz3DS_Id",
}
}
}]
}
...
},
...
]/**SCHEMA_VIEW_CONFIG_DIFF*/
Take note, the “itemsAttributeName” does not have a “$” prefix and the “recordId” does. Additionally, the recordId value is the “items” property and “primaryColumnName” values combined as a single string with a “.” in-between. Also, note, you’re absolutely allowed to pass other column values in the params as well, not just the Id, if needed. Also, once you add a rowToolbarItems array to the grid/list, you’ll lose the out of the box actions for Open, Copy, and Delete. If you still want those, you’ll need to add those back. The are defined as follows – Be sure to change the “items” and “primaryColumnName” values as mentioned above (if this is a Section list it can likely stay the same as below, however for a list on a page those will be different):
// default open action
{
type: "crt.MenuItem",
caption: "DataGrid.RowToolbar.Open",
icon: "edit-row-action",
disabled: "$Items.PrimaryModelMode | crt.IsEqual : 'create'",
clicked: {
request: "crt.UpdateRecordRequest",
params: {
"itemsAttributeName": "Items",
"recordId": "$Items.PDS_Id",
}
}
},
// default copy action
{
type: "crt.MenuItem",
caption: "DataGrid.RowToolbar.Copy",
icon: "'copy-row-action",
disabled: "$Items.PrimaryModelMode | crt.IsEqual : 'create'",
clicked: {
request: "crt.CopyRecordRequest",
params: {
"itemsAttributeName": "Items",
"recordId": "$Items.PDS_Id",
}
}
},
// default delete action
{
type: "crt.MenuItem",
caption: "DataGrid.RowToolbar.Delete",
icon: "delete-row-action",
clicked: {
request: "crt.DeleteRecordRequest",
params: {
"itemsAttributeName": "Items",
"recordId": "$Items.PDS_Id",
}
},
}
Note, if you’re modifying an out of the box list page (Accounts_ListPage for example), then you won’t see the elements for the list in the page code. This is because they’re defined in the parent list page. However, you can merge in changes by adding something like the following to the viewConfigDiff:
{
"operation": "merge",
"name": "DataTable",
"values": {
"rowToolbarItems": [
// add any items here
]
}
}
Note, you’ll want to check the parent page to make sure that the list element “name” is correct. For section list pages it’s typically DataTable, however, for lists on pages (such as the Cases list on the Accounts_FormPage, etc) that will not be the case so you’ll need to check what the list name is to use for the merge element above.
Handling the Menu Item Request
Once the menu item is added, we’ll need to be able to handle the request, assuming this is a custom request and not an out of the box action (like the open, copy, delete requests above). For handling the request, we’ll add it to the requests of the page. In the first code sample in this article, where we’re adding the “cfx.SomeCustomHandler” request, we’ve added to the “params” a “recordId” param and populated it with the row’s primary Id value. That will get passed into the request as request.recordId:
{
request: "cfx.SomeCustomHandler",
handler: async (request, next) => {
const id = request.recordId;
// do something here
return next?.handle(request);
}
}
We can pass in other attributes bound in the grid as well. Not just the record Id. However, the column must exist in the data for the list. View how to add other columns to the list‘s data.
Once you start adding these row actions, you’ll realize they can be a big time saver & convenience for users. Plus they’re easy to implement so it’s a big win all around.




Hi Ryan. Thank you for this helpful article. But I have a question about ‘details’ in Freedom UI. Did you implement some virtual details in Freedom UI? We need to use some webservice to fetch data and just display it to the user in this ‘virtual detail’
Hello Alex,
I’ve started trying to piece together what is needed to accomplish this since it’s something I need to do as well. I’ve not yet gotten far enough to know if it’s possible and how to do it yet. So far, it seems like it could be possible, but not sure yet. If I end up making any progress on this, I’ll post the details here for sure.
Ryan
I’ve tried to analyze Terrasoft’s component ‘crt.DataGrid’ and there are several types of dataSource – EntityDataSource and WebServiceDataSource. So I think that it is possible, but we need to understand how to declare DataGrid with the second type of dataSource. Looking forward to your implementation. if I find a solution, I will definitely share it as well.
Hi Ryan. I think I have a solutuion.
1. Create an object.
2. Mark it as virtual.
3. Create a C# class that implement IEntitySchemaQueryExecutor. It has one method GetEntityCollection. Create some entites in it and return them.
(There are examples by Terrasoft, you can find them by interface’s name)
IMPORTANT – they use attributes for those classes and I think attribute’s naming is very important:
[DefaultBinding(typeof(IEntitySchemaQueryExecutor), Name = “UsrMyObjectQueryExecutor”)]
UsrMyObjectQueryExecutor – this name.
4. Currently it works with IEntitySchemaQueryExecutor, but it’s marked as obsolete and we need to use IEntityQueryExecutor.
Hi Ryan.
Is it possible to run a business process by clicking this button in the menu? I’m trying to do it but the process is not running
Thank you!
JC
Yes, definitely possible, I have several menu items on lists that start processes. See this article for starting the process: https://customerfx.com/article/starting-a-process-from-client-side-code-on-a-creatio-freedom-ui-page/
It’s also possible to wire up the “crt.RunBusinessProcessRequest” directly to the menu rather than execute a custom request and then start the process in code, but that way works as well.
Ryan
Is is possible to modify detail globally in Freedom UI? Not for one page
Not currently possible.
Ryan
I wonder how can we check the current number of records in the list form in the front-end code.
Also I wonder how can we access the data in individual lines, but for now I just need to know at least if the list currently is empty or not. This will be used as one of the conditions to decide whether we allow saving the record or not.
To be honest, I don’t know if that is possible. The list data is paged as you scroll, so I don’t believe it’s possible to get all the list data. Might be possible to get if it’s empty or not at least. Might try to see what you can find out from getting the list’s data source to view what is available.
Worst case, you could just do a model query to see what records exist (using the same filter as the bound list) to see if there are records added. See https://customerfx.com/article/querying-data-using-filter-conditions-via-the-model-class-equivalent-to-enityschemaquery-in-a-creatio-freedom-ui-page/
Ryan
I found a way to get the count, actually, which goes like this:
request.$context.attributes.ProductsList.length
where ProductList (in my case) is the code of the list. Haven’t checked all the aspects of this value but what’s important for me is that it’s 0 for empty list and seems to reflect the number of entries when the list is not empty… of course no documentation found here, just debugging the system.
Nice, thanks for sharing that
Hi Rayan,
Can we show and hide these buttons based on the specific value in a column in that list? For example, we have a status column. If the status is in draft stage, we should show the delete option; otherwise, we should hide it. This should affect every single row in the freedom UI detail.
Thank you.
Yes, I believe so, but I’ve not tried it. In the config you can bind list data attributes to things like disabled (and I assume visible as well). You’d need to ensure the values in are in the list data – see https://customerfx.com/article/including-additional-column-values-in-a-page-datasource-load-on-a-creatio-page/
You’d need to create your own converter for the page (in the sample above a converter example is “crt.IsEqual”). See https://academy.creatio.com/docs/8.x/dev/development-on-creatio-platform/platform-customization/freedom-ui/customize-page-fields/examples/implement-the-field-value-conversion
Then you could bind the property as follows (or something like this):
“disabled”: “$Items.PDS_UsrMyStage | usr.IsNotDraft”
Ryan