Skip to main content
Version: 10

Freedom UI Designer setup area for a custom Freedom UI component

Level: advanced

The Freedom UI Designer lets no-code creators build and customize pages without writing code. When developers extended the designer with custom Freedom UI components, no-code creators could only add those components to a page. Configuring any property of such a component required a developer to modify the source code of the Freedom UI page directly.

Creatio lets developers implement a custom setup area for a custom Freedom UI component using a remote module. The setup area can be implemented as part of the same remote module as the corresponding Freedom UI component itself. A setup area implemented using a remote module behaves the same way as the setup areas of out-of-the-box components:

  1. Opens in the Freedom UI Designer when a user selects the corresponding Freedom UI component on the canvas.
  2. Reads the current component properties from the Freedom UI page schema.
  3. Stores any changes back to the schema immediately.
  4. Reflects them on the canvas in real time.

It can expose any properties of the component, giving no-code creators full control over the component configuration directly in the designer.

Example: Implement a Freedom UI Designer setup area for a custom Freedom UI component.

To implement a custom setup area for a custom Freedom UI component, complete the following steps.

1. Create an Angular project​

To create an Angular project to develop a custom setup area, follow the instructions: Create an Angular project.

2. Install npm packages​

To install npm packages, follow the instructions: Install npm packages.

3. Create a custom Freedom UI component​

To implement a custom Freedom UI component, follow the instructions: Custom Freedom UI component implemented using remote module.

4. Create the setup area component​

  1. Ensure the project includes the "AGENTS.md" file. Instructions: Create a custom Freedom UI component (step 1).

  2. Run the ng g c features/design/property-panels/some-component-setup-area command in the Visual Studio Code terminal to create an Angular class in the project, where some-component-setup-area is a custom class name. This adds the SomeComponentSetupAreaComponent class files to the "src/app/features/design/property-panels" project directory.

  3. Define the component constants.

    1. Go to the "src/app/features/runtime/view-elements/some-component-setup-area" directory.

    2. Create the "some-component-setup-area.constants.ts" file.

    3. Open the "some-component-setup-area.constants.ts" file.

    4. Export the following constants that define:

      • setup area type, for example, SOME_SETUP_AREA_TYPE_CONSTANT
      • property codes, for example, SOME_PROPERTY_CODE
      • default property values, for example, SOME_DEFAULT_VALUE
    5. Save the file.

  4. Specify that the SomeComponentSetupAreaComponent is a view element.

    1. Open the "some-component-setup-area.component.ts" file.
    2. Flag the component using the @CrtViewElement decorator.
    3. Import the required functionality from the libraries into the file.
    4. Save the file.
    "some-component-setup-area.component.ts" file
    /* Import the required functionality from the libraries. */
    import {
    CrtViewElement
    } from '@creatio-devkit/common';

    /* Define the setup area type identifier. */
    export const SOME_SETUP_AREA_TYPE_CONSTANT = 'usr.SomeComponentSetupArea';

    /* Define the property codes used in the Freedom UI page schema. */
    export const SOME_PROPERTY_CODE = 'someProperty';

    /* Define the default property values. */
    export const SOME_DEFAULT_VALUE = 'someDefaultValue';

    @CrtViewElement({
    selector: 'usr-some-component-setup-area-component',
    type: SOME_SETUP_AREA_TYPE_CONSTANT
    })

    export class SomeComponentSetupAreaComponent { }

5. Implement the business logic of the setup area component​

  1. Open the "some-component-setup-area.component.ts" file.

  2. Add the viewNodeEditor setter to implement the PropertyPanel interface. The PropertyPanel interface defines the contract for the setup area implemented using a remote module. The Freedom UI Designer calls the viewNodeEditor setter when a user selects the corresponding Freedom UI component on the canvas.

    "some-component-setup-area.component.ts" file
    import { ViewNodeEditor } from '@creatio/interface-designer';

    export interface PropertyPanel {
    /* Provides methods to edit the schema view node the property panel is
    opened in the Freedom UI Designer. */
    set viewNodeEditor(nodeEditor: ViewNodeEditor);
    }

    When switching between components of the same type, Creatio reuses the setup area instance and calls the setter again with a new ViewNodeEditor. The implementation must handle multiple setter calls on the same instance correctly and reload state each time. To handle this, delegate initialization to a private async method called from the setter. The _init() method stores the nodeEditor for later use, reads the current property values from the schema, and marks the setup area as ready to display.

    "some-component-setup-area.component.ts" file
    @Input()
    @CrtInput()
    public set viewNodeEditor(nodeEditor: ViewNodeEditor) {
    this._init(nodeEditor).catch(
    (error) => console.error('Error initializing setup area:', error)
    );
    }

    /* Store the view node editor, read the current property values from the
    schema, and mark the setup area as ready to display. */
    private async _init(nodeEditor: ViewNodeEditor): Promise<void> {
    /* Store the view node editor for later use when reading or writing
    property values. */
    this._viewNodeEditor = nodeEditor;

    /* Read the current property values from the schema. Implement this
    method in step 4. */
    await this._loadElementData();

    /* Mark the setup area as ready once initialization is complete. */
    this.isPanelReady.set(true);
    }
  3. Initialize InterfaceDesignerSchemaService to access view model attributes and data sources.

    "some-component-setup-area.component.ts" file
    private readonly _schemaEditor = new InterfaceDesignerSchemaService().getSchemaEditor();

    InterfaceDesignerSchemaService provides a getSchemaEditor() method that returns a SchemaEditor. SchemaEditor is the entry point for reading and modifying the Freedom UI page schema — it groups together the sub-editors for working with UI elements, the data model, and the view model. The getSchemaEditor() method throws an error if the SchemaEditor is not available. In the designer runtime, this call is safe to make during component initialization. The code below shows the SchemaEditor structure.

    SchemaEditor structure
    interface SchemaEditor {
    /* For working with UI elements. */
    viewEditor: SchemaViewEditor;
    /* For working with the data model. */
    modelEditor: SchemaModelEditor;
    /* For working with the view model. */
    viewModelEditor: SchemaViewModelEditor;
    }
  4. Implement the method that reads the current property values from the Freedom UI page schema asynchronously on setup area initialization. Use ViewNodeEditor to read and write property values through the methods described in the table below.

    Method

    Description

    getPropertyValue(propertyName)

    Reads the current value of a property.

    setPropertyValue(propertyName, options)

    Writes a new value to a property.

    Both methods are asynchronous and return promises. Always use await when calling them.

    A property value is a typed object that describes how a value is assigned in the schema. The table below lists the available property value types and how each type appears in the Freedom UI page schema JSON.

    Type

    Description

    Example of schema JSON

    Constant

    A literal value stored directly in the schema (a string, number, boolean, or object).

    {
    "text": "32",
    "visible": true
    }

    Attribute binding

    A reference to a view model attribute, prefixed with $ in the schema JSON.

    {
    "control": "$Account.Name"
    }

    Resource binding

    A reference to a localization string, prefixed with "$Resources.Strings." in the schema JSON.

    {
    "label": "$Resources.Strings.SaveButton_caption"
    }

    Request binding

    A reference to a request handler, stored as an object with request and optional params fields.

    {
    "clicked": {
    "request": "crt.SaveRecordRequest",
    "params": {}
    }
    }

    Use optional chaining and default values when reading property values: "const value = propertyValue?.value ?? defaultValue. This avoids runtime errors when a property has not been set yet.

  5. Implement the method that writes the updated property values back to the schema when the user changes a field. When you call setPropertyValue(), pass one of the following options in the options parameter.

    Example that sets property values of different types
    /* Set a constant value. */
    await this._viewNodeEditor.setPropertyValue('text', { constant: 'Some string' });

    /* Bind to a view model attribute. */
    await this._viewNodeEditor.setPropertyValue('value', { bindToAttribute: 'Account.Name' });

    /* Bind to a localized resource. */
    await this._viewNodeEditor.setPropertyValue('caption', { bindToResource: 'AccountCaption' });

    /* Bind to a request. */
    await this._viewNodeEditor.setPropertyValue('clicked', {
    bindToRequest: {
    request: 'crt.SaveRecordRequest',
    params: { someParam: 'someValue' },
    },
    });

    The bindToRequest option accepts a RequestBindingOptions object with the following properties.

    RequestBindingOptions interface
    interface RequestBindingOptions {
    request: string;
    params?: Record<string, JsonData>;
    }

    setPropertyValue returns a typed object that reflects the value written to the schema. The returned object includes a type field and type-specific fields such as value, attributePath, resourcePath, or requestType, depending on the binding type used.

    To clear a property, set it to a null constant: setPropertyValue(name, { constant: null }).

    To bind a property to a view model attribute, for example, a value that maps to a data source column, use viewModelEditor to manage view model attributes. The table below lists the key methods of viewModelEditor. All methods are asynchronous and return promises. Always use await when calling them.

    Method

    Description

    getAttributeEditor(name)

    Returns an attribute editor, or undefined if the attribute does not exist.

    createAttribute(name, options)

    Creates a new attribute. Throws if the attribute already exists — remove it first with removeAttribute().

    canRemoveAttribute(name)

    Checks whether an attribute can be safely removed.

    removeAttribute(name)

    Deletes an attribute.

    To bind a new attribute to a data source column, use createAttribute with a bindToModel option. Before you bind, verify that the target data source exists using modelEditor.getDataSourceEditor(dataSourceName). The code below shows how to create an attribute with a model binding.

    Example that creates an attribute with a model binding
    const dataSource = await modelEditor.getDataSourceEditor(dataSourceName);
    if (dataSource?.dataSourceType === DataSourceType.EntityDataSource) {
    await viewModelEditor.createAttribute('MyAttribute', {
    bindToModel: { dataSourceName: dataSourceName, dataSourceAttributePath: 'Name' }
    });
    }
  6. Save the file.

  7. Add the markup of the setup area component to the "some-component-setup-area.component.html" file.

  8. Add the styles of the setup area component to the "some-component-setup-area.component.scss" file. Use the "@creatio/interface-designer/styles/properties-panel-styles.scss" file to align the setup area styles with the visual style of the out-of-the-box setup areas.

  9. Link the setup area to the custom Freedom UI component.

    1. Open the "runtime.designer-definitions.ts" file.

    2. Set the propertiesPanel property to the setup area type identifier in the component designer definition. The value must match the type defined in the @CrtViewElement decorator of the setup area component.

    3. Save the file.

      "runtime.designer-definitions.ts" file
      export async function loadRuntimeDesignerDefinitions(_context: RemoteDesignerDefinitionsLoadContext
      ): Promise<RemoteFeatureDesignerDefinitions> {
      return {
      viewElements: [
      {
      type: SOME_COMPONENT_TYPE,
      propertiesPanel: SOME_SETUP_AREA_TYPE_CONSTANT,
      },
      ],
      };
      }

As a result, when a user selects the corresponding Freedom UI component in the Freedom UI Designer, Creatio reads the component designer definition, resolves the propertiesPanel type, and instantiates the registered setup area.

6. Add the setup area component to the Freedom UI Designer​

  1. Register the SomeComponentSetupAreaComponent view element in the design feature so that Freedom UI Designer can display it.

    1. Open the "design-feature.module.ts" file.

    2. Add the SomeComponentSetupAreaComponent to the @CrtModule decorator and Angular module declarations.

      "design-feature.module.ts" file
      @CrtModule({
      viewElements: [SomeComponentSetupAreaComponent],
      })
      @NgModule({
      declarations: [SomeComponentSetupAreaComponent],
      imports: [CommonModule],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      })
      export class DesignFeatureModule {}
    3. Open the "design.feature-activation.ts" file.

    4. Define the SomeComponentSetupAreaComponent as an Angular Element. Learn more: Angular elements overview (official vendor documentation).

      "design.feature-activation.ts" file
      export async function activateDesignFeature(): Promise<void> {
      const moduleRef = await ensureFeatureModuleRef(DesignFeatureModule);

      if (!customElements.get('usr-some-component-setup-area')) {
      const element = createCustomElement(SomeComponentSetupAreaComponent, {
      injector: moduleRef.injector,
      });
      customElements.define('usr-some-component-setup-area', element);
      }

      bootstrapCrtModule('some-package-name', DesignFeatureModule, {
      resolveDependency: (token) => moduleRef.injector.get(token as ProviderToken<unknown>),
      });
      }
    5. Open the "design.feature-definition.ts" file.

    6. Add the public type of the SomeComponentSetupAreaComponent to the discovery.viewElements section.

      "design.feature-definition.ts" file
      export const designFeatureDefinition = {
      id: 'some-package-name-design',
      discovery: {
      viewElements: [{ type: SOME_SETUP_AREA_TYPE_CONSTANT }],
      },
      activate: () =>
      import('./design.feature-activation').then((m) => m.activateDesignFeature()),
      } satisfies RemoteFeatureDefinition;
    7. Save the files.

  2. Run the npm run build command in the Visual Studio Code terminal to build the project. This adds the build to the "dist" directory of the Angular project.

  3. Repeat step 2 of the add the custom Freedom UI component to the Freedom UI page instructions to upload packages to Creatio using the Clio utility.

As a result, the custom setup area will appear in the Freedom UI Designer when a user selects the corresponding Freedom UI component on the canvas. No-code creators will be able to configure the component properties directly in the designer without modifying the Freedom UI page schema manually.


See also​

Custom Freedom UI component implemented using remote module

Implement a Freedom UI Designer setup area for a custom Freedom UI component


Resources​

Remote module template