Article

Passing Data Between Freedom UI Pages in Creatio

Passing Data Between Freedom UI Pages in Creatio (Real Project Case)

Introduction

This article is based on a real Freedom UI use case, not a hypothetical demo.

The requirement comes from an actual configuration scenario:

  • A Freedom UI form page (UsrSmartFilter_FormPage)
  • A custom Freedom UI component/page (UsrColumnViewerComponent)
  • The need to pass runtime values (object name + record Id)
  • And then persist user selections back into the database

No sandbox. No assumptions. Only what works in Freedom UI today.

Real Problem Statement

In a Freedom UI implementation, the user:

  1. Opens UsrSmartFilter_FormPage
  2. Selects an object (for example: Account, Contact, etc.)
  3. Clicks a button to configure columns
  4. A second Freedom UI page/component opens
  5. That page must:
    • Know which object was selected
    • Know which UsrSmartFilter record is being edited
    • Show all columns of the selected object
    • Save selected column names back to UsrSmartFilter.UsrColumnNames_Values

Classic UI sandbox messaging cannot be used.

Why Sandbox and Page Parameters Cannot Be Used

In Freedom UI:

  • Pages are isolated
  • There is no exposed sandbox API
  • Parameters passed via crt.OpenPageRequest are not injected into the target page context

This is a documented and observed platform behavior, not a theory.

The Working Mechanism: BroadcastChannel

The solution that works reliably is using the browser-native <strong>BroadcastChannel</strong> API.

This is not a Creatio abstraction. It is a standard Web API that works because:

  • Freedom UI pages run in the same browser context
  • Messaging happens at the browser level
  • No Creatio internals are bypassed

Real Sender: UsrSmartFilter_FormPage Handler

This handler exists on UsrSmartFilter_FormPage and is triggered by a button click.

What It Sends

  • Selected object schema name
  • Current page record Id (UsrSmartFilter.Id)

Real Handler Code

{
	request: "QNT.GetColumns",
	handler: async (request, next) => {
		const objectValue = request.$context.PDS_UsrSelectedObject_n4di2v4;
		const pageId = request.$context.Id;
 
		if (!objectValue || !pageId) {
			return next?.handle(request);
		}
 
		const channel = new BroadcastChannel("UsrSmartFilter_Channel");
 
		channel.postMessage({
			selectedObjectName: objectValue.displayValue,
			pageId: pageId
		});
 
		channel.close();
 
		return next?.handle(request);
	}
}

This code is taken directly from a working Freedom UI page.

Real Receiver: UsrColumnViewerComponent

UsrColumnViewerComponent is a custom Freedom UI component responsible for:

  • Receiving the object name and page Id
  • Loading the selected object schema dynamically
  • Allowing the user to select columns
  • Persisting the selection

Receiving the Data

this._channelIn = new BroadcastChannel("UsrSmartFilter_Channel");
 
this._channelIn.onmessage = async (event) => {
	const { selectedObjectName, pageId } = event.data || {};
 
	this._selectedObjectName = selectedObjectName;
	this._parentPageId = pageId;
 
	await this._loadObjectSchema();
	this._render();
};

This logic runs when the component is initialized.

Loading the Object Schema Dynamically

Instead of hardcoding Account, the schema is loaded dynamically using the received object name:

const model = await sdk.Model.create(this._selectedObjectName);
const schema = await model.getSchema();
 
this._columns = Object.values(schema.attributes).map(attr => ({
	name: attr.name,
	caption: attr.caption || attr.name
}));

This is standard Freedom UI Model API usage.

Persisting the Selected Columns (Real Persistence)

When the user selects or unselects columns, the component:

  1. Builds a comma-separated string
  2. Updates the existing UsrSmartFilter record
const model = await sdk.Model.create("UsrSmartFilter");
 
await model.update({
	Id: this._parentPageId,
	UsrColumnNames_Values: columnsCsv
});

This is not messaging — this is actual data persistence.

Why This Is a Real, Production-Safe Pattern

  • Uses only documented Web APIs
  • Uses Creatio Freedom UI Model API
  • No reliance on undocumented sandbox features
  • Works across page reloads once persisted

This pattern has been validated in real projects.

Key Constraints (Real, Not Theoretical)

  • BroadcastChannel does not queue messages
  • Receiver must be initialized first
  • Transient data should always be persisted

Ignoring these leads to real bugs.

Conclusion

This article does not describe a hypothetical demo.

It documents a real Freedom UI implementation pattern for:

  • Passing data between pages
  • Dynamically loading object metadata
  • Persisting user configuration

Until Creatio provides an official sandbox replacement for Freedom UI, this is the correct approach.

Final Takeaway

Freedom UI pages do not share state.
Browser-level messaging + Model API persistence is the only reliable solution today.

This is based on actual working code, not assumptions.

Like 2

Like

Share

1 comments

Thanks for this write up. I've been using the native BroadcastChannel API for communication in Freedom UI and works fantastically!

It's important to make sure the receiver also gets disconnected to allow garbage collection. For the sender, it's easy to just call channel.close(); after sending, but the listener is typically initialized in the crt.HandleViewModelInitRequest on the receiving page. For this, I will put a reference to the channel object in an attribute on the receiving page (how to use attributes) and then dispose/close in the crt.HandleViewModelDestroyRequest:

{
    request: "crt.HandleViewModelDestroyRequest",
    handler: async (request, next) =&gt; {
        const channel = await request.$context.MyChannelAttr;
        if (channel) {
        	channel.close();
        	request.$context.MyChannelAttr = null;
        }
        return next?.handle(request);
    }
}

Ryan

Show all comments