Introduction to view components and view actions
Types of view components
There are two types of view components that can be created: standalone, and record editor fields.
A standalone view component can be used by itself, while a record editor field view component is designed to be used within a record editor and is mapped to one of record's fields,
For most view components, there are two Angular components created: a design time component and a runtime component.
The design time component is used by the view designer. This component is displayed on the view designer canvas. It is responsible for collecting, validating, and persisting the view component configuration properties.
The runtime component is used by the application. This is the component with which the end users interact.
Before you begin
Make sure the view components are a part of a coded application, or a library. To learn how to create an application, or a library, please visit this Creating-a-Project-using-Maven-and-the-Archetype.
Creating view components
View components can be created by using Angular schematics provided in the BMC Helix Innovation Studio SDK.
- To create a standalone view component, navigate to the project's webapp directory located at <bundle-name>/bundle/src/main/webapp, and run the following command:
- To create a record editor field view component, navigate to the webapp directory, and run the following command:
Standalone view components
In this section, we will take a closer look at the parts of a standalone view component created by using the following command:
This command will create the files that contain the boilerplate code of a simple standalone view component.
The files are divided into two directories: design, and runtime. There is also a registration module and some common files located in the root directory.
The design directory contains the implementation of the design time view component, while the runtime directory is where you will find the runtime view component implementation.
Below is the list of all files that a standalone view component comprises.
File name | File description |
---|---|
<view-component-name>.types.ts | A TypeScript interface that describes view component's runtime properties. |
<view-component-name>-registration.module.ts | An Angular module required for registering the view component. |
/design | The design time view component implementation |
<view-component-name>-design.component.html | The HTML template of the design time view component. |
<view-component-name>-design.component.scss | CSS styles applied to the design time view component. |
<view-component-name>-design.component.ts | The design time view component. |
<view-component-name>-design.model.ts | An Angular service that defines the design time view component model. |
<view-component-name>-design.types.ts | A TypeScript interface that describes design time view component's properties. |
/runtime | The runtime view component implementation. |
<view-component-name>.component.html | The HTML template of the runtime view component. |
<view-component-name>.component.scss | CSS styles applied to the runtime view component. |
<view-component-name>.component.ts | The runtime view component. |
Code walkthrough
The view components are created in the view-components directory:
CREATE libs/com-example-myapp/src/lib/view-components/foo/foo-registration.module.ts (918 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/foo.types.ts (164 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/design/foo-design.component.html (40 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/design/foo-design.component.scss (97 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/design/foo-design.component.ts (496 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/design/foo-design.model.ts (5687 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/design/foo-design.types.ts (170 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/runtime/foo.component.html (52 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/runtime/foo.component.scss (97 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo/runtime/foo.component.ts (1975 bytes)
UPDATE libs/com-example-myapp/src/lib/com-example-myapp.module.ts (813 bytes)
- foo-registration.module.ts
FooRegistrationModule is defined in this file. In the constructor of this Angular module, the view component gets registered to make it available in the view designer and in the applications.
RxViewComponentRegistryService#register method is passed a view component descriptor object. Some of its most used properties are described below:
Property | Description |
---|---|
type | A string that identifies the view component. To make it unique, the name of the view component is prefixed with the owner bundle ID. This string is used as the name of a DOM element that represents this view component in an application. This string is also saved in the view definition as the identifier of the view component. |
name | A string that is used in the View designer to describe the view component. For example, in the palette, in the breadcrumb, in the expression builder data dictionary, and so on. |
group | A string that identifies the section in the view designer palette where this view component will be placed. If not specified, this property defaults to owner bundle friendly name. |
icon | The name of the icon to display in the View designer palette. |
component | An Angular component that implements the runtime view component. |
designComponent | An Angular component that implements the design time view component. |
designComponentModel | An Angular service responsible for design time behavior of the view component, including initialization, property inspector configuration, property validation, data dictionary construction, and so on. |
properties | An array of component property descriptor objects with the following properties:
|
The standard properties (Hidden, CSS classes, and Available on devices) are added to the list of the view component properties by using RX_STANDARD_PROPS_DESC. These are the properties that are available for most view components.
import { RX_STANDARD_PROPS_DESC, RxViewComponentRegistryService } from '@helix/platform/view/api';
import { FooDesignComponent } from './design/foo-design.component';
import { FooDesignModel } from './design/foo-design.model';
import { FooComponent } from './runtime/foo.component';
@NgModule()
export class FooRegistrationModule {
constructor(rxViewComponentRegistryService: RxViewComponentRegistryService) {
rxViewComponentRegistryService.register({
type: 'com-example-myapp-foo',
name: 'Foo',
group: 'MyApp',
icon: 'wall',
component: FooComponent,
designComponent: FooDesignComponent,
designComponentModel: FooDesignModel,
properties: [
{
name: 'message',
localizable: true,
enableExpressionEvaluation: true
},
...RX_STANDARD_PROPS_DESC
]
});
}
}
- foo.types.ts
IFooProperties interface is defined in this file. It describes the runtime properties of the view component.
This interface extends IRxStandardProps, which describes the standard view component properties (hidden , styles , availableOnDevices).
export interface IFooProperties extends IRxStandardProps {
name: string;
message: string;
}
- foo-design.component.ts
This file contains the Angular component that implements the design time view component.
In this example, it's a simple class with a single model property.
import { FooDesignModel } from './foo-design.model';
@Component({
standalone: true,
selector: 'com-example-myapp-foo-design',
styleUrls: ['./foo-design.component.scss'],
templateUrl: './foo-design.component.html'
})
export class FooDesignComponent {
@Input()
model: FooDesignModel;
}
- foo-design.component.html
This file contains the HTML template for the design time view component. It defines how the view component will be rendered on the view designer canvas.
- foo-design.component.scss
This file contains the CSS rules to apply to the design time view component.
All CSS rules are defined by using SASS language.
In this example, $color-background SASS variable is imported from styles/variables and then used to set the (light gray) background color of the design time view component. By using SASS variables helps view components look more consistent and be theming-ready.
p {
background-color: $color-background;
padding: 10px;
}
- foo-design.model.ts
This file contains the FooDesignModel class that implements the view component design model.
This class has multiple responsibilities. It helps the design time view component integrate into the view designer environment.
Refer to the comments in the code below for more details.
import { Tooltip } from '@helix/platform/shared/api';
import {
ExpressionFormControlComponent,
IExpressionFormControlOptions,
TextFormControlComponent
} from '@helix/platform/shared/components';
import { IViewDesignerComponentModel, RX_STANDARD_PROPS_DEFAULT_VALUES } from '@helix/platform/view/api';
import {
getStandardPropsInspectorConfigs,
IViewComponentDesignCommonDataDictionaryBranch,
IViewComponentDesignSandbox,
IViewComponentDesignValidationIssue,
validateStandardProps,
ViewDesignerComponentModel
} from '@helix/platform/view/designer';
import {
IViewComponentDesignSettablePropertiesDataDictionary
} from '@helix/platform/view/designer/public-interfaces/view-component-design-settable-properties-data-dictionary.interfaces';
import { takeUntil } from 'rxjs/operators';
import { IFooProperties } from '../foo.types';
import { IFooDesignProperties } from './foo-design.types';
const initialComponentProperties: IFooProperties = {
name: '',
message: ''
};
export class FooDesignModel extends ViewDesignerComponentModel<IFooProperties, IFooDesignProperties> implements IViewDesignerComponentModel<IFooProperties, IFooDesignProperties> {
constructor(protected injector: Injector,
protected sandbox: IViewComponentDesignSandbox<IFooDesignProperties>) {
super(injector, sandbox);
// Configure view component property inspector.
sandbox.updateInspectorConfig(this.setInspectorConfig(initialComponentProperties));
// Validate view component properties.
// Note that the standard view component properties should also be validated.
this.sandbox.componentProperties$
.pipe(takeUntil(this.sandbox.destroyed$))
.subscribe((properties: IFooDesignProperties) => {
this.sandbox.setValidationIssues(this.validate(this.sandbox, properties));
});
this.sandbox.getComponentPropertyValue('name').subscribe((name) => {
const componentName = name ? `${this.sandbox.descriptor.name} (${name})` : this.sandbox.descriptor.name;
// Add settable view component properties to the expression builder data dictionary.
// These properties can be set via the Set property view action.
this.sandbox.setSettablePropertiesDataDictionary(componentName, this.getSettablePropertiesDataDictionaryBranch());
// Add view component properties to the expression builder data dictionary.
// The values of these properties can be used when building expressions.
this.sandbox.setCommonDataDictionary(this.prepareDataDictionary(componentName));
});
}
// This method is called when a new, or an existing view component is initialized in the view designer.
// It returns values for all properties of the view component.
static getInitialProperties(currentProperties?: IFooProperties): IFooDesignProperties {
return {
// initial values for custom properties
name: '',
message: '',
// initial values for the standard properties available for all view components
...RX_STANDARD_PROPS_DEFAULT_VALUES,
// property values of an existing view component that are already saved in the view
...currentProperties
};
}
private getSettablePropertiesDataDictionaryBranch(): IViewComponentDesignSettablePropertiesDataDictionary {
return [
{
label: 'Hidden',
expression: this.getExpressionForProperty('hidden')
},
{
label: 'Message',
expression: this.getExpressionForProperty('message')
}
];
}
private prepareDataDictionary(componentName: string): IViewComponentDesignCommonDataDictionaryBranch {
return {
label: componentName,
children: [
{
label: 'Message',
expression: this.getExpressionForProperty('message')
}
]
};
}
private setInspectorConfig(model: IFooProperties) {
return {
inspectorSectionConfigs: [
{
label: 'General',
controls: [
{
name: 'name',
component: TextFormControlComponent,
options: {
label: 'Name',
tooltip: new Tooltip('Enter a name to uniquely identify this view component.')
}
},
{
name: 'message',
component: ExpressionFormControlComponent,
options: {
label: 'Message',
tooltip: new Tooltip('The Message is a required property whose value will be displayed at runtime.'),
dataDictionary$: this.expressionConfigurator.getDataDictionary(),
operators: this.expressionConfigurator.getOperators(),
isRequired: true
} as IExpressionFormControlOptions
},
// Add standard properties available for most view components, such as
// Hidden, Available on devices, CSS classes.
...getStandardPropsInspectorConfigs()
]
}
]
};
}
private validate(
sandbox: IViewComponentDesignSandbox<IFooDesignProperties>,
model: IFooDesignProperties
): IViewComponentDesignValidationIssue[] {
const validationIssues: IViewComponentDesignValidationIssue[] = [];
if (!model.message) {
validationIssues.push(sandbox.createError('Message cannot be blank.', 'message'));
}
// Validate standard properties.
validationIssues.push(...validateStandardProps(model));
return validationIssues;
}
}
- foo-design.types.ts
IFooDesignProperties interface is defined in this file. It describes the design time properties of the view component.
This interface extends IRxStandardProps, which describes the standard view component properties (hidden, styles , availableOnDevices).
export interface IFooDesignProperties extends IRxStandardProps {
name: string;
message: string;
}
- foo.component.ts
This file contains the Angular component that implements the runtime view component.
It inherits the base view component functionality from BaseViewComponent.
@RxViewComponent decorator must be applied to the runtime view component class. It allows the applications to determine the owner bundle of the view component and load that bundle when needed.
For more information, see the following code:
import { RxViewComponent } from '@helix/platform/view/api';
import { BaseViewComponent, IViewComponent } from '@helix/platform/view/runtime';
import { Observable, throwError } from 'rxjs';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { IFooProperties } from '../foo.types';
@Component({
standalone: true,
selector: 'com-example-myapp-foo',
styleUrls: ['./foo.component.scss'],
templateUrl: './foo.component.html'
})
@RxViewComponent({
name: 'com-example-myapp-foo'
})
export class FooComponent extends BaseViewComponent implements OnInit, IViewComponent {
@Input()
config: Observable<IFooProperties>;
api = {
// This method will be called when a component property is set via the Set property view action.
setProperty: this.setProperty.bind(this)
};
protected state: IFooProperties;
ngOnInit() {
super.ngOnInit();
// Make component API available to runtime view.
this.notifyPropertyChanged('api', this.api);
// Subscribe to configuration property changes.
this.config.pipe(distinctUntilChanged(), takeUntil(this.destroyed$)).subscribe((config: IFooProperties) => {
// Setting isHidden property to true will remove the component from the DOM.
this.isHidden = Boolean(config.hidden);
this.state = { ...config };
});
}
private setProperty(propertyPath: string, propertyValue: any): void | Observable<never> {
switch (propertyPath) {
case 'hidden': {
this.state.hidden = propertyValue;
this.notifyPropertyChanged(propertyPath, propertyValue);
break;
}
case 'message': {
this.state.message = propertyValue;
this.notifyPropertyChanged(propertyPath, propertyValue);
break;
}
default: {
return throwError(`Foo : property ${propertyPath} is not settable.`);
}
}
}
}
- foo.component.html
This file contains the HTML template for the runtime view component. It defines how the view component will be rendered in the application.
{{state.message ?? '[No message]'}}
</p>
- foo.component.scss
This file contains the CSS rules to apply to the runtime view component.
All CSS rules are defined using SASS language.
In this example, a $color-background SASS variable is imported from styles/variables and then used to set the (light gray) background color of the view component. By using SASS variables helps view components look more consistent and be theming-ready.
p {
background-color: $color-background;
padding: 10px;
}
Record editor field view components
In this section, we will take a closer look at the parts of a record editor field view component created by using the following command:
This command will create the files that contain the boilerplate code of a simple record editor field view component.
The files are divided into two directories: design, and runtime. The registration module is located in the root directory.
The design directory contains the implementation of the design time view component, while the runtime directory is where you will find the runtime view component implementation.
Below is the list of all files that a record editor field view component comprises.
File name | File description |
---|---|
<view-component-name>-registration.module.ts | An Angular module required for registering the view component. |
/design | The design time view component implementation. |
<view-component-name>-design.component.html | The HTML template of the design time view component. |
<view-component-name>-design.component.scss | CSS styles applied to the design time view component. |
<view-component-name>-design.component.ts | The design time view component. |
<view-component-name>-design.model.ts | An Angular service that defines the design time view component model. |
<view-component-name>-design.types.ts | A TypeScript interface that describes design time view component's properties. |
/runtime | The runtime view component implementation. |
<view-component-name>.component.html | The HTML template of the runtime view component. |
<view-component-name>.component.scss | CSS styles applied to the runtime view component. |
<view-component-name>.component.ts | The runtime view component. |
<view-component-name>.types.ts | A TypeScript interface that describes runtime view component's properties. |
Code walkthrough
- The view components are created in the view-components directory:
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/foo-field-registration.module.ts (1674 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/design/foo-field-design.component.html (261 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/design/foo-field-design.component.scss (97 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/design/foo-field-design.component.ts (625 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/design/foo-field-design.model.ts (4054 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/design/foo-field-design.types.ts (198 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/runtime/foo-field.component.html (749 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/runtime/foo-field.component.scss (97 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/runtime/foo-field.component.ts (1591 bytes)
CREATE libs/com-example-myapp/src/lib/view-components/foo-field/runtime/foo-field.types.ts (209 bytes)
UPDATE libs/com-example-myapp/src/lib/com-example-myapp.module.ts (620 bytes)
- foo-field-registration.module.ts
FooFieldRegistrationModule is defined in this file. In the constructor of this Angular module, the view component gets registered to make it available in the view designer and in the applications.
RxViewComponentRegistryService#register method is passed a view component descriptor object. Some of its most used properties are described below:
Property | Description |
---|---|
type | A string that identifies the view component. To make it unique, the name of the view component is prefixed with the owner bundle ID. This string is used as the name of a DOM element that represents this view component in an application. This string is also saved in the view definition as the identifier of the view component. |
name | A string used in the View designer to describe the view component, such as in the palette, the breadcrumb, the expression builder data dictionary, and so on. |
icon | The name of the icon to display in the View designer palette. |
group | A string that identifies the section in the view designer palette where this view component will be placed. If not specified, this property defaults to owner bundle friendly name. |
options | An object with additional configuration options such as canBeEmbeddedInRecordEditor: a boolean that indicates whether the view component can be shown inside a record editor. |
canBeInsertedInto | A method that determines whether this view component can be dropped inside another view component on the view designer canvas. This method receives an array of component types that includes the target view component and all of its parents. |
component | An Angular component that implements the runtime view component. |
designComponent | An Angular component that implements the design time view component. |
designComponentModel | An Angular service responsible for design time behavior of the view component, including initialization, property inspector configuration, property validation, data dictionary construction. |
properties | An array of component property descriptor objects with the following properties:
|
The standard record editor field properties are added to the list of the view component properties using RX_BASE_FIELD_PROPERTIES. These are the properties that are available for all record editor field components:
- api: the API of the view component
- disabled: a boolean that Indicates if the view component should be disabled.
- recordDefinition: the record definition to which the parent record editor is bound.
- recordInstance: the record instance displayed in the parent record editor.
- hidden: a boolean that Indicates if the view component should be hidden.
- inReadState: a boolean that indicates if the parent record editor is in read state
- value: an expression for the value of the field. It can be used for calculated properties.
import { RxViewComponentRegistryService, RxViewComponentType } from '@helix/platform/view/api';
import { RX_BASE_FIELD_PROPERTIES } from '@helix/platform/view/components';
import { FooFieldDesignComponent } from './design/foo-field-design.component';
import { FooFieldDesignModel } from './design/foo-field-design.model';
import { FooFieldComponent } from './runtime/foo-field.component';
@NgModule()
export class FooFieldRegistrationModule {
constructor(rxViewComponentRegistryService: RxViewComponentRegistryService) {
rxViewComponentRegistryService.register({
type: 'com-example-myapp-foo-field',
name: 'Foo Field',
icon: 'wall',
group: 'MyApp',
options: {
// Specify that the view component can be placed inside a record editor.
// If this property is not set, the Record editor will not accept the view component.
canBeEmbeddedInRecordEditor: true
},
// Do not allow the view component to be placed inside a view component
// other than the Record editor.
canBeInsertedInto(componentTypes: string[]): boolean {
return componentTypes.includes(RxViewComponentType.RecordEditor);
},
component: FooFieldComponent,
designComponent: FooFieldDesignComponent,
designComponentModel: FooFieldDesignModel,
// An array of view component properties.
// Note, that the custom properties must be combined with the base field properties.
properties: [...RX_BASE_FIELD_PROPERTIES, {
name: 'message',
enableExpressionEvaluation: true
}]
});
}
}
- foo-field-design.component.html
This file contains the HTML template for the design time view component. It defines how the view component will be rendered on the view designer canvas.
<adapt-rx-textfield [required]="model.isRequired$ | async"
[label]="model.label$ | async"
[disabled]="true"
ngModel
></adapt-rx-textfield>
- foo-field-design.component.scss
This file contains the CSS rules to apply to the design time view component.
All CSS rules are defined using SASS language.
In this example, $color-background SASS variable is imported from styles/variables and then used to set the (light gray) background color of the design time view component. By using SASS variables helps view components look more consistent and be theming-ready.
p {
background-color: $color-background;
padding: 10px;
}
- foo-field-design.component.ts
This file contains the Angular component that implements the design time view component.
In this example, it's a simple class with a single model property.
import { Component, Input } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AdaptRxTextfieldModule } from '@bmc-ux/adapt-angular';
import { FooFieldDesignModel } from './foo-field-design.model';
@Component({
standalone: true,
selector: 'com-example-myapp-foo-field-design',
styleUrls: ['./foo-field-design.component.scss'],
templateUrl: './foo-field-design.component.html',
imports: [CommonModule, FormsModule, AdaptRxTextfieldModule]
})
export class FooFieldDesignComponent {
@Input()
model: FooFieldDesignModel;
}
- foo-field-design.model.ts
This file contains the FooFieldDesignModel class that implements the view component design model. This class has multiple responsibilities. It helps the design time view component integrate into the view designer environment and into the record editor design view component.
Refer to the comments in the code below for more details.
import { RX_RECORD_DEFINITION } from '@helix/platform/record/api';
import { Tooltip } from '@helix/platform/shared/api';
import { ExpressionFormControlComponent, IExpressionFormControlOptions } from '@helix/platform/shared/components';
import { BaseRecordEditorFieldDesign } from '@helix/platform/view/components';
import {
IViewComponentDesignSandbox,
IViewComponentDesignValidationIssue,
IViewDesignerInspectorConfig
} from '@helix/platform/view/designer';
import {
IViewDesignerInspectorSectionConfig
} from '@helix/platform/view/designer/public-interfaces/view-designer-inspector.types';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { IFooFieldDesignProperties } from './foo-field-design.types';
export class FooFieldDesignModel extends BaseRecordEditorFieldDesign {
// An array of record field types to which this view component can be bound.
fieldResourceTypes = [RX_RECORD_DEFINITION.resourceTypes.character];
componentProperties$: Observable<IFooFieldDesignProperties> = this.sandbox.componentProperties$;
constructor(public injector: Injector, public sandbox: IViewComponentDesignSandbox<IFooFieldDesignProperties>) {
super(injector, sandbox);
// Validate custom view component properties.
// Note that the standard properties are validated in the base class.
combineLatest([this.sandbox.componentProperties$])
.pipe(
map(([componentProperties]: [IFooFieldDesignProperties]) =>
this.validateCustomProperties(this.sandbox, componentProperties)
)
)
.subscribe((validationIssues) => {
this.sandbox.setValidationIssues(validationIssues);
});
}
// This method is called when a new, or an existing view component is initialized in the view designer.
// It returns values for all properties of the view component.
static getInitialProperties(currentProperties?: IFooFieldDesignProperties): IFooFieldDesignProperties {
return {
// initial values for custom properties
message: '',
// initial values for the standard properties available for all view components
...BaseRecordEditorFieldDesign.getInitialProperties(),
// property values of an existing view component that are already saved in the view
...currentProperties
};
}
// Override to customize the configuration of the view component property inspector.
getInspectorConfig(): Observable<IViewDesignerInspectorConfig> {
return super.getInspectorConfig()
.pipe(
map(
(inspectorConfig: IViewDesignerInspectorConfig) => this.buildInspectorConfig(inspectorConfig)
)
);
};
private buildInspectorConfig(defaultInspectorConfig: IViewDesignerInspectorConfig): IViewDesignerInspectorConfig {
// Create a new section in the view component property inspector.
const inspectorSectionConfig: IViewDesignerInspectorSectionConfig = {
label: 'Custom',
controls: [
{
name: 'message',
component: ExpressionFormControlComponent,
options: {
label: 'Message',
tooltip: new Tooltip('This message will be displayed above the label.'),
dataDictionary$: this.expressionConfigurator.getDataDictionary(),
operators: this.expressionConfigurator.getOperators(),
isRequired: true
} as IExpressionFormControlOptions
}
]
};
defaultInspectorConfig.inspectorSectionConfigs.push(inspectorSectionConfig);
return defaultInspectorConfig;
}
private validateCustomProperties(
sandbox: IViewComponentDesignSandbox,
model: IFooFieldDesignProperties
): IViewComponentDesignValidationIssue[] {
const validationIssues = [];
if (!model.message) {
validationIssues.push(sandbox.createError('Message cannot be blank.', 'message'));
}
return validationIssues;
}
}
- foo-field-design.types.ts
IFooFieldDesignProperties interface is defined in this file. It describes the design-time properties of the view component.
This interface extends IBaseRecordEditorFieldProperties, which describes the standard record editor field view component properties:
- fieldId
- label
- value
- hidden
- styles
- availableOnDevices
- disabled
export interface IFooFieldDesignProperties extends IBaseRecordEditorFieldProperties {
message?: string;
}
- foo-field.component.html
This file contains the HTML template for the runtime view component. It defines how the view component will be rendered in the application.
<ng-container>
<p>{{ (config | async).message }}</p>
<rx-read-only-field
*ngIf="inReadState; else editStateElementRef"
[label]="label"
[value]="getDisplayValue()"
></rx-read-only-field>
</ng-container>
<!-- This HTML will be used when the record editor is in edit state. -->
<ng-template #editStateElementRef>
<adapt-rx-textfield [label]="label"
[formControl]="formControl"
[required]="isRequired"
[readonly]="isDisabled"
[disabledStyleForReadonlyState]="true"
[maxlength]="maxLength"
></adapt-rx-textfield>
</ng-template>
- foo-field.component.scss
This file contains the CSS rules to apply to the runtime view component.
All CSS rules are defined by using SASS language.
In this example, a $color-background SASS variable is imported from styles/variables and then used to set the (light gray) background color of the view component. By using SASS variables helps view components look more consistent and be theming-ready.
p {
background-color: $color-background;
padding: 10px;
}
- foo-field.component.ts
This file contains the Angular component that implements the runtime view component.
It inherits the base record editor field view component functionality from BaseRecordEditorFieldComponent.
@RxViewComponent decorator must be applied to the runtime view component class. It allows the applications to determine the owner bundle of the view component and load that bundle when needed.
For more information, see the following code:
import { Component, Injector } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AdaptRxTextfieldModule } from '@bmc-ux/adapt-angular';
import { RxFieldDefinitionService } from '@helix/platform/record/api';
import { ReadOnlyFieldModule } from '@helix/platform/ui-kit';
import { RxViewComponent } from '@helix/platform/view/api';
import { BaseRecordEditorFieldComponent } from '@helix/platform/view/components';
import { IViewComponent } from '@helix/platform/view/runtime';
import { IFooFieldRuntimeProperties } from './foo-field.types';
@Component({
standalone: true,
selector: 'com-example-myapp-foo-field',
styleUrls: ['./foo-field.component.scss'],
templateUrl: './foo-field.component.html',
imports: [CommonModule, ReadOnlyFieldModule, AdaptRxTextfieldModule, ReactiveFormsModule]
})
@RxViewComponent({
name: 'com-example-myapp-foo-field'
})
export class FooFieldComponent extends BaseRecordEditorFieldComponent implements IViewComponent {
protected maxLength: number;
constructor(injector: Injector,
private rxFieldDefinitionService: RxFieldDefinitionService) {
super(injector);
}
// Override to initialize internal component properties.
onConfigInitialized(config: IFooFieldRuntimeProperties): void {
super.onConfigInitialized(config);
if (this.fieldDefinition.maxLength && !this.rxFieldDefinitionService.isSystemField(this.fieldDefinition)) {
this.maxLength = this.fieldDefinition.maxLength;
}
}
}
- foo-field.type.ts
IFooFieldRuntimeProperties interface is defined in this file. It describes the runtime properties of the view component.
This interface extends IBaseRecordEditorFieldComponentConfig, which describes the standard record editor field view component properties (hidden, styles, availableOnDevices).
export interface IFooFieldRuntimeProperties extends IBaseRecordEditorFieldComponentConfig {
message?: string;
}
View actions
As with the view components, view actions have two sides of the implementation: runtime and design time.
At runtime, a view action is a service that implements the execute method. The method receives the view action parameters and returns an Observable that should be completed when the view action is completed. It can also throw an error if something goes wrong. When a view action throws an error, the following view actions will not be executed.
At design-time, a view action must provide the design manager and the design model services. These services will be responsible for configuring the view action in the view designer.
Creating view actions
View actions can be created with an Angular schematic provided in the Helix Platform SDK.
To create a view action, navigate to project's webapp directory (<bundle-name>/bundle/src/main/webapp) and run the following command:
Exploring a view action
In this section, we will take a closer look at the parts of a view action created using the following command:
This command will create the files that contain the boilerplate code of a simple view action.
Below is the list of all files that a view action comprises.
File name | File description |
---|---|
<view-action-name>-action.module.ts | An Angular module required for registering the view action. |
<view-action-name>-action.service.ts | The runtime view action service. |
<view-action-name>-action.types.ts | A TypeScript interface that describes the runtime view action parameters. |
<view-action-name>-action-design.types.ts | A TypeScript interface that describes the design time view action parameters. |
<view-action-name>-action-design-manager.service.ts | The design time view action manager service responsible for view action parameter validation. |
<view-action-name>-action-design-model.class.ts | The design time view action model class responsible for view action initialization and configuration. |
Code walkthrough
- The view actions are created in the actions directory:
CREATE libs/com-example-myapp/src/lib/actions/foo/foo-action-design-manager.service.ts (727 bytes)
CREATE libs/com-example-myapp/src/lib/actions/foo/foo-action-design-model.class.ts (2184 bytes)
CREATE libs/com-example-myapp/src/lib/actions/foo/foo-action-design.types.ts (181 bytes)
CREATE libs/com-example-myapp/src/lib/actions/foo/foo-action.module.ts (1392 bytes)
CREATE libs/com-example-myapp/src/lib/actions/foo/foo-action.service.ts (1210 bytes)
CREATE libs/com-example-myapp/src/lib/actions/foo/foo-action.types.ts (64 bytes)
UPDATE libs/com-example-myapp/src/lib/com-example-myapp.module.ts (572 bytes)
- foo-action.module.ts
FooActionModule is defined in this file. In the constructor of this Angular module, the view action gets registered to make it available in the view designer and in the applications.
RxViewActionRegistryService#register method is passed a view action descriptor object. Some of its most used properties are described below:
Property | Description |
---|---|
name | The name of the view action as saved in the view definition. To make it unique, always prefix it with the bundle name, similar to how it's done by the Angular schematic. |
label | The name of the action that will be displayed in the view designer. |
service | A service that implements the business logic of the view action. |
designManager | The design manager service responsible for the view action parameter validation. |
designModel | The class responsible for the view action design time behavior. |
parameters | An array of view action parameter descriptor objects with the following properties:
|
import { RxViewActionRegistryService } from '@helix/platform/view/api';
import { FooActionDesignManagerService } from './foo-action-design-manager.service';
import { FooActionDesignModel } from './foo-action-design-model.class';
import { FooActionService } from './foo-action.service';
@NgModule({
providers: [FooActionService, FooActionDesignManagerService]
})
export class FooActionModule {
constructor(
rxViewActionRegistryService: RxViewActionRegistryService,
fooActionService: FooActionService,
fooActionDesignManagerService: FooActionDesignManagerService
) {
rxViewActionRegistryService.register({
name: 'comExampleMyappFoo',
label: 'Foo',
// a service that will execute the view action at runtime
service: fooActionService,
// the design manager service responsible for view action parameter validation at design time
designManager: fooActionDesignManagerService,
// the design model class responsible for the design time behavior of the view action
designModel: FooActionDesignModel,
// the list of view action input parameters
parameters: [
{
name: 'message',
label: 'Message',
isRequired: true,
enableExpressionEvaluation: true
}
]
});
}
}
- foo-action.service.ts
This service implements view action's business logic that will be executed at runtime.
@RxViewAction decorator must be applied to the view action service class. It will allow the view designer and the applications to determine the owner bundle of the view action and load it when needed.
import { RX_MODAL, RxModalService } from '@helix/platform/ui-kit';
import { IViewActionService, RxViewAction } from '@helix/platform/view/api';
import { EMPTY, from, Observable, throwError } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { IFooActionProperties } from './foo-action.types';
@Injectable()
@RxViewAction({
name: 'comExampleMyappFoo'
})
export class FooActionService implements IViewActionService<IFooActionProperties, never> {
constructor(private rxModalService: RxModalService) {
}
// Implements the runtime behavior of the view action.
execute(inputParameters: IFooActionProperties): Observable<never> {
return from(this.rxModalService.confirm({
title: 'default',
modalStyle: RX_MODAL.modalStyles.warning,
message: inputParameters.message
})).pipe(
// Return a completed observable if confirmed, or throw error if rejected.
// Throwing an error will cancel the following actions.
switchMap((result: boolean) => {
if (result) {
return EMPTY;
} else {
return throwError(null);
}
})
);
}
}
- foo-action.types.ts
This file contains an interface that describes view action parameters.
message: string;
}
- foo-action-design.types.ts
This file contains an interface that describes view action's design time parameters.
This interface must extend IViewActionDesignProperties that describes the common view action parameters.
export interface IFooActionDesignProperties extends IViewActionDesignProperties {
message: string;
}
- foo-action-design-manager.service.ts
FooActionDesignManagerService is the service that is responsible for view action parameter validation at design time.
import { IViewActionDesignManager } from '@helix/platform/view/api';
import { IViewComponentDesignValidationIssue } from '@helix/platform/view/designer';
import { Observable, of } from 'rxjs';
import { IFooActionDesignProperties } from './foo-action-design.types';
@Injectable()
export class FooActionDesignManagerService implements IViewActionDesignManager<IFooActionDesignProperties> {
// This method will be called automatically to validate view action input parameters.
validate(actionProperties: IFooActionDesignProperties, propertyName: string): Observable<IViewComponentDesignValidationIssue[]> {
// Add custom validation here.
return of([]);
}
}
- foo-action-design-model.class.ts
FooActionDesignModel class is responsible for design time behavior of the view action, including initialization, parameter configuration, and expression builder data dictionary updates.
Refer to the comments in the code below for more details.
import { ExpressionFormControlComponent, IExpressionFormControlOptions } from '@helix/platform/shared/components';
import {
IViewActionDesignPropertyEditorConfig,
IViewActionDesignSandbox,
IViewActionOutputDataDictionary,
ViewActionDesignEditableProperties
} from '@helix/platform/view/api';
import { RxViewDesignerActionModel } from '@helix/platform/view/designer';
import { IFooActionDesignProperties } from './foo-action-design.types';
export class FooActionDesignModel extends RxViewDesignerActionModel {
// This method is called when a new, or an existing view action is initialized in the view designer.
// It returns values for all input parameters of the view action.
static getInitialProperties(currentInputParams: ViewActionDesignEditableProperties<IFooActionDesignProperties>) {
return {
// initial values for custom input parameters
message: '',
// input parameter values of an existing view action that are already saved in the view
...currentInputParams
};
}
constructor(protected injector: Injector,
readonly sandbox: IViewActionDesignSandbox<IFooActionDesignProperties>) {
super(injector, sandbox);
// Configure view action input parameter editor.
this.sandbox.setActionPropertyEditorConfig(this.getActionEditorConfig());
// Add view action output parameters to the expression builder data dictionary.
this.sandbox.setActionOutputDataDictionary(this.getActionOutputDataDictionary());
}
private getActionEditorConfig(): IViewActionDesignPropertyEditorConfig {
return [
{
name: 'message',
component: ExpressionFormControlComponent,
options: {
label: 'Message',
isRequired: true,
dataDictionary$: this.expressionConfigurator.getDataDictionary(),
operators: this.expressionConfigurator.getOperators()
} as IExpressionFormControlOptions
}
];
}
private getActionOutputDataDictionary(): IViewActionOutputDataDictionary {
// Add view action output parameters here.
return [];
}
}