@vicoders/reactive-form v1.0.134
Vicoders Reactive Form
- Vicoders 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 @vicoders/reactive-formor
npm install @vicoders/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 '@vicoders/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 '@vicoders/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" [onInitReactive]="onInitReactive" [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);
}
}
onInitReactive(form) {
form.get('textbox-1').valueChanges.subscribe((val) => {
form.get('textbox-2').setValue(val);
});
}
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
- Result data Type: Array value of options was selected
- Example:
['1', '2']- Set default value: Each option want set default, need add
selected: truefor this.
{
value: 1,
label: 'label'
selected: true
}TexBoxMask
- 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
SingleSelect2
- Use like DropDown
SwitchInput
- result:
trueorfalse
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 |
Gutenberg
| Option | Type | Desciption | Defalut Value |
|---|
add css @import "./../node_modules/@vicoders/ng-gutenberg/style/ng-gutenberg.scss"
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: '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);
}
...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/@vicoders/reactive-form/style/styles.scss",
],
...- With Form want use new style: <reactive-form themeID="'reactive-form-theme-1'" >
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago