Hello,

In my Project's form page, I have two charts that summarize data from a Time Cards object in the form of a bar chart and a column chart. After placing the chart component onto my form page, I had trouble resizing the charts to make them larger. I attached below an image for reference. Any help on how to resize charts would be greatly appreciated. Please let me know if you have any questions or need me to further clarify. 

 

Best,

Shubh

Like 0

Like

2 comments
Best reply

If you drag them taller it will give more room for the chart bars/columns and the text will be more readable.

If you drag them taller it will give more room for the chart bars/columns and the text will be more readable.

Ryan Farley,

Thank you! This worked. I did not see the little square to drag the chart taller before.

Show all comments

Dear,

When starting Creatio we have the following errors :

Classic UI

It seems that Creatio is not finding some language files.

Can these mistakes be corrected ?

Thank you !
Nicolas

Like 0

Like

1 comments

Hello!

Try to change the language of the user to English, recompile the system. Then switch back to French and try to reproduce the error.

Have a nice day!

Show all comments

Hi Creatio Community!
I’m building a custom Freedom UI component and need some advice on how to pass data from the component back to the parent page.
I have a custom component with some internal logic and a variable whose value changes based on user interaction inside the component (e.g. clicks, selections).
Now I want to pass this variable's value back to the parent page where the component is used — for example, to trigger some business logic or store it in a page field.
Is there a way to work with the page context from within the custom component?
Or maybe there's a recommended pattern for passing data upward from a component to the parent page in Freedom UI?

Any examples or best practices would be greatly appreciated!

Thanks in advance

Like 2

Like

0 comments
Show all comments

Hi Creatio Community!
I’m currently working on a custom Angular component (Freedom UI) and ran into an issue I hope someone has already dealt with. I created a custom table component that loads data from a web service — the structure is quite complex with nested records, so I chose to use a service instead of standard object bindings.

I'm receiving the current record ID like this:

@Input()
@CrtInput()
public recordId!: { value: string; displayValue: string };

This is how I set recordId into component

{
	"operation": "insert",
	"name": "CustomTable",
	"values": {
		"recordId": "$UsrContractConditionsGroupDS_UsrContract_c00dfo4",
		"type": "usr.Table"
	},
	"parentName": "ListOfTarrifGroupByPeriod_ExpansionPanel",
	"propertyName": "items",
	"index": 0
},

When I open the page for the first time, everything works fine — the recordId arrives as expected. 

But when I back to section and open this record again, the component throws an error because recordId is undefined, and my table is empty.

I’m calling the service directly in ngOnInit().

Could you please advise on the correct way to store the record ID in the component so that it doesn’t get lost when opening the page a second time?

 

Like 1

Like

2 comments
Best reply

Hello Artem,

 

You can store record ID in the custom attribute and populate its value when the page is opening in the request crt.HandleViewModelInitRequest.

 

viewModelConfigDiff: /**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/[
	{
		"operation": "merge",
		"path": [
			"attributes"
		],
		"values": {
			"RecordId": {}
			...
		}
	}
	...
]
handlers: [
	{
		request: "crt.HandleViewModelInitRequest",
		handler: async (request, next) => {
			await next?.handle(request);
			request.$context.RecordId = request.$context.Id;
		}
	}
]

Then you can use this custom attribute in your component

 

{
	"operation": "insert",
	"name": "CustomTable",
	"values": {
		"recordId": "$RecordId",
		"type": "usr.Table"
	},
	"parentName": "ListOfTarrifGroupByPeriod_ExpansionPanel",
	"propertyName": "items",
	"index": 0
},

Hello Artem,

 

You can store record ID in the custom attribute and populate its value when the page is opening in the request crt.HandleViewModelInitRequest.

 

viewModelConfigDiff: /**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/[
	{
		"operation": "merge",
		"path": [
			"attributes"
		],
		"values": {
			"RecordId": {}
			...
		}
	}
	...
]
handlers: [
	{
		request: "crt.HandleViewModelInitRequest",
		handler: async (request, next) => {
			await next?.handle(request);
			request.$context.RecordId = request.$context.Id;
		}
	}
]

Then you can use this custom attribute in your component

 

{
	"operation": "insert",
	"name": "CustomTable",
	"values": {
		"recordId": "$RecordId",
		"type": "usr.Table"
	},
	"parentName": "ListOfTarrifGroupByPeriod_ExpansionPanel",
	"propertyName": "items",
	"index": 0
},

Iryna Oriyenko,

Thank you very much!

Show all comments

I'm looking for help with setting a default tab in a Freedom UI page.

I want the "Lead Info" tab (which is the first tab) to always be selected by default when opening a lead record. However, after I perform or complete an activity and return to the Lead page, it automatically opens the "Lead Activities" tab instead of the "Lead Info" tab.

Is there a way to set the page to always open the "Lead Info" tab, regardless of the last active tab or navigation history?

Like 0

Like

2 comments

You can set the active tab using something like this (where "Tabs" is the name of the tab control): 

request.$context.Tabs_SelectedTabIndex_Profile = 0;

I've not tried setting that in the crt.HandleViewModelInitRequest, but it could work - although might need to wrap in a setTimeout just to break out of the message loop and let the page load first and for the saved user profile for selected tab is loaded first (I don't believe there is a way to prevent that). Try something like this:

{
    request: "crt.HandleViewModelInitRequest",
    handler: async (request, next) => {
        setTimeout(() => { request.$context.Tabs_SelectedTabIndex_Profile = 0; }, 1000);
        return await next?.handle(request);
    }
}

Might need to play with the timeout interval (the 1000), although it's not 100% it will work.

Ryan

Try using this code in the  handler:

      {

      request: "crt.HandleViewModelInitRequest",

      handler: async (request, next) =>{

        request.$context.Tabs_SelectedTabIndex_Profile=0;

        return next?.handle(request);

      }

    }

Show all comments

Original Reference: Ryan Farley, “Getting Multi‑select Records from a Creatio Freedom UI List via Code” Customer FX, Jan 30, 2024 

 

The Challenge

In Creatio 8.1+, lists support multi‑selection. Ryan’s post shows how to bind a custom bulk‑action button and read the DataTable_SelectionState attribute:

const selectedIds = (await request.$context.DataTable_SelectionState).selected;

However, this only covers the case where users manually pick some rows (“specific” selection). If they click “Select All,” the selected array is empty and selectionState.type === "all", with an unselected array instead. Nor does the basic snippet iterate through all pages of data.

An Alternative, Complete Handler

The code below demonstrates how to:

  1. Detect whether the user chose a subset (“specific”) or all records (“all”).
  2. Extract the true list of record IDs in both cases.
  3. Execute a business process for each record.
  4. Access other context values (e.g. an Opportunity ID wrapped in a  proxy).
{
 request: "cfx.ButtonClicked",
 handler: async (request, next) => {
   // 1. Retrieve selection state
   const selectionState = await request.$context.DataTable_SelectionState;
   // 2. Grab manual selections (if any)
   const selectedIds = selectionState?.selected || [];
   // 3. Retrieve another context value (example: Opportunity ID)
   const opportunityProxy = await request.$context.UsrEntity_66d2fb2DS_UsrOpportunityId_dppjv0g;
   const opportunityId = opportunityProxy?.value;
   // Helper: execute business process
   const runBP = async (recordId) => {
     return request.$context.executeRequest({
       type: "crt.RunBusinessProcessRequest",
       processName: "UsrProcess_4e23e14",
       processParameters: {
         ProcessSchemaParameter1: recordId,
         opportunityId
       },
       $context: request.$context
     });
   };
   // 4. Handle specific selections
   if (selectionState.type === "specific") {
     if (!selectedIds.length || !opportunityId) {
       console.error("No records selected or missing Opportunity ID.");
       return;
     }
     for (const id of selectedIds) {
       await runBP(id);
     }
   // 5. Handle “select all”
   } else if (selectionState.type === "all") {
     // a. Get IDs the user explicitly un‑selected (if any)
     const unselected = selectionState.unselected || [];
     // b. Read the full page of items bound to the list
     const items = await request.$context.Items;
     if (!Array.isArray(items)) {
       console.error("Items binding is not an array.");
       return;
     }
     // c. Build a final list of IDs: include every item not in unselected
     const allIds = items
       .map(item => item.PDS_Id.__zone_symbol__value)
       .filter(id => !unselected.includes(id));
     if (!allIds.length || !opportunityId) {
       console.error("No records to process or missing Opportunity ID.");
       return;
     }
     for (const id of allIds) {
       await runBP(id);
     }
   }
   // 6. Continue the chain
   return next?.handle(request);
 }
}
How This Works
  • selectionState.selected vs. .unselected:
    • When users pick specific rows, selected holds their IDs.
    • When they click the header checkbox (“Select All”), the engine treats it as “all except any I un‑checked,” so selected is empty and type === "all". The unselected array lists exceptions.
  • Reading the full list (Items):
    • You only get the current page of records in the Items binding. If your grid is paginated, you’ll need to iterate through pages server‑side or adjust your viewModel to load all records you intend to process.

 

 

 

Like 1

Like

Share

1 comments

This is fantastic, thanks for sharing! 

For list's that are multi-select I believe there is also the following (with likely the same results, returns an array of Ids):

request.$context.MyList_SelectedRows

At least that was the case, haven't verified that is still present. 

Also, if the list isn't multi-select (meaning no checkboxes), then I don't believe SelectedRows or SelectionState has anything for the single selected row. For non-multi-select lists you'd get the selected row using (return's the row's Id): 

request.$context.MyList_ActiveRow

Ryan

Show all comments

Hello,

Could I get some help understanding or directing me to an article that thoroughly explains the lookup titled "Package in installed application?"

What does the "Primary" check box do? What does the "Current package" check box do?

Can a package be named in more than one row, aka, more than one application?

I cannot locate an academy article explaining this function.

Like 0

Like

1 comments

How can i add new page parameter in pre-configured page in default creatio pre-configured page in freedom ui

Like 1

Like

2 comments

In the page designer, expand page parameters and add them there

Ryan

Hi Ryan Farley,

i just want to display the existing data source and the parameter, can i add to the parameter on the pre-configured page with the existing default parameter?

because in here i have most of the parameter but,

on the pre-configured page i just have some of them

Show all comments

I have two attachment pages, but when I attach a file on the first page, it also appears on the second page. How can I implement a filter so that the attachment only shows on the page where it was uploaded? i'm using FreedomUI.

 

Like 0

Like

2 comments
Best reply

You can use tags. The File objects have a Tag field, you can set up each file list to have a different tag, then the tag will get added to the file based on which list the file was added to, and the lists will also filter by this tag.

To do this, you simply need to enter a tag name for each of the file lists, just type in a value in the File tag property: 

Ryan

You can use tags. The File objects have a Tag field, you can set up each file list to have a different tag, then the tag will get added to the file based on which list the file was added to, and the lists will also filter by this tag.

To do this, you simply need to enter a tag name for each of the file lists, just type in a value in the File tag property: 

Ryan

Ryan Farley,

Thank you Ryan

Show all comments

After adding records ,How can I reload a detail list in a Freedom UI page from a task script within a business process, without using the live update feature in the object?
Thank you

Like 0

Like

3 comments

Note that i'add this code in the task script in the process 


var userConnection = Get<UserConnection>("UserConnection");
string messageText = "Successful";

Terrasoft.Configuration.MsgChannelUtilities.PostMessageToAll("ReloadDetail", messageText);
return true;    

i got this error message 
"The type or namespace name 'MsgChannelUtilities' does not exist in the namespace 'Terrasoft.Configuration' (are you missing an assembly reference?"

 

 

"The type or namespace name 'MsgChannelUtilities' does not exist in the namespace 'Terrasoft.Configuration' (are you missing an assembly reference?" - this error message will occur in case the business process is created in the assembly package. The only workaround is to uncheck the "Assembly package" property in the package settings or move the process to the regular package.

 

As for the possibilities and if LiveUpdate is not an option - yes, socket nessage to the client-side and triggering the LoadDataRequest with reload enabled is the only option.

I also encountered this same error, but in my case, it was due to a dependency issue. I resolved it by adding the 'crtBase' package as a dependency in the package containing the business process.

Show all comments