2016-11-25 19:07:33 +00:00

7.5 KiB

Form Extensibility and Customisation

Note: it is assumed you are familiar with Alfresco Activiti form definition structure.

  • How components and widgets are rendred on a Form
  • Replacing default form widgets with custom components
  • Replacing custom stencils with custom components

How components and widgets are rendred on a 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.

Component type resolvers

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.

Static component mapping

You can (re)map fields like following:

let customResolver: ComponentTypeResolver = () => CustomWidgetComponent;
formRenderingService.setComponentTypeResolver('text', customResolver, true);

or simply:

formRenderingService.setComponentTypeResolver('text', () => CustomWidgetComponent, true);

Dynamic component mapping

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 UnknownWidget;
};
formRenderingService.setComponentTypeResolver('text', customResolver, true);

Default component mappings

Stencil Name Field Type Component Type
Text text TextWidget
Number integer NumberWidget
Multi-line text multi-line-text MultilineTextWidget
Checkbox boolean CheckboxWidget
Dropdown dropdown DropdownWidget
Date date DateWidget
Amount amount AmountWidget
Radio buttons radio-buttons RadioButtonsWidget
Hyperlink hyperlink HyperlinkWidget
Display value readonly DisplayValueWidget
Display text readonly-text DisplayTextWidget
Typeahead typeahead TypeaheadWidget
People people PeopleWidget
Group of people functional-group FunctionalGroupWidget
Dynamic table dynamic-table DynamicTableWidget
N/A container ContainerWidget (layout component)
Header group ContainerWidget
Attach upload AttachWidget or UploadWidget (based on metadata)
N/A N/A UnknownWidget

Replacing default form widgets with custom components

This is a short walkthrough on replacing a standard Text widget with a custom component for all Alfresco Activiti forms rendered within <activiti-form> component.

First let's create a simple Alfresco Activiti form with Text widgets:

default text widget

Every custom widget must inherit WidgetComponent class in order to function properly:

import { Component } from '@angular/core';
import { WidgetComponent } from 'ng2-activiti-form';

@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 adding 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 storing custom widgets in a separate dedicated module (and optionally as separate redistributable library) don't forget importing 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:

custom text widget

Replacing custom stencils with custom components

This is a short walkthrough on rendering custom Alfresco Activiti stencils by means of custom Angular 2 components.

Creating custom stencil

First let's create a basic stencil and call it Custom Stencil 01:

custom stencil

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:

custom stencil form

Once wired with a new task it should look like the following within Alfresco Activiti web application:

custom stencil task

Creating custom widget

If you load previously created task into ADF <actiiti-form> component you will see something like the following:

adf stencil

Let's create an Angular 2 component to render missing content:

import { Component } from '@angular/core';
import { WidgetComponent } from 'ng2-activiti-form';

@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 the runtime you should now see your custom Angular 2 component rendered in place of the stencils:

adf stencil runtime