Custom Freedom UI component implemented using remote module
The element library of the Freedom UI Designer includes a wide set of out-of-the-box components that cover most Freedom UI page customizations. For business scenarios that require something more specific — for example, a specialized input field, a custom data visualization, or a third-party widget — Creatio lets you build your own UI components and add them to the library.
Creatio lets you implement Freedom UI components of the following types:
- Custom Freedom UI component implemented using remote module.
- Custom Freedom UI component based on a Classic UI element. Learn more: Custom Freedom UI component based on a Classic UI element.
The development of a custom Freedom UI component using a remote module is based on the Module Federation plugin that divides the app into multiple smaller modules built independently. The plugin lets you develop custom Freedom UI components using various frameworks and library versions. Learn more: Module Federation (official vendor documentation).
When using the Module Federation plugin, you might experience difficulties with managing libraries that register themselves as global variables, for example, lodash.
The remote module template includes runtime and design default features. The runtime feature implements Freedom UI components and related business logic. The design feature implements Freedom UI Designer setup areas. If needed, you can add more features to split the remote module into smaller parts that Creatio loads separately.
The runtime feature definition publishes the feature discovery metadata in the discovery section, for example, public view element types and other information Creatio uses to resolve remote runtime contributions. The same definition loads designer metadata in the loadDesignerDefinitions() function and activates the runtime implementation when Creatio requests the feature. This approach is intended for advanced scenarios.
The remote module template contains a RemoteEntryDefinition object exported from the "src/main.ts" file. The RemoteEntryDefinition defines the RemoteFeatureDefinition objects provided by the package.
Example that creates a custom Freedom UI component using remote module: Implement a custom Freedom UI component using remote module.
To implement a custom Freedom UI component using a remote module, complete the following steps.
1. Create an Angular project
This procedure covers the custom Freedom UI component development in Microsoft Visual Studio Code. We recommend using components created using the Angular framework. The framework requires a globally installed @angular/cli command-line interface (Angular CLI). Run the npm i -g @angular/cli command in the Visual Studio Code terminal to set up the environment for component development using Angular CLI.
A custom Freedom UI component lets you include an external JS library. To do this, install the release version of the library using npm.
To create an Angular project via the Clio utility:
-
Run the
dotnet tool install clio -gcommand in the Visual Studio Code terminal to install the Clio utility (performed once). -
Run the
clio reg-web-app some_application_name -u https://mycreatio.com/ -l SomeLogin -p SomePasswordcommand in the Visual Studio Code terminal to register a new Creatio instance in Clio.Where:
some_application_nameis the Creatio instance name.https://mycreatio.com/is the Creatio instance URL.SomeLoginis the user login to the Creatio instance.SomePasswordis the user password to the Creatio instance.
-
Run the
clio install-gate some_application_namecommand in the Visual Studio Code terminal to install thecliogatesystem package into your development environment. -
Run the
clio restart some_application_namecommand in the Visual Studio Code terminal to restart your Creatio instance. -
Run the
clio createwcommand in the Visual Studio Code terminal to create a new workspace. -
Run the
clio ui some_application_name -v usr --package SomePackageName --empty truecommand in the Visual Studio Code terminal to create a project for the remote module, whereSomePackageNameis the name of the package to implement the remote module. If the workspace does not contain a package that has the specified name, Visual Studio Code creates a new package that has the specified name. -
Run the
cd some_directory/some_application_namecommand in the Visual Studio Code terminal to move to the project directory.
As a result, an Angular project to develop a custom UI component using a remote module will be added.
The project already contains:
- The remote entry exported from "src/main.ts."
- The runtime feature files.
- The optional design feature files that you extend when implementing a custom component.
2. Install npm packages
- Run the
npm icommand in the Visual Studio Code terminal to install thenpmpackages. - Run the
npm i @creatio-devkit/commoncommand in the Visual Studio Code terminal to update the@creatio-devkit/commonlibrary version. - Run the
npm i @creatio/interface-designercommand in the Visual Studio Code terminal to install the@creatio/interface-designerlibrary. The project must include this library, which provides the API for interacting with the Freedom UI page schema from the setup area. - Run the
npm i class-transformercommand in the Visual Studio Code terminal to install theclass-transformerlibrary. - Run the
npm run buildcommand in the Visual Studio Code terminal to build the project.
As a result, required npm packages are installed.
3. Create a custom Freedom UI component
-
Add the "AGENTS.md" file to the project. The project must include the "AGENTS.md" file that contains instructions for coding agents like Copilot. It describes how the agent should behave in that project: coding rules, workflow expectations, tool usage, review standards, and any repo-specific constraints.
-
If you work with an existing remote module project, create the "AGENTS.md" file in the project root with the following content.
"AGENTS.md" file## Remote Module Reference Guides**MANDATORY**: Read and follow these guides before working on any task related to Creatio Freedom UI integrations with remote modules:Runtime components: node_modules\@creatio-devkit\common\AI_GUIDES_INDEX.mdDesign-time components (if relevant): node_modules\@creatio\interface-designer\AI_GUIDES_INDEX.md -
If you create a new project from the remote module template, the "AGENTS.md" file is already included.
-
-
Run the
ng g c features/runtime/view-elements/some-component-name --view-encapsulation=ShadowDomcommand in the Visual Studio Code terminal to create an Angular component in the project.Where:
- "features/runtime/view-elements" specifies that the Angular component is added to the runtime feature.
some-component-nameis the custom component name.
This adds the component files to the "src/app/features/runtime/view-elements/some-component-name" directory.
-
Define the component constants.
-
Go to the "app/features/runtime/view-elements/some-component-name" directory.
-
Create the "some-component-name.constants.ts" file.
-
Open the "some-component-name.constants.ts" file.
-
Export the following constants that define:
- the view element type
- the view element selector
- the component icon
-
Save the file.
"some-component-name.constants.ts" file/* The type of the view element.*/export const ELEMENT_TYPE = 'usr.SomeComponentName';/* The selector of the view element. */export const ELEMENT_SELECTOR = 'usr-some-component-name';/* Define the component icon as an inline SVG. */export const ELEMENT_ICON = `<svg ...>...</svg>`; -
-
Define the runtime feature ID.
- Open the "src/app/features/runtime/runtime-feature.ids.ts" file.
- Export the constant that defines the runtime feature ID.
- Save the file.
"runtime-feature.ids.ts" file/* The ID of the runtime feature. */export const RUNTIME_FEATURE_ID = 'some-package-name-runtime'; -
Specify that the
SomeComponentNameComponentis a view element.- Open the "some-component-name.component.ts" file.
- Flag the component using the
@CrtViewElementdecorator. - Import the required functionality from the libraries into the file.
- Save the file.
"some-component-name.component.ts" file/* Import the required functionality from the libraries. */import { Component, OnInit, ViewEncapsulation } from '@angular/core';import { CrtViewElement } from '@creatio-devkit/common';import { ELEMENT_TYPE, ELEMENT_SELECTOR } from './some-component-name.constants';@Component({selector: 'usr-some-component-name',templateUrl: './some-component-name.component.html',styleUrls: ['./some-component-name.component.scss'],encapsulation: ViewEncapsulation.ShadowDom})/* Register the component as a Freedom UI view element. */@CrtViewElement({selector: ELEMENT_SELECTOR,type: ELEMENT_TYPE,})export class SomeComponentNameComponent implements OnInit {} -
Register the
SomeComponentNameComponentview element in the runtime feature.The template already contains the runtime feature activation and
bootstrapCrtModule()call. Extend the existing files to register the custom component.-
Open the "runtime-feature.module.ts" file.
-
Add the
SomeComponentNameComponentview element to the@CrtModuledecorator and Angular module declarations."runtime-feature.module.ts" file/* Import the required functionality from the libraries. */import { NgModule } from '@angular/core';import { CommonModule } from '@angular/common';import { CrtModule } from '@creatio-devkit/common';import {SomeComponentNameComponent} from './view-elements/some-component-name/some-component-name.component';@CrtModule({/* Specify that SomeComponentNameComponent is a view element. */viewElements: [SomeComponentNameComponent]})@NgModule({declarations: [SomeComponentNameComponent],imports: [CommonModule],})export class RuntimeFeatureModule {} -
Open the "runtime.feature-definition.ts" file.
-
Add the public type of the
SomeComponentNameComponentview element to thediscovery.viewElementssection."runtime.feature-definition.ts" file/* Import the required functionality from the libraries. */import type {RemoteFeatureDefinition} from '@creatio-devkit/common';import { ELEMENT_TYPE } from './view-elements/some-component-name/some-component-name.constants';import { RUNTIME_FEATURE_ID } from './runtime-feature.ids';/*** Root definition for the runtime feature.** Describes the view elements this remote package contributes at runtime* and provides the entry points used to load designer metadata and* activate the feature.*/export const runtimeFeatureDefinition = {id: RUNTIME_FEATURE_ID,discovery: {viewElements: [{ type: ELEMENT_TYPE },],},loadDesignerDefinitions: (context) =>import('./runtime.designer-definitions').then((m) => m.loadRuntimeDesignerDefinitions(context)),activate: () =>import('./runtime.feature-activation').then((m) => m.activateRuntimeFeature()),} satisfies RemoteFeatureDefinition; -
Open the "runtime.feature-activation.ts" file.
-
Define the
SomeComponentNameComponentview element as an Angular Element. Learn more: Angular elements overview (official vendor documentation)."runtime.feature-activation.ts" file/* Import the required functionality from the libraries. */import { Injector, ProviderToken, Type } from '@angular/core';import { createCustomElement } from '@angular/elements';import { bootstrapCrtModule } from '@creatio-devkit/common';import { ensureFeatureModuleRef, ensureRemoteAppContext } from '../../remote-app-context';import { RuntimeFeatureModule } from './runtime-feature.module';import {SomeComponentNameComponent} from './view-elements/some-component-name/some-component-name.component';import { REMOTE_ENTRY_NAME } from '../../remote-entry.ids';import { ELEMENT_SELECTOR } from './view-elements/some-component-name/some-component-name.constants';function defineCustomElement(selector: string,component: Type<unknown>,injector: Injector): void {if (!customElements.get(selector)) {customElements.define(selector, createCustomElement(component, { injector }));}}/* Initialize the runtime feature module and boot the Creatio runtime integration. */export async function activateRuntimeFeature(): Promise<void> {await ensureRemoteAppContext();const moduleRef = await ensureFeatureModuleRef(RuntimeFeatureModule);const injector = moduleRef.injector;defineCustomElement(ELEMENT_SELECTOR, SomeComponentNameComponent, injector);bootstrapCrtModule(REMOTE_ENTRY_NAME, RuntimeFeatureModule, {resolveDependency: (token) =>injector.get(token as ProviderToken<unknown>),});}The remote module template includes the "remote-app-context.ts" file that exports
ensureFeatureModuleRef. The file bootstraps the remote application module and caches feature module references across calls."remote-app-context.ts" file/* Import the required functionality from the libraries. */import { Injector, NgModuleRef, Type, createNgModule } from '@angular/core';import { platformBrowser } from '@angular/platform-browser';import { RemoteAppModule } from './remote-app.module';export interface RemoteAppContext {injector: Injector;moduleRef: NgModuleRef<RemoteAppModule>;}let remoteAppContextPromise: Promise<RemoteAppContext> | undefined;const featureModuleRefs = new Map<Type<unknown>, NgModuleRef<unknown>>();async function bootstrapRemoteApp(): Promise<RemoteAppContext> {const moduleRef = await platformBrowser().bootstrapModule(RemoteAppModule);const injector = moduleRef.injector;return {injector,moduleRef,};}async function ensureRemoteAppContext(cultureName?: string): Promise<RemoteAppContext> {if (!remoteAppContextPromise) {remoteAppContextPromise = bootstrapRemoteApp().catch((error: unknown) => {remoteAppContextPromise = undefined;throw error;});}return remoteAppContextPromise;}export async function ensureFeatureModuleRef<T>(moduleType: Type<T>): Promise<NgModuleRef<T>> {const existingModuleRef = featureModuleRefs.get(moduleType) as NgModuleRef<T> | undefined;if (existingModuleRef) {return existingModuleRef;}const { injector } = await ensureRemoteAppContext();const moduleRef = createNgModule(moduleType, injector);featureModuleRefs.set(moduleType, moduleRef);return moduleRef;} -
Save the files.
-
-
Run the
npm run buildcommand in the Visual Studio Code terminal to build the project.
As a result, the build will be added to the "dist" directory of the Angular project.
4. Implement the business logic of the custom Freedom UI component
-
Implement the business logic of the component.
- Open the "src/app/features/runtime/view-elements/some-component-name/some-component-name.component.ts" file.
- Add the
valueproperty to theSomeComponentNameComponentcomponent class. The property manages the component value. - Flag the
valueproperty using the@Inputand@CrtInputdecorators. - Import the required functionality from the libraries into the file.
- Save the file.
"some-component-name.component.ts" file/* Import the required functionality from the libraries. */import { Component, Input, OnInit } from '@angular/core';import { CrtInput, CrtViewElement } from '@creatio-devkit/common';export class SomeComponentNameComponent implements OnInit {/* Add decorators to the value property. */@Input()@CrtInput()/* The component value. */public value: some_value_type;} -
Add the markup of the custom component to the "some-component-name.component.html" file.
-
Add the styles of the custom component to the "some-component-name.component.scss" file.
-
Run the
npm run buildcommand in the Visual Studio Code terminal to build the project.
As a result, the build will be added to the "dist" directory of the Angular project.
Examples that implement additional business logic of the custom Freedom UI component: Implement the validation in the custom Freedom UI component, Implement a custom converter using remote module.
5. Add the custom Freedom UI component to the Freedom UI Designer (optional)
We recommend adding the custom Freedom UI component to the library of the Freedom UI Designer if you are going to use the component as part of no-code development.
To add the custom Freedom UI component to the library of the Freedom UI Designer:
-
Set up the component layout.
- Open the "runtime.designer-definitions.ts" file.
- Import the required functionality from the libraries into the file.
- Add the metadata of the custom component to the
viewElementscollection returned by theloadRuntimeDesignerDefinitions()function. - Specify the same public
typethat you registered in the "runtime.feature-definition.ts" file. - Specify the path to the image in the
toolbarConfig.iconproperty. You can specify the path in the constant and use its value in thetoolbarConfig.iconproperty. Make sure the icon meets the Requirements for icons of custom Freedom UI page components. - Set up the
toolbarConfigproperty that manages the element layout in the library of the Freedom UI Designer. - Specify the default property values of the component in the
defaultPropertyValuesproperty. - Save the file.
- Localize the custom Freedom UI component if needed. Instructions: Localize custom Freedom UI component implemented using remote module.
"runtime.designer-definitions.ts" file/* Import the required functionality from the libraries. */import type {RemoteDesignerDefinitionsLoadContext,RemoteFeatureDesignerDefinitions,} from '@creatio-devkit/common';import { ELEMENT_ICON, ELEMENT_TYPE } from './view-elements/some-component-name/some-component-name.constants';export async function loadRuntimeDesignerDefinitions(context: RemoteDesignerDefinitionsLoadContext): Promise<RemoteFeatureDesignerDefinitions> {return {viewElements: [{type: ELEMENT_TYPE,toolbarConfig: {caption: 'Some component name',/* The path to the component image. */icon: ELEMENT_ICON,},defaultPropertyValues: {label: 'Some component name'}}]};} -
Create Freedom UI Designer setup area for a custom Freedom UI component if needed. Instructions: Freedom UI Designer setup area for a custom Freedom UI component.
-
Run the
npm run buildcommand in the Visual Studio Code terminal to build the project.
As a result, the build will be added to the "dist" directory of the Angular project.
6. Add the custom Freedom UI component to the Freedom UI page
-
Create a custom app if needed. Instructions: Create an app manually (user documentation).
-
Upload packages to Creatio using the Clio utility.
- Go to the "../Terrasoft.WebApp/Terrasoft.Configuration/Pkg/SomePackage/Files" directory.
- Create a "src/js" directory.
- Copy the build artifacts from the "dist" project directory to the "../Terrasoft.WebApp/Terrasoft.Configuration/Pkg/SomePackage/Files/src/js" directory.
- Run the
clio pushw -e SomeApplicationNamecommand in the Visual Studio Code terminal to upload the changes to Creatio.
-
Open the needed app in the Application Designer.
-
Open the needed page in the Freedom UI Designer.
-
Drag the custom Freedom UI component to the canvas.
-
Click
→ Source code to open the source code of the Freedom UI page. -
Bind the
valueproperty of the custom Freedom UI component to the corresponding model attribute in theviewConfigDiffschema section.viewConfigDiff schema sectionviewConfigDiff: /**SCHEMA_VIEW_CONFIG_DIFF*/[{"operation": "insert","name": "SomeComponentName","values": {/* The property that defines the element type. */"type": "usr.SomeComponentName",/* The property that defines the component value. Bound to theSomeAttributeName attribute. */"value": "$SomeAttributeName"}}]/**SCHEMA_VIEW_CONFIG_DIFF*/, -
Click Save.
As a result, the custom Freedom UI component will be added to the Freedom UI page. When the page loads, Creatio resolves the component type declared in the runtime feature and activates its implementation.
See also
Custom Freedom UI component based on a Classic UI element
Custom converter implemented using remote module
Operations with schemas in Creatio IDE
Requirements for icons of custom Freedom UI page components
Resources
Angular elements overview (official vendor documentation)
Module Federation (official vendor documentation)