@reflaunt/reactive-form
v1.1.51
Published
- [Reflaunt Reactive Form](#reflaunt-reactive-form) - [Introduce](#introduce) - [Install](#install) - [Use](#use) - [Selections](#selections) - [TextBox](#textbox) - [CheckBox](#checkbox) - [Radio](#radio) - [Dropdown](#dropdown) - [DateTimePick
Downloads
74
Readme
Reflaunt Reactive Form
- Reflaunt Reactive Form
- Introduce
- Install
- Use
- Selections
- Validation
- Theme No.1 (ThemeID: reactive-form-theme-1)
Introduce
- Reactive-Form was built on Angular Reactive Form
- Reactive-Form helps us to build a form with many types of fields faster
Install
- Step 1: Download package form npm
yarn add @reflaunt/reactive-form
or
npm install @reflaunt/reactive-form
- Step2: Import module (app.module.ts)
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)
...
"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",
...
],
...
Use
- Declare an array of selections (app.component.ts)
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 (app.component.html)
...
<reactive-form [(inputs)]="inputs" [onSubmit]="onSubmit" [onChangeReactive]="onChange" [keysChange]="keysChange" [scrollOnErrror]="true" submitText="Save">
</reactive-form>
...
- Use Reset Form Button (app.component.html)
...
<reactive-form ... [isReset]="true">
...
</reactive-form>
...
- Use CustomSubmit & CustomButton (app.component.html)
...
<reactive-form ... [customSubmit]="customSubmit" customeSubmitText="ok" [customButton]="customButton" customButtonText="ok1">
...
</reactive-form>
...
- Get form value (app.component.ts)
...
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 = () => {
}
...
Selections
- 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) | A array of object (validators: [ { label: VALIDATOR_REQUIRED, validator: Validators.required, message: 'This field is required' }]
) | [ ] |
TextBox
| Option | Type | Desciption | Defalut Value | | -------- | :-----: | ------------------------- | ------------: | | type | string | 'password', 'number', ... | none | | disabled | boolean | | false |
- Result data Type: String (example:
'This is my textBox'
)
CheckBox
- Result data Type: Boolean (example:
true || false
)
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
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 }
)
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
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))) |
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']
)
PhoneCode
- Result data Type: Object of phone code & phone number
- Example:
{
code: '+84',
value: '123456789',
alpha2Code: 'VN'
}
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
}
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"
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...'
) | ' ' |
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
|
TextArea
| Option | Type | Desciption | Defalut Value | | ------ | :----: | ------------------------ | ------------: | | rows | Number | Number lines of textarea | 10 |
Province
- 63 provinces & cities of Vietnam
- Result data Type: String (example:
'Thành phố Hà Nội'
)
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
|
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>`
|
SingleSelect2
- Use like DropDown
SwitchInput
- result:
true
orfalse
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
...
<reactive-form [captcha]="'captcha'"></reactive-form>
...
('captcha' is key of Captcha input in Form)
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 | | ""
|
TypeAheadWithoutApi
- use like dropdown & singleselect2
RangerSlider
| Option | Type | Desciption | Defalut Value | | ------- | :----: | ---------- | ------------: | | min | number | | | | max | number | | | step | number | | | | miLabel | string | | | | id | number | | | | unit | string | | |
Update&fill
use
support.UpdateInputsValue(inputs, optionsData)
to update options for Dropdown, Radio, ListCheckBox, Select2use
supportUpdateFormValue(input, valuesData)
to fill data for inputs controllFull Example (app.component.ts):
...
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: '[email protected]',
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);
}
...
Validation
Default Validation
https://angular.io/api/forms/Validators
- Use & Example:
...
...
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
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
...
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)
...
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)
...
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
Theme No.1 (ThemeID: reactive-form-theme-1)
- angular.js: add style.css:
...
"styles": [
...
"node_modules/@reflaunt/reactive-form/style/styles.scss",
],
...
- With Form want use new style: <reactive-form [themeID]="'reactive-form-theme-1'" >