npm package: @reflaunt/reactive-form
Description: - [Reflaunt Reactive Form](#reflaunt-reactive-form) - [Introduce](#introduce) - [Install](#install) - [Use](#use) - [Selections](#selections)   - [TextBox](#textbox)   - [CheckBox](#checkbox)   - [Radio](#radio)   - [Dropdown](#dropdown)   - [DateTimePick
Latest version: 1.1.51

# Reflaunt Reactive Form

- [Reflaunt Reactive Form](#reflaunt-reactive-form)
- [Introduce](#introduce)
- [Install](#install)
- [Use](#use)
- [Selections](#selections)
  - [TextBox](#textbox)
  - [CheckBox](#checkbox)
  - [Radio](#radio)
  - [Dropdown](#dropdown)
  - [DateTimePicker](#datetimepicker)
  - [Dropzone](#dropzone)
  - [ListCheckBox](#listcheckbox)
  - [PhoneCode](#phonecode)
  - [Select2](#select2)
  - [TexBoxMask](#texboxmask)
  - [TinyMce](#tinymce)
  - [UploadFile](#uploadfile)
  - [TextArea](#textarea)
  - [Province](#province)
  - [SelectionByApi](#selectionbyapi)
  - [Block](#block)
  - [SingleSelect2](#singleselect2)
  - [SwitchInput](#switchinput)
  - [Captcha](#captcha)
  - [TypeAhead](#typeahead)
  - [TypeAheadWithoutApi](#typeaheadwithoutapi)
  - [RangerSlider](#rangerslider)
  - [Update&fill](#updatefill)
- [Validation](#validation)
  - [Default Validation](#default-validation)
  - [Custom Validation](#custom-validation)
- [Theme No.1 (ThemeID: reactive-form-theme-1)](#theme-no1-themeid-reactive-form-theme-1)


<a name="introduce"></a>

# Introduce

- Reactive-Form was built on [Angular Reactive Form](https://angular.io/guide/reactive-forms)
- Reactive-Form helps us to build a form with many types of fields faster

<a name="install"></a>

# Install

- Step 1: Download package form npm

```
yarn add @reflaunt/reactive-form
```

or

```
npm install @reflaunt/reactive-form
```

- Step2: Import module (_app.module.ts_)

```javascript
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AngularReactiveFormModule, UPLOAD_DIRECTIVE_HEADERS, UPLOAD_DIRECTIVE_API_URL, TINYMCE_CONFIG } from '@reflaunt/reactive-form';

@NgModule({
  declarations: [AppComponent],
  imports: [...ReactiveFormsModule, AngularReactiveFormModule],
  providers: [
    {
      provide: UPLOAD_DIRECTIVE_HEADERS,
      useValue: {
        Authorization: `Bearer ${token}`
      }
    },
    {
      provide: UPLOAD_DIRECTIVE_API_URL,
      useValue: 'http://api.reflaunt.local'
    },
    {
      provide: TINYMCE_CONFIG,
      useValue: {
        apiUpload: '/api/v1/upload',
        paramName: 'files',
        resultTransformer: result => result.data[0].full_path
      }
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

- Step 3: Import style & script (_angular.json_)

```javascript
...
 "styles": [
    ...
    "node_modules/primeng/resources/themes/nova-light/theme.css",
    "node_modules/primeng/resources/primeng.min.css",
    "node_modules/primeicons/primeicons.css",
    "node_modules/select2/dist/css/select2.min.css",
    ...
  ],
  "scripts": [
    "node_modules/jquery/dist/jquery.js",
    "node_modules/select2/dist/js/select2.min.js",
    ...
  ],
...
```

<a name="use"></a>

# Use

- Declare an array of selections (_app.component.ts_)

```javascript
import { InputBase, TextBox } from '@reflaunt/reactive-form';
...
public inputs: any;
ngOnInit() {
    let inputs: InputBase<any>[] = [
      new TextBox({
        key: 'textbox',
        label: 'TextBox',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      })
    ];
    this.inputs = inputs;
  }
...
```

- Show [Reactive Form](https://angular.io/guide/reactive-forms) (_app.component.html_)

```html
...
<reactive-form [(inputs)]="inputs" [onSubmit]="onSubmit" [onChangeReactive]="onChange" [keysChange]="keysChange" [scrollOnErrror]="true" submitText="Save">
</reactive-form>
...
```

- Use Reset Form Button (_app.component.html_)

```html
...
<reactive-form ... [isReset]="true">
  ...
</reactive-form>
...
```

- Use CustomSubmit & CustomButton (_app.component.html_)

```html
...
<reactive-form ... [customSubmit]="customSubmit" customeSubmitText="ok" [customButton]="customButton" customButtonText="ok1">
  ...
</reactive-form>
...
```

- Get form value (_app.component.ts_)

```javascript
...
onSubmit(form) {
  if (form.valid) {
    console.log('data', form.value);
  }
}

onChange(form) {
  if (form.valid) {
    console.log('data', form.value);
  }
}

customSubmit = form => {
  this.onSubmit(form);
}

customButton = () => {
}
...
```

<a name="selections"></a>

# Selections

<a name="defaultoptions"></a>

- Each Selection will have its own options, but all of them have default options:

| Option        | Type                                                            | Desciption                                                                                                                            | Defalut Value |
| ------------- | :-------------------------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------- | ------------: |
| label         | string                                                          | label of selector (`label: 'label'`)                                                                                                  | ' '           |
| key           | string                                                          | key of selector (`key: 'key'`)                                                                                                        | ' '           |
| value         | any                                                             | value of selector                                                                                                                     | null          |
| classes       | array                                                           | A array of strings, use to add class for selector (`classes: ['col-6']`)                                                              | [ ]           |
| group         | integer                                                         | Any selectors that have the same group will be in the same group                                                                      | null          |
| group_classes | array                                                           | Any class of group (group)                                                                                                            | null          |
| validators    | array ([ValidatorFn](https://angular.io/api/forms/ValidatorFn)) | A array of object (`validators: [ { label: VALIDATOR_REQUIRED, validator: Validators.required, message: 'This field is required' }]`) | [ ]           |

<a name="textbox"></a>

## TextBox

| Option   | Type    | Desciption                | Defalut Value |
| -------- | :-----: | ------------------------- | ------------: |
| type     | string  | 'password', 'number', ... | none          |
| disabled | boolean |                           | false         |

- Result data Type: String (example: `'This is my textBox'`)

<a name="checkbox"></a>

## CheckBox

- Result data Type: Boolean (example: `true || false`)

<a name="radio"></a>

## Radio

| Option  | Type   | Desciption                                | Defalut Value |
| ------- | :----: | ----------------------------------------- | ------------: |
| options | object | example: `[{ value: 1, label: 'label' }]` | [ ]           |
| content | html   | example: ``                               | null          |


- Result data Type: Value of Object was choosed: `1`

<a name="dropdown"></a>

## Dropdown

| Option   | Type    | Desciption                                | Defalut Value |
| -------- | :-----: | ----------------------------------------- | ------------: |
| options  | object  | example: `[{ value: 1, label: 'label' }]` | [ ]           |
| disabled | boolean |                                           | false         |

- Result data Type: Object (example: `{ label: 'label', value: 1 }`)

<a name="datetimepicker"></a>

## DateTimePicker

| Option         | Type    | Desciption                                                          | Defalut Value |
| -------------- | :-----: | ------------------------------------------------------------------- | ------------: |
| showIcon       | Boolean | `true`: display icon, `false`: undisplay icon                       | `false`       |
| monthNavigator | Boolean | `true`: display month navigator, `false`: undisplay month navigator | `false`       |
| yearNavigator  | Boolean | `true`: display year navigator, `false`: undisplay year navigator   | `false`       |
| yearRange      | String  | example: `'1995:2050'`                                              | '1995:2050'   |
| showTime       | Boolean | `true`: display time, `false`: undisplay time                       | `false`       |
| timeOnly       | Boolean | `true`: display only time, `false`: undisplay time                  | `false`       |
| dateFormat     | String  | example" `'dd/mm/yy'`                                               | `'dd/mm/yy'`  |
| disabled       | boolean |                                                                     | `false`       |
| minDate        | Date    | `new Date('month/date/year') // 11/05/2018`                         | null          |
| maxDate        | Date    | `new Date('month/date/year') // 11/10/2018`                         | null          |

- Result data Type: Date

<a name="dropzone"></a>

## Dropzone

| Option        | Type    | Desciption                                 | Defalut Value |
| ------------- | :-----: | ------------------------------------------ | ------------: |
| message       | String  | Description of selector                    | ' '           |
| upload_path   | String  | Name of folder save your file              | 'folder'      |
| acceptedFiles | String  | type of files accept upload (`'image/*'`)  | \*            |
| maxFilesize   | number  | Maximum size file upload                   | 50            |
| maxFiles      | number  | Maximun number of file upload              | 0             |
| showPreview   | Boolean | Display preview content of upload          | false         |
| url           | String  | Url upload file, example: `/api/v1/upload` | ' '           |

| paramName | String | Name of param upload file | 'file' |
| resultTransformer | Function | Each Api upload will have a different result structure so you must customize it to push on dropzone, (example: `resultTransformer: result => result.data[0].full_path` - data: result of api upload))) |

<a name="listcheckbox"></a>

## ListCheckBox

| Option  | Type    | Desciption                                | Defalut Value |
| ------- | :-----: | ----------------------------------------- | ------------: |
| options | object  | example: `[{ value: 1, label: 'label' }]` | [ ]           |
| disable | boolean |                                           | false         |

- Result data Type: Array value of options (example: `['1', '2']`)

<a name="phonecode"></a>

## PhoneCode

- Result data Type: Object of phone code & phone number
- Example:

```
  {
    code: '+84',
    value: '123456789',
    alpha2Code: 'VN'
  }
```

<a name="select2"></a>

## Select2

| Option        | Type    | Desciption                                | Defalut Value |
| ------------- | :-----: | ----------------------------------------- | ------------: |
| options       | object  | example: `[{ value: 1, label: 'label' }]` | [ ]           |
| tags          | Boolean | Added a value not in options              | false         |
| placeholder   | String  | placeholder of select2                    | ' '           |
| isSelectAll   | Boolean | show select all & clear All ?             | `false`       |
| selectBtnText | string  | label of select all btn                   | 'Select All'  |
| clearBtnText  | Boolean | label of clear all btn                    | 'Clear All'   |

- Result data Type: Array value of options was selected
- Example:

```
  ['1', '2']
```

- Set default value: Each option want set default, need add `selected: true` for this.

```
  {
    value: 1,
    label: 'label'
    selected: true
  }
```

<a name="textboxmask"></a>

## TexBoxMask

| Option             | Type    | Desciption                                                                                        | Defalut Value |
| ------------------ | :-----: | ------------------------------------------------------------------------------------------------- | ------------: |
| mask               | Array   | Array of Regex. (example: Mask Date `[/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/]`) | [ ]           |
| valueWithCharacter | Boolean | Get value with Character or none                                                                  | false         |
| placeholder        | String  | Placeholder                                                                                       | ' '           |
| guide              | Boolean | Show mask when enter Charactor or none                                                            | false         |

- Result data Type: String with mask or none
- Example:

```
  "07/10/1995"
```

```
  "07101995"
```

<a name="tinymce"></a>

## TinyMce

| Option  | Type   | Desciption                                                                  | Defalut Value |
| ------- | :----: | --------------------------------------------------------------------------- | ------------: |
| height  | String | Height of selector                                                          | '300'         |
| plugins | String | Name of plugins you want add to selector. (example: `'print,bold,thin...'`) | ' '           |

<a name="uploadfile"></a>

## UploadFile

| Option            | Type     | Desciption                                                                                                                                                                                             | Defalut Value |
| ----------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------: |
| id                | number   | Id of input control UploadFile                                                                                                                                                                         | 1             |
| uploadPath        | String   | Name of folder save your file                                                                                                                                                                          | ' '           |
| accept            | String   | type of files accept upload (`'png|jpg|jpeg'`)                                                                                                                                                         | \*            |
| allowMaxSize      | number   | Maximum size file upload                                                                                                                                                                               | 2             |
| multiple          | boolean  | `true` or `false`                                                                                                                                                                                      | `false`       |
| apiUpload         | String   | Url upload file, example: `/api/v1/upload`                                                                                                                                                             | ' '           |
| paramName         | String   | Name of param upload file                                                                                                                                                                              | 'files'       |
| resultTransformer | Function | Each Api upload will have a different result structure so you must customize it to push on dropzone, (example: `resultTransformer: result => result.data[0].full_path` - data: result of api upload))) |
| title             | Html     |                                                                                                                                                                                                        | `Upload`      |

<a name="textarea"></a>

## TextArea

| Option | Type   | Desciption               | Defalut Value |
| ------ | :----: | ------------------------ | ------------: |
| rows   | Number | Number lines of textarea | 10            |

<a name="province"></a>

## Province

- 63 provinces & cities of Vietnam
- Result data Type: String (example: `'Thành phố Hà Nội'`)

<a name="selectionbyapi"></a>

## SelectionByApi

| Option            | Type     | Desciption                                                                                                                                                                                      | Defalut Value |
| ----------------- | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------: |
| apiUpload         | String   | Api used to search or filter `'api/v1/admin/users?search='`                                                                                                                                     | null          |
| resultTransformer | Function | Each Api upload will have a different result structure so you must customize it to push on SelectionByApi, (example: `resultTransformer: result => result.data` - data: result of api upload))) |
| fieldName         | Function | return label by data object                                                                                                                                                                     | `""`          |
| multiple          | boolean  | `true or false`                                                                                                                                                                                 | `false`       |
| lengthToSearch    | Number   | lenght of character start to filter                                                                                                                                                             | `3`           |

<a name="block"></a>

## Block

- Print [HTML] in Form

| Option        | Type    | Desciption                                                               | Defalut Value |
| ------------- | :-----: | ------------------------------------------------------------------------ | ------------: |
| classes       | array   | A array of strings, use to add class for selector (`classes: ['col-6']`) | [ ]           |
| group         | integer | Any selectors that have the same group will be in the same group         | null          |
| group_classes | array   | Any class of group (group)                                               |
| content       | Text    | `` `<p><strong>Buon Ngu</strong></p>` ``                                 |

<a name="singleselect2"></a>

## SingleSelect2

- Use like DropDown

<a name="switchinput"></a>

## SwitchInput

- result: `true` or `false`


<a name="captcha"></a>

## Captcha

| Option       | Type   | Desciption                            | Defalut Value       |
| ------------ | :----: | ------------------------------------- | ------------------: |
| numberOfChar | Number | Number character                      | 4                   |
| type         | String | ``` 'number' or 'alphabet' or null``` | null                |
| message      | String | Message of captcha                    | 'Captcha not found' |


- Use

```html
...
<reactive-form [captcha]="'captcha'"></reactive-form>
...
```
('captcha' is key of Captcha input in Form)

<a name="typeahead"></a>

## TypeAhead
- search by api with type ahead ng-boostraps

| Option            | Type     | Desciption                                                                                                                                                                                      | Defalut Value |
| ----------------- | :------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------: |
| apiUpload         | String   | Api used to search or filter `'api/v1/admin/users?search='`                                                                                                                                     | null          |
| resultTransformer | Function | Each Api upload will have a different result structure so you must customize it to push on SelectionByApi, (example: `resultTransformer: result => result.data` - data: result of api upload))) |
| keyName           | array    | array of key display on optional                                                                                                                                                                | `""`          |
| fieldName         | Funtion  | return label in input by key                                                                                                                                                                    | `""`          |
| id                | number   |                                                                                                                                                                                                 | `""`          |
| searchBy          | string   |                                                                                                                                                                                                 | `""`          |


<a name="typeaheadwithoutapi"></a>

## TypeAheadWithoutApi
- use like dropdown & singleselect2


<a name="rangersilder"></a>

## RangerSlider

| Option  | Type   | Desciption | Defalut Value |
| ------- | :----: | ---------- | ------------: |
| min     | number |            |               |
| max     | number |            |
| step    | number |            |               |
| miLabel | string |            |               |
| id      | number |            |               |
| unit    | string |            |               |


<a name="update&fill"></a>

## Update&fill

- use `support.UpdateInputsValue(inputs, optionsData)` to update options for Dropdown, Radio, ListCheckBox, Select2

- use `supportUpdateFormValue(input, valuesData)` to fill data for inputs controll

- Full Example (_app.component.ts_):

```javascript
...
keysChange = ['textbox'];
public optionsData = {
    dropdown: [
      { label: 'Ha Noi', value: 1 },
      { label: 'TP - HCM', value: 2 },
      { label: 'Da Nang', value: 3 }],
    radio: [
      { label: 'Nam', value: 'nam' },
      { label: 'Nu', value: 'nu' }
    ],
    listcheckbox: [
      { label: 'Football', value: 1 },
      { label: 'Volleyball', value: 2 },
      { label: 'Movies', value: 3 },
      { label: 'Camping', value: 4 }
    ],
    select2: [
      { label: 'Football', value: 1 },
      { label: 'Volleyball', value: 2 },
      { label: 'Movies', value: 3 },
      { label: 'Camping', value: 4 }
    ],
    singleselect2: [
      { label: 'Football', value: 1 },
      { label: 'Volleyball', value: 2 },
      { label: 'Movies', value: 3 },
      { label: 'Camping', value: 4 }
    ]
  };

  public valuesData: any = {
    dropdown: _.find(this.data.dropdown, item => item.value === 2),
    radio: 'nu',
    listcheckbox: [2, 3],
    select2: [4, 1],
    checkbox: true,
    datetimepicker: new Date(),
    uploadfile: [
      'http://www.tensorflownews.com/wp-content/uploads/2018/09/1_8GVucX9yhnL21KCtcyFDRQ.png',
      'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTxadr9ykSPoaet-5e7-_YZtueYaRJSvggWtEShh2EJyAjAf5-D'
    ],

    phonecode: {
      code: '84',
      value: '3684523975'
    },
    textarea: 'Content',
    tinymce: '<b>blabla</b>',
    textboxmask: '07101995',
    singleselect2: _.find(this.data.singleselect2, item => item.value === 2),
    selectionbyapi: {
      created_at: '2018-12-18T03:53:00.000Z',
      email: 'admin@reflaunt.com',
      id: 2,
      last_login: '2019-03-28T08:35:08.000Z',
      status: 0,
      updated_at: '2019-03-28T08:35:08.000Z'
    }
  };

 ngOnInit() {
   const inputs: InputBase<any>[] = [
      new TextBox({
        key: 'textbox11111',
        label: 'Textbox',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new TextBox({
        key: 'textbox',
        label: 'Textbox',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new Dropdown({
        key: 'dropdown',
        label: 'Dropdown',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new Radio({
        key: 'radio',
        label: 'Radio',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new ListCheckBox({
        key: 'listcheckbox',
        label: 'ListCheckBox',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new Select2({
        id: 1,
        key: 'select2',
        label: 'Select2',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1,
        isSelectAll: true
      }),
      new CheckBox({
        key: 'checkbox',
        label: 'CheckBox',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new DateTimePicker({
        key: 'datetimepicker',
        label: 'DateTimePicker',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1,
        monthNavigator: true,
        yearNavigator: true,
        yearRange: '1990:2030'
      }),
      new UploadFile({
        key: 'uploadfile',
        label: 'UploadFile',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1,
        accept: 'png|jpg|jpeg',
        allowMaxSize: 2,
        apiUpload: '/api/v1/upload',
        multiple: true,
        resultTransformer: result => result.data[0].full_path,
        paramName: 'files'
      }),
      new PhoneCode({
        key: 'phonecode',
        label: 'PhoneCode',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new TextArea({
        key: 'textarea',
        label: 'TextArea',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1,
        placeholder: 'placeholder of textarea'
      }),
      new TinyMce({
        key: 'tinymce',
        label: 'TinyMce',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new TextBoxMask({
        key: 'textboxmask',
        label: 'TextBoxMask',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1,
        mask: [/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/]
      }),
      new Province({
        key: 'province',
        label: 'Province',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      }),
      new SelectionByApi({
        key: 'selectionbyapi',
        label: 'SelectionByApi',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1,
        apiUpload: '/api/v1/admin/users?search=',
        resultTransformer: result => result.data,
        fieldName: result => result.email,
        multiple: false,
        lengthToSearch: 1
      }),
      new SingleSelect2({
        key: 'singleselect2',
        label: 'Single Select2',
        classes: ['col-12'],
        group_classes: ['col-12'],
        group: 1
      })
    ];
    this.inputs = inputs;
    this.inputs = support.UpdateInputsValue(inputs, this.data);
    this.inputs = support.UpdateFormValue(inputs, this.values);
  }

...
```

<a name="validation"></a>

# Validation

## Default Validation

[https://angular.io/api/forms/Validators](https://angular.io/api/forms/Validators)

- Use & Example:

```javascript
...
...
 new TextBox({
        ...
        validators: [
          {
            validator: Validators.required,
            message: 'This field is required'
          },
          {
            validator: Validators.pattern('[a-zA-Z0-9.-_]{1,}@[a-zA-Z.-]{2,}[.]{1}[a-zA-Z]{2,}'),
            message: 'This field must be a email address'
          },
          {
            validator: Validators.minLength(6),
            message: 'Minimmum alowed charactes are 6'
          },
          {
            validator: Validators.maxLength(20),
            message: 'Maximmum alowed charactes are 20'
          }
        ],
        ...
      }),
...
```

## Custom Validation

[https://angular.io/guide/form-validation#custom-validators](https://angular.io/guide/form-validation#custom-validators)

Reactive-Form provide us some default validator

- isEqualValidator('input-key')
- isDifferentValidator('input-key')
- isExistValidator('apiUrl', 'your-message') - 'apiUrl' must return { exist: true } if item exist

```javascript
...
import { DefaultValidators } from 'angular-reactive-form';
...
 new TextBox({
        ...
        validators: [
          {
            validator: DefaultValidators.isEqualValidator('input-key'),
            message: 'This field not match with input-key '
          },
          {
            validator: DefaultValidators.isDifferentValidator('input-key'),
            message: 'This field must diffrent with input-key'
          }
        ],
        asyncValidators: [DefaultValidators.isExistValidator(apiUrl, yourMessage)]
        ...
      }),
...
```

If you want create a custom validator:

- Step 1: Create new file (_example.validators.ts_)

```javascript
...
  import { AbstractControl, ValidationErrors, AsyncValidatorFn } from '@angular/forms';

  export function isEqualValidator(matchValue: any) {
    return (control: AbstractControl): ValidationErrors | null => {
      const value = control.value;
      if (value) {
        const compareValue = control.root.get(matchValue).value;
        if (value !== compareValue) {
          return { notEqual: { valid: false, value: control.value } };
        }
      }
      return null;
    };
  }

  export function isExistValidator(url, message): AsyncValidatorFn {
    return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
      /* Your code */
      return new Promise(async (resolve, reject) => {
        if (value) {
          /* Your code => result */
          if (result.exist === true) {
            resolve({ isExist: { valid: false, value: control.value, message: message } });
          }
          resolve(null);
        }
        resolve(null);
      });
    };
  }
...
```

- Step 2: Use (_app.component.ts_)

```javascript
...
import { isEqualValidator, isExistValidator } from './validators/example';
...
 new TextBox({
        ...
        validators: [
          {
            validator: isEqualValidator('input-key'),
            message: 'This field not match with input-key '
          }
        ],
        asyncValidators: [isExistValidator('/api/v1/test/check/', 'Email is Exits')]

        ...
      }),
...
```
uu
<a name="theme"></a>

# Theme No.1 (ThemeID: reactive-form-theme-1)

- angular.js: add style.css:
```javascript
...
 "styles": [
              ...
              "node_modules/@reflaunt/reactive-form/style/styles.scss",
            ],
...
```
- With Form want use new style:  <reactive-form
      [themeID]="'reactive-form-theme-1'" ></reactive-form>