npm package: @smartbit4all/form
Description: Stable version: 1.6.8
Latest version: 1.7.15

# Smart Form

Stable version: 1.6.8

## References

These packages must be updated in case of a new version:

- [_@smartbit4all/dialog_](https://www.npmjs.com/package/@smartbit4all/dialog)
- [_@smartbit4all/view-context_](https://www.npmjs.com/package/@smartbit4all/view-context)

---

## How to use

### Installation

Go to your project, open the terminal and use the following command:

    npm i moment

    npm i @angular/material-moment-adapter@13.3.9

    npm i @angular-material-components/datetime-picker@7.0.1

    npm i ngx-mask@13.1.15

    npm i @smartbit4all/form

Then import it in the AppModule:

`app.module.ts:`

    import { SmartformModule } from '@smartbit4all/form';
    ...
    @NgModule({
        declarations: [...]
        imports: [
            ...
            SmartformModule,
        ]
        ...
    })

### Usage

`example.component.html`

    <div *ngIf="form">
        <smartform #smartFilter [smartForm]="form"></smartform>
    </div>

`example.component.ts`

    // smartFilter
    @ViewChild("smartFilter") smartFilterChild!: SmartformComponent;
    form!: SmartForm;

    model?: RegistrationForm;

    myValidator(group: any): ValidatorFn {
        return fieldMatchingValidator(
            group,
            "registrationData.password",
            "registrationData.confirmPassword"
        );
    }

    constructor() {
        this.constructForm();
    }

    async constructForm(): Promise<void> {
        this.model = await this.apiCall();

        let nationalities: any = {
            europe: {
                displayValue: "Europe",
                countries: [
                    {
                        displayValue: "Hungarian",
                        objectUri: "hungarian",
                    },
                    {
                        displayValue: "German",
                        objectUri: "german",
                    },
                ],
            },
            northAmerica: {
                displayValue: "North America",
                countries: [
                    {
                        displayValue: "American",
                        objectUri: "usa",
                    },
                    {
                        displayValue: "Canadian",
                        objectUri: "canada",
                    },
                ],
            },
        };

        this.form = {
            direction: SmartFormWidgetDirection.COL,
            model: this.model,
            useOnValueChangeEvent: true,
            useOnBlurEvent: true,
            validators: [
                {
                    validator: this.myValidator.bind(this),
                    errorMessage: "The passwords are not the same",
                    name: SmartValidatorName.fieldMatchingValidator,
                },
            ],
            widgets: [
                {
                    key: "registrationData.email",
                    label: "Email",
                    type: SmartFormWidgetType.TEXT_FIELD,
                    showLabel: true,
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            isCustom: false,
                            name: "required",
                        },
                        {
                            validator: Validators.email,
                            errorMessage: "This is not a valid email",
                            isCustom: false,
                            name: "email",
                        },
                    ],
                },
                {
                    key: "registrationData.password",
                    label: "Password",
                    type: SmartFormWidgetType.TEXT_FIELD,
                    showLabel: true,
                    isPassword: true,
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            name: SmartValidatorName.required,
                        },
                        {
                            validator: Validators.minLength(8),
                            errorMessage: "The password must be at least 8 characters",
                            name: SmartValidatorName.minLength,
                        },
                    ],
                },
                {
                    key: "registrationData.confirmPassword",
                    label: "Confirm password",
                    type: SmartFormWidgetType.TEXT_FIELD,
                    showLabel: true,
                    isPassword: true,
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            name: SmartValidatorName.required,
                        },
                        {
                            validator: Validators.minLength(8),
                            errorMessage: "The password must be at least 8 characters",
                            name: SmartValidatorName.minLength,
                        },
                    ],
                },
                {
                    key: "registrationData.phone",
                    label: "Phone",
                    type: SmartFormWidgetType.TEXT_FIELD,
                    showLabel: true,
                    mask: "+00-00-000-0000",
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            name: SmartValidatorName.required,
                        },
                        {
                            validator: this.phoneNumberValidator("36"),
                            errorMessage: "This is not a valid phone number",
                            isCustom: true,
                            name: "validPhoneNumber",
                        },
                    ],
                },
                {
                    key: "registrationData.dateOfBirth",
                    label: "Date of birth",
                    type: SmartFormWidgetType.DATE_PICKER,
                    showLabel: true,
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            name: SmartValidatorName.required,
                        },
                    ],
                },
                {
                    key: "registrationData.nationality",
                    label: "Nationality",
                    type: SmartFormWidgetType.SELECT,
                    showLabel: true,
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            name: SmartValidatorName.required,
                        },
                    ],
                    valueList: Object.keys(nationalities).map((key: string) => {
                        return {
                            key: key,
                            label: nationalities[key].displayValue,
                            type: SmartFormWidgetType.ITEM_GROUP,
                            valueList: nationalities[key].countries.map((value: Value) => {
                                return {
                                    key: value.objectUri,
                                    label: value.displayValue,
                                    type: SmartFormWidgetType.ITEM,
                                    value: value.objectUri,
                                };
                            }),
                        };
                    }),
                },
                {
                    key: "registrationData.interests",
                    label: "Interests",
                    type: SmartFormWidgetType.TEXT_FIELD_CHIPS,
                    showLabel: true,
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            name: SmartValidatorName.required,
                        },
                    ],
                },
                {
                    key: "tos",
                    label: "Terms of Services",
                    type: SmartFormWidgetType.CHECK_BOX,
                    showLabel: true,
                    validators: [
                        {
                            validator: Validators.required,
                            errorMessage: "This field is mandatory",
                            name: SmartValidatorName.required,
                        },
                    ],
                    valueList: [
                        {
                            key: "tos",
                            label: "Terms of Services",
                            type: SmartFormWidgetType.ITEM,
                        },
                    ],
                },
                {
                    key: "subscribeToNewsletter",
                    label: "Newsletter",
                    type: SmartFormWidgetType.TOGGLE,

                },
            ],
        };
    }

    submit(): void {
    	this.form = this.smartFilterChild?.submitForm();
    }

### Using multiple languages - i18n

The labels of the form can be translated to any languages that your application supports, all you have to do is providing a service to the form (ideally the TranslateService exported from [@ngx-translate/core](https://www.npmjs.com/package/@ngx-translate/core)).

The SmartForm requires a SmartTranslateService which contains some very basic functions:

    export interface SmartTranslateService {
        get(key: string | Array<string>, interpolateParams?: Object): Observable<string | any>;
        get onLangChange(): EventEmitter<any>;
        get currentLang(): string;
        set currentLang(currentLang: string);
        instant(key: string | Array<string>, interpolateParams?: Object): string | any;
    }

Example:

`en.json:`

    {
        "username": "Username",
        "password": "Password",
        ...
    }

`hu.json:`

    {
        "username": "Felhasználónév",
        "password": "Jelszó",
        ...

}

`any.component.ts:`

    import { TranslateService } from "@ngx-translate/core";

    ...

    export class AnyComponent {
        form: SmartForm;

        constructor(private translate: TranslateService) {
            this.translate.addLangs(["en", "hu"]);
            this.translate.setDefaultLang("hu");

            this.constructForm();
        }

        constructForm(): void {
            let model: any = {
                username: "",
                password: "",
                language: "",
            };

            let possibleValues: any[] = [
                {
                    displayValue: "hungarian",
                    objectUri: "Hungarian",
                },
                {
                    displayValue: "english",
                    objectUri: "English",
                },
            ];

            this.form = {
                direction: SmartFormWidgetDirection.COL,
                model,
                translateService: this.translate, // The TranslateService is provided here
                widgets: [
                    {
                        key: "username",
                        label: "username",
                        placeholder: "username",
                        type: SmartFormWidgetType.TEXT_FIELD,
                    },
                    {
                        key: "password",
                        label: "password",
                        type: SmartFormWidgetType.TEXT_FIELD,
                    },
                    {
                        key: "language",
                        label: "language",
                        type: SmartFormWidgetType.SELECT,
                        valueList: possibleValues.map((value: any) => {
                            return {
                                key: value.objectUri,
                                label: value.displayValue,
                                type: SmartFormWidgetType.ITEM,
                                value: value.objectUri,
                            };
                        }),
                    },
                ],
            };
        }

        toggleLanguage(): void {
            this.translate.use(
                this.translate
                    .getLangs()
                    .filter((lang: string) => lang !== this.translate.currentLang)[0]
            );
        }
    }

---

## Version logs

## @smartbit4all/form v1.6.3

**Type: Bugfix**

A bug has been fixed which caused that the **invalidFields** property did not become `undefined` automatically if the form was valid.

## @smartbit4all/form v1.6.2

**Type: Update**

The **SmartLabel** got a new property which allows to mark that as required.

    export interface SmartLabel {
        ...
        markAsRequired?: boolean;
    }

## @smartbit4all/form v1.6.0

**Type: Update**

This version contains major updates and fixes.

### SmartFormWidget - type specific intellisense and type check

The **SmartFormWidget** became a type declaration:

    export declare type SmartFormWidget<T> =
        | SmartTextField<T>
        | SmartTextFieldNumber<T>
        | SmartTextFieldChips<T>
        | SmartTextBox<T>
        | SmartSelect<T>
        | SmartSelectMultiple<T>
        | SmartCheckBox<T>
        | SmartRadioButton<T>
        | SmartDatePicker<T>
        | SmartDateTimePicker<T>
        | SmartContainer<T>
        | SmartLabel
        | SmartTime<T>
        | SmartToggle<T>
        | SmartIndicatorItem;

All the types above have one property in common which is _type: SmartFormWidgetType_. By declaring the type of the widget the intellisense only shows those properties which are available.

### SmartIndicatorItem

The SmartForm got a new widget that can be used for password strength indicator.

    export interface SmartIndicatorItem {
        type: SmartFormWidgetType.INDICATOR;
        key: string;
        label: string;
        originalLabel?: string;
        showLabel?: boolean;
        indicator: SmartIndicator;
        isVisible?: boolean;
        cssClass?: string;
    }

    export interface SmartIndicator {
        indicateProperty: string;
        indicatorLength: number;
        indicatorStatusLabels: string[];
        originalIndicatorStatusLabels?: string[];
        indicatorValue: number;
        changeIndicatorStatus: (valueToCheck: string) => number;
    }

### SmartValidator - icons

The SmartValidator got two new properties for setting custom icon and it's color to an error.

    export interface SmartValidator {
        ...
        icon?: string;
        iconColor?: string;
    }

## @smartbit4all/form v1.5.0

**Type: Update**

From now on the SmartForm can be used in multi language applications by default.

## @smartbit4all/form v1.4.12

**Type: Update**

The max size of each files can be set for SmartFileUploader.

## @smartbit4all/form v1.4.10

**Type: Update**

In order to attract the user's attention to the invalid fields of the form, the submitForm() function can return a `SmartFormInvalidFields` object, which contains the name and keys of all the invalid fields.

    export interface SmartFormInvalidFields {
        invalidFieldNames: string[];
        invalidFieldKeys: string[];
    }

**Type: Bugfix**

The TEXT_FIELD_CHIPS had a bug validation bug.

## @smartbit4all/form v1.4.9

**Type: Update**

Autofocus is disabled due to an issue.

This is the validator update, in which the validation of the form was rethinked.

The `SmartFormWidget` and the `SmartForm` both got a new property:

    validators?: SmartValidator[];

A **SmartValidator** looks like this:

    export interface SmartValidator {
        validator: ValidatorFn;
        errorMessage?: string;
        name: SmartValidatorName | string;
    }

The _name_ refers to the _property name_ of the object **returned by the validator**. To make this easier, the `SmartValidatorName` enum contains all the validators supported by the `SmartForm` by default.

    export enum SmartValidatorName {
        // Angular validators
        required = "required",
        requiredTrue = "required",
        email = "email",
        max = "max",
        maxLength = "maxlength",
        min = "min",
        minLength = "minlength",
        pattern = "pattern",
        passwordValidator = "passwordValidator",

        // Custom validators
        fieldMatchingValidator = "fieldsMatch",
    }

## @smartbit4all/form v1.4.7

**Type: Update**

Autofocus property added to `SmartFormWidget`.

## @smartbit4all/form v1.4.6

**Type: Update**

onValueChange event added to TEXT_FIELD_NUMBER widget.

## @smartbit4all/form v1.4.5

**Type: Bugfix**

Text field chips bugfix.

## @smartbit4all/form v1.4.3

**Type: Update**

Date time picker widget has been introduced.

## @smartbit4all/form v1.4.2

**Type: Bugfix**

A bug with the Toggle widget has been fixed.

## @smartbit4all/form v1.4.1

**Type: Update**

Version 1.4.0 contains major updates which improves the **usability** of the `SmartForm`.

Updates:

- complex data structure support
- constraints
- text field masking
- label style update

**Complex data structure support:**

From now property path as the key of the widget works with complex models:

    let model = {
        id: "random_id",
        data: {
            username: "user's name",
        }
    }

     let form: SmartForm = {
        direction: SmartFormWidgetDirection.COL,
        model,
        widgets: [
            {
                key: "id",
                label: "Id",
                type: SmartFormWidgetType.TEXT_FIELD,

            },
            {
                key: "data.username",
                label: "Username",
                type: SmartFormWidgetType.TEXT_FIELD,

            },
        ]
    };

**Constraints:**

The `SmartFormComponentConstraint` type has been introduced. Generic settings such as visibility, mandatoriness and enablement can be injected into the SmartForm and applied by key.

    export interface SmartFormComponentConstraint {
        /**
        * The data name to identify the related component or componenets. If it is a path the format is the following - dataSheet.field.
        */
        dataName?: string;
        visible?: boolean;
        mandatory?: boolean;
        enabled?: boolean;
    }

**Text field masking:**

Text fields can be masked with a simple string.

SmartFormWidget for phone numbers (Hungarian):

    {
        key: "phoneNumber",
        label: "Phone number",
        type: SmartFormWidgetType.TEXT_FIELD,

        mask: "+00-00-000-0000",
    },

## @smartbit4all/form v1.3.7

**Type: Features**

This version add onValueChange event to TEXT field SmartformWidget.

## @smartbit4all/form v1.3.5

**Type: Features**

This version introduces the `isVisible?: boolean` property, and also supports to show icons in **Selects**.

## @smartbit4all/form v1.3.2

**Type: Update**

The labels of the widgets were updated. The SmartForm model got a `labelTheme` property which is applied to the labels. The `labelTheme` should be a theme defined by MaterialTheme.

## @smartbit4all/form v1.3.1

**Type: Features**

From now on the `<smartformwidget></smartformwidget>` always has a CSS class which is the key of the widget.

## @smartbit4all/form v1.3.0

**Type: Features**

This version contains two important updates:

- Detecting CapsLock in password fields
- Submitting the form using enter

The changes in the models are the followings:

    export interface SmartForm {
        ...
        submitOnEnter?: () => void;
    }

    export interface SmartFormWidget<T> {
        ...
        capsLockWarning?: string;
        capsLockWarningIcon?: string;
    }

## @smartbit4all/form v1.2.9

**Type: Bugfix**

The min value of `SmartFormWidgetType.TEXT_FIELD_NUMBER` used to be a static 10.

## @smartbit4all/form v1.2.8

**Type: Bugfix**

Previously the `SmartFormWidgetType.SELECT` used to emit onBlur events, however it was not supposed to. From now on it only emits onValueChange events.

## @smartbit4all/form v1.2.7

**Type: Update**

`@smartbit4all/icon` support.

## @smartbit4all/form v1.2.6

**Type: Update**

This update contains date issue fixes for the **DATEPICKER** widget. **The hint** for the datePicker can be injected when creating the widget with the `dateFormatHint` property.

    export interface SmartFormWidget<T> {
        ...
        dateFormatHint?: string;
    }

Using custom locales became more easy, just paste the following line with the correct locale into the **providers** in `app.module.ts`:

`app.module.ts:`

    @NgModule({
        ...
        providers: [
            ...
            { provide: MAT_DATE_LOCALE, useValue: "hu-HU" },
        ]
    }

## @smartbit4all/form v1.2.4

**Type: Update**

The label position of the Toggle button can be set.

    export enum ToggleLabelPosition {
        BEFORE = "before",
        AFTER = "after",
    }

     export interface SmartFormWidget<T> {
        ...
        toggleLabelPosition?: ToggleLabelPosition;
    }

## @smartbit4all/form v1.2.3

**Type: Feature**

The following widget types can be set to readonly:

- TEXT_FIELD
- TEXT_FIELD_NUMBER
- TEXT_FIELD_CHIPS
- TEXT_BOX

The change:

    export interface SmartFormWidget<T> {
        ...
        isReadonly?: boolean;
    }

## @smartbit4all/form v1.2.2

**Type: Bug fix**

Fix row layout inside Container widgets.
From now every row item align to center.

## @smartbit4all/form v1.2.1

**Type: Update**

From now on custom buttons can be added to two types of inputs:

    TEXT_FIELD
    TEXT_FIELD_NUMBER

The properties of the custom button:

    export enum SmartFormTextFieldButtonIconPosition {
        PRE,
        POST,
    }

    export interface SmartFormTextFieldButton {
        icon?: string;
        iconPosition?: SmartFormTextFieldButtonIconPosition;
        label?: string;
        callback: (widget: SmartFormWidget<any>) => any;
        color?: ThemePalette;
    }

    export interface SmartFormWidget<T> {
        ...
        textFieldButton?: SmartFormTextFieldButton;
    }

## @smartbit4all/form v1.2.0

**Type: Update**

Slide Toggle has been added as a new SmarFormWidget type.

Changes:

1.  Toggle SmartFormWidgetType

        export enum SmartFormWidgetType {
            TEXT_FIELD,
            TEXT_FIELD_NUMBER,
            TEXT_FIELD_CHIPS,
            TEXT_BOX,
            SELECT,
            SELECT_MULTIPLE,
            CHECK_BOX,
            CHECK_BOX_TABLE,
            RADIO_BUTTON,
            DATE_PICKER,
            FILE_UPLOAD,
            ITEM,
            CONTAINER,
            LABEL,
            TIME,
        >>> TOGGLE
        }

2.  SmartFormWidgetComponent html

        <div *ngSwitchCase="smartFormWidgetType.TOGGLE">
            <div *ngIf="widgetInstance.showLabel">
                <h4>{{ widgetInstance.label }}</h4>
            </div>
            <mat-slide-toggle
                formControlName="{{ widgetInstance.key }}"
                value="{{ widgetInstance.value }}"
                (change)="onValueChange?.next($event)"
                >{{ widgetInstance.label }}</mat-slide-toggle
            >
        </div>

## @smartbit4all/form v1.1.11

**Type: Update**

The **name** property of the `SmartForm` became **nullable**.

    export interface SmartForm {
        name?: string;
        ...
    }

## @smartbit4all/form v1.1.10

**Type: Bugfix**

Calling submitForm had not validated the form.

## @smartbit4all/form v1.1.7

**Type: Bugfix**

Fix CSS class issue.

## @smartbit4all/form v1.1.5

**Type: Update**

Fix CSS class in label and for layout classes.
Select and Multiple-select widget visible value can be set by SmartFormWidget's label property.

## @smartbit4all/form v1.1.4

**Type: Feature**

Show label can be set globally on form, however it can be overrided on widgets.

## @smartbit4all/form v1.1.1

**Type: Feature**

From now on TextFields can be used as password fields.

## @smartbit4all/form v1.1.0

**Type: Feature**

### Feature: custom CSS class for widgets

The `SmartFormWidget` got a new property (_cssClass?: string;_), which allows to inject any custom CSS class for each widgets.

### Feature: onBlur and onValueChange

From now on `SmartformComponent` has two subscribable Subjects:

- The **onBlur** subject signals when the user leaves the SmartFormWidget's input field.
- The second subject is the **onValueChange** which emits an event when the value of the SmartFormWidget was changed.

**The onBlur event can be used on the following types of widgets:**

- TEXT_FIELD
- TEXT_FIELD_NUMBER
- TEXT_FIELD_CHIPS
- TEXT_BOX
- DATE_PICKER
- SELECT
- SELECT_MULTIPLE
- TIME

**The onValuechange event can be used on the following types of widgets:**

- RADIO_BUTTON
- CHECK_BOX
- SELECT
- SELECT_MULTIPLE

**How to use it:**

Firstly, set the **useOnBlurEvent** and **useOnValueChangeEvent** properties to **true**.

    this.smartForm = {
    	name: '',
    	direction: SmartFormWidgetDirection.COL,
    	widgets: [ ... ],
        useOnBlurEvent: true,
        useOnValueChangeEvent: true
    };

Secondly, subscribe to the events.

    @ViewChild('exampleForm') child?: SmartformComponent;
    smartForm: SmartForm;

    ngAfterViewInit(){
        this.child?.onBlurSubject.subscribe( ... );
        this.child?.onValueChangeSubject.subscribe( ... );
    }

## @smartbit4all/form v1.0.1

**Type: Update**

The package has been published.

## @smartbit4all/form v0.2.0

**Type: Feature**

From now on SmartForm supports object handling by default. This means that an object with default values can be added to the **SmartForm**, and if the keys of the **SmartFormWidget**s are matches with the properties of the given object, the value changes will be handled by the **SmartFormService** automatically.

### Let's see an examplatory scenario:

**You have an object like this:**

    const loginRequestDetails = {
        email: "example@domain.com",
        password: "examplePwd123#"
    }

**You want to use this in your form:**

    this.smartForm = {
    	name: '',
    	direction: SmartFormWidgetDirection.COL,
        model: loginRequestDetails,
    	widgets: [
    		{
    			key: 'email',
    			label: 'Email',
    			type: SmartFormWidgetType.TEXT_FIELD,
    			value: '',
    			validators: [Validators.required, Validators.email],
    			errorMessage: 'It is not a valid email.'
    		},
            {
    			key: 'password',
    			label: 'Password',
    			type: SmartFormWidgetType.TEXT_FIELD,
    			value: '',
    			validators: [Validators.required],
    		}
    	]
    };

**The output will be this:**

<label>Email</label>
<input type="text" value="example@domain.com">

<label>Password</label>
<input type="text" value="examplePwd123#">

**After submit:**

After you have submitted the SmartForm, the new values are available in `this.smartForm.model`.

## @smartbit4all/form v0.1.7

**Type: Bugfix**

A major bug has been fixed which made _getting the value of a container_ impossible.

In the previous version the **SmartFormService** only had the **toSmartForm()** function, which was not able to recreate the smartForm in depth.

**The bugfix:**

    toSmartForm(group: FormGroup, smartForm: SmartForm): SmartForm {
        smartForm = this.toSmartFormDeeply(smartForm.widgets, group, smartForm);

        return smartForm;
    }

    toSmartFormDeeply(
        widgets: SmartFormWidget<any>[],
        group: FormGroup,
        smartForm: SmartForm
    ): SmartForm {
        widgets.forEach((widget) => {
            if (widget.type === SmartFormWidgetType.CONTAINER && widget.valueList?.length) {
                smartForm = this.toSmartFormDeeply(widget.valueList, group, smartForm);
            } else {
                widget.value = group.controls[widget.key].value;
            }
        });
        return smartForm;
    }

## @smartbit4all/form v0.1.6

**Type: Feature**

This version contains one major change: **TIME** picker component have been added.
Small layout change: 1em margin have been added to **CONTAINER** typed widgets.

**Usage have not changed**.

## @smartbit4all/form v0.1.5

**Type: Feature**

This version contains two changes: **LABEL** type to the SmartFormWidgetType enum
and
**widgetDescription** to the SmartFormWidget have been added.

**Changes:**

The LABEL type widget can be used as separated label for the input field.
Description can be added to the label if you'd like to.

## @smartbit4all/form v0.1.4

**Type: Feature**

This version contains two major changes: a **submit function** and **custom validators** have been added to the `SmartForm`.

---

**Changes:**

The **submitForm()** function is implemented in the `SmartformComponent`. The new values are translated back to the given `SmartForm` object if the status of the form is valid. If it is not, the function throws an error with the current state.

    submitForm(): SmartForm {
        if (this.form.status === "VALID") {
            return this.service.toSmartForm(this.form, this.smartForm);
        } else {
            throw new Error(`The form status is ${this.form.status}.`);
        }
    }

In order to use **multiple custom validators**, the `SmartFormWidget` got a validators property. A custom error message can be set with the **errorMessage** property.

    export interface SmartFormWidget<T> {
        ...
        validators?: ValidatorFn[];
        errorMessage?: string;
    }

## @smartbit4all/form v0.1.2

**Type: Feature**

_Prefix_ and _Suffix_ have been added to `SmartFormWidget`. These two can be used with **TextFields**.

**TEXT_FIELD_NUMBER** as a new `SmartFormWidgetType` can be used for numbers.

**Example for prefix, suffix and TEXT_FIELD_NUMBER:**

    {
        key: 'money',
        label: 'Money',
        type: SmartFormWidgetType.TEXT_FIELD_NUMBER,
        value: 10,
        prefix: '€',
        suffix: '.00',
        ...
    }

**Output:** _€ 10.00_

## @smartbit4all/form v0.1.1

**Type: Feature**

`CONTAINER` has been added to `SmartFormWidgetType`.

This **CONTAINER** type can be used for creating inline forms with _n_ widgets in it.

**CONTAINER usage example**

    {
        key: 'container-key',
        label: '',
        value: '',
        type: SmartFormWidgetType.CONTAINER,
        direction: SmartFormWidgetDirection.ROW,
        valueList: [
            {
                key: 'container-key-1',
                label: '1st label',
                type: SmartFormWidgetType.TEXT_FIELD,
                value: this.containerValue,
                ...
            },
            {
                key: 'container-key-2',
                label: '2nd label',
                type: SmartFormWidgetType.TEXT_FIELD,
                value: this.containerValue,
                ...
            },
            ...
        ],
        ...
    }

**Type: Feature**

A new property has been added to `SmartFormWidget` which disables the SmartFormWidget.

**The new property**

    export interface SmartFormWidget<T> {
        ...
        isDisabled?: boolean;
    }

**The change in the SmartFormService**

    createFormControls(widgets: SmartFormWidget<any>[]) {
        ...
        if (widget.isDisabled) {
            formControl.disable();
        }
        ...
    }

**Type: Bugfix**

The **CHECKBOX** widget had a bug, which caused that the label was shown after the options.

**The code snippet which caused the bug**

    .checkbox {
        flex-direction: column-reverse;
    }

**The code snippet which solved the bug**

    .checkbox {
        flex-direction: column;
    }

## @smartbit4all/form v0.0.2

**Type: Feature**

The basic smart form with its features.