Denys Vuika cd2b489100
[ADF-5146] Upgrade to Angular 10 (#5834)
* remove useless module

* upgrade to angular 8

* upgrade material to v8

* upgrade adf libs

* migrate demo shell to v8

* upgrade to angular 9

* upgrade material to v9

* remove hammer

* upgrade nx

* upgrade datetime picker

* upgrade flex layout

* update core api

* remove entry components

* code fixes

* upgrade testbed usage

* code fixes

* remove unnecessary core-js from tests

* upgrade CLI

* ts config fixes

* fix builds

* fix testing config

* compile fixes

* fix demo shell dev setup

* fix core tests

* fix card view import

* upgrade nx

* disable smart builds for now

* remove fdescribe

* restore smart builds

* fix issues

* unify tsconfigs and fix newly found issues

* fix configuration and cleanup package scripts

* improved production build from the same config

* use ADF libs directly instead of node_modules

* disable smart build

* single app configuration (angular)

* fix core build

* fix build scripts

* lint fixes

* fix linting setup

* fix linting rules

* various fixes

* disable affected libs for unit tests

* cleanup insights package.json

* simplify smart-build

* fix content tests

* fix tests

* test fixes

* fix tests

* fix test

* fix tests

* disable AppExtensionsModule (monaco example)

* remove monaco extension module

* upgrade bundle check rules

* fix insights tests and karma config

* fix protractor config

* e2e workaround

* upgrade puppeteer and split linting and build

* reusable resources config

* update protractor config

* fix after rebase

* fix protractor config

* fix e2e tsconfig

* update e2e setup

* Save demoshell artifact on S3 and remove travis cache

* Push the libs on S3 and fetch before releasing it

* Add deps

* Add dependencies among libs and run only affected unit test and build

* fix the travis stage name

* fix after renaming dev to demoshell

* force the order of the projects

* remove unused dependencies

* fix content e2e script

* exit codes fix

* add extra exit codes to core e2e

* postinstall hook and package cleanup

* cleanup packages

* remove deprecated code and dependency on router

* improve bundle analyzer script

* minor code fixes

* update spec

* fix code after rebase

* upgrade protractor after rebase

* fix e2e mapping lib

* Update tsconfig.e2e.json

* update e2e tsconfig

* fix angular config

* fix protractor runs

* cache dist folder for libs

* update material selectors for dropdowns

* selector fixes

* remove duplicated e2e that have unit tests already

* fix login selector

* fix e2e

* fix test

* fix import issues

* fix selector

* cleanup old monaco extension files

* cleanup demo shell login

* add protractor max retries

* disable customisations of protractor

* fix login validation

* fix after rebase

* fix after rebase, disable latest versions of libs

* Hide the report tab and rollback the localstorage

* rename protractor config back to js

* restore lint as part of build

* cleanup code

* do not copy anything to node_modules on dist test

* fix unit tests

* config fixes

* fix code

* fix code after rebase

* fix tests

* remove existing words from spellcheck

* remove useless directive decorators

* update package.json after rebase

* add js-api back

* code fixes

* add missing export

* update configs

* fix code

* try fix the sso login test

* fix

* remove puppeteer unit

* fix e2e script

* fix

* make provider easy

* fix routes module before upgrade

* fix unit tests

* upgrade angular cli

* upgrade to angular 10

Co-authored-by: maurizio vitale <maurizio.vitale@alfresco.com>
Co-authored-by: Eugenio Romano <eugenio.romano@alfresco.com>
Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
2020-07-03 13:01:05 +01:00

11 KiB

Title, Added
Title Added
Form extensibility and customization v2.0.0

Form Extensibility and Customization

This page describes how you can customize ADF forms to your own specification.

Note: it is assumed you are familiar with Alfresco Process Services (powered by Activiti) form definition structure.

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

Contents

How components and widgets are rendered 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. This can either be a predefined component type or dynamically evaluated based on the field properties and metadata.

Static component mapping

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);

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

Default component mappings

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

Replacing default form widgets with custom components

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:

default text widget

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 ]
})
export class CustomEditorsModule {}

Every custom widget should be added into the following collections: declarations, exports.

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:

custom text widget

Replacing custom stencils with custom components

This is a short walkthrough on rendering custom APS stencils by means of custom Angular 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 Formruntime template and Formeditor 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 APS web application:

custom stencil task

Creating custom widget

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

adf stencil

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 ]
})
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:

adf stencil runtime

See Also