npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@vicoders/reactive-form

v1.0.134

Published

- [Vicoders Reactive Form](#vicoders-reactive-form) - [Introduce](#introduce) - [Install](#install) - [Use](#use) - [Selections](#selections) - [TextBox](#textbox) - [CheckBox](#checkbox) - [Radio](#radio) - [Dropdown](#dropdown) - [DateTimePick

Downloads

2,229

Readme

Vicoders Reactive Form

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-form

or

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;
  }
...
...
<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

| 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 or false

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, Select2

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

  • Full 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/@vicoders/reactive-form/style/styles.scss",
            ],
...
  • With Form want use new style: <reactive-form [themeID]="'reactive-form-theme-1'" >