Note: it is assumed you are familiar with Alfresco Process Services (powered by Activiti) form definition structure.
Form
All form field editors (aka widgets) on a Form
are rendered by means of FormFieldComponent
that takes an instance of a FormFieldModel
:
<form-field [field]="field"></form-field>
This component depends on FormRenderingService
service to map FormFieldModel
to UI component
based on field type or metadata information.
FormRenderingService
maps field types to corresponding instances exposing ComponentTypeResolver
interface:
export interface ComponentTypeResolver {
(field: FormFieldModel): Type<{}>;
}
Typically a ComponentTypeResolver
is a function that takes FormFieldModel
and returns corresponding component type.
It can be either a predefined component type or a dynamically evaluated based on field properties and metadata.
You can (re)map fields like in the following:
let customResolver: ComponentTypeResolver = () => CustomWidgetComponent;
formRenderingService.setComponentTypeResolver('text', customResolver, true);
or simply:
formRenderingService.setComponentTypeResolver('text', () => CustomWidgetComponent, true);
Alternatively your resolver may return different component types based on FormFieldModel
state and condition:
let customResolver: ComponentTypeResolver = (field: FormFieldModel): Type<{}> => {
if (field) {
let params = field.params;
}
return UnknownWidgetComponent;
};
formRenderingService.setComponentTypeResolver('text', customResolver, true);
Stencil Name | Field Type | Component Type |
---|---|---|
Text | text | TextWidgetComponent |
Number | integer | NumberWidgetComponent |
Multi-line text | multi-line-text | MultilineTextWidgetComponentComponent |
Checkbox | boolean | CheckboxWidgetComponent |
Dropdown | dropdown | DropdownWidgetComponent |
Date | date | DateWidgetComponent |
Amount | amount | AmountWidgetComponent |
Radio buttons | radio-buttons | RadioButtonsWidgetComponent |
Hyperlink | hyperlink | HyperlinkWidgetComponent |
Display value | readonly | DisplayValueWidgetComponent |
Display text | readonly-text | DisplayTextWidgetComponentComponent |
Typeahead | typeahead | TypeaheadWidgetComponent |
People | people | PeopleWidgetComponent |
Group of people | functional-group | FunctionalGroupWidgetComponent |
Dynamic table | dynamic-table | DynamicTableWidgetComponent |
N/A | container | ContainerWidgetComponent (layout component) |
Header | group | ContainerWidgetComponent |
Attach | upload | AttachWidgetComponent or UploadWidgetComponent (based on metadata) |
N/A | N/A | UnknownWidgetComponent |
This is a short walkthrough on replacing a standard Text
widget with a custom component for all APS forms
rendered within <adf-form>
component.
First let's create a simple APS form with Text
widgets:
Every custom widget must inherit WidgetComponent
class in order to function properly:
import { Component } from '@angular/core';
import { WidgetComponent } from '@alfresco/adf-core';
@Component({
selector: 'custom-editor',
template: `
<div style="color: red">Look, I'm a custom editor!</div>
`
})
export class CustomEditorComponent extends WidgetComponent {}
Now you will need to add it to the application module or any custom module that is imported into the application one:
import { NgModule } from '@angular/core';
import { CustomEditorComponent } from './custom-editor.component';
@NgModule({
declarations: [ CustomEditorComponent ],
exports: [ CustomEditorComponent ],
entryComponents: [ CustomEditorComponent ]
})
export class CustomEditorsModule {}
Every custom widget should be added into all three module collections: declarations
, exports
and entryComponents
.
If you decided to store custom widgets in a separate dedicated module (and optionally as separate redistributable library) don't forget to import it into your main application one:
@NgModule({
imports: [
// ...
CustomEditorsModule
// ...
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
Now you can import FormRenderingService
in any of your Views and override default mapping similar to the following:
import { Component } from '@angular/core';
import { CustomEditorComponent } from './custom-editor.component';
@Component({...})
export class MyView {
constructor(formRenderingService: FormRenderingService) {
formRenderingService.setComponentTypeResolver('text', () => CustomEditorComponent, true);
}
}
At runtime it should look similar to the following:
This is a short walkthrough on rendering custom APS stencils by means of custom Angular components.
First let's create a basic stencil and call it Custom Stencil 01
:
Note the internal identifier
value as it will become a field type
value when corresponding form is rendered.
Next put some simple html layout for Form
runtime template
and Form
editor template
fields:
<div style="color: blue">Custom activiti stencil</div>
Now you are ready to design a test form based on your custom stencil:
Once wired with a new task it should look like the following within APS web application:
If you load previously created task into ADF <adf-form>
component you will see something like the following:
Let's create an Angular component to render missing content:
import { Component } from '@angular/core';
import { WidgetComponent } from '@alfresco/adf-core';
@Component({
selector: 'custom-stencil-01',
template: `<div style="color: green">ADF version of custom Activiti stencil</div>`
})
export class CustomStencil01 extends WidgetComponent {}
Put it inside custom module:
import { NgModule } from '@angular/core';
import { CustomStencil01 } from './custom-stencil-01.component';
@NgModule({
declarations: [ CustomStencil01 ],
exports: [ CustomStencil01 ],
entryComponents: [ CustomStencil01 ]
})
export class CustomEditorsModule {}
And import into your Application Module
@NgModule({
imports: [
// ...
CustomEditorsModule
// ...
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
Now you can import FormRenderingService
in any of your Views and provide new mapping:
import { Component } from '@angular/core';
import { CustomStencil01 } from './custom-stencil-01.component';
@Component({...})
export class MyView {
constructor(formRenderingService: FormRenderingService) {
formRenderingService.setComponentTypeResolver('custom_stencil_01', () => CustomStencil01, true);
}
}
At runtime you should now see your custom Angular component rendered in place of the stencils: