vuejs-form
v1.3.0
Published
Form wrapper simplifying developer ability to access, check, fill, and pass input data
Downloads
1,927
Maintainers
Keywords
Readme
That Vue Form
Installation
NPM
npm install --save-dev vuejs-form
Yarn
yarn add vuejs-form --save
CDN
<script src='https://unpkg.com/vuejs-form@latest/build/vuejs-form.min.js'></script>
Four Official Apis
Playground Examples
Curious, but not 100% on whether this is what you're looking for? Try vuejs-form out for yourself before installing -- here's some live examples ready for you to tinker away at!
Simple Vue Example
<template>
<div>
<input type='email' v-model='form.email' placeholder="Email"/>
<span v-if="form.errors().has('email')">
{{ form.errors().get('email') }}
</span>
<input type='password' v-model='form.password' placeholder="Password"/>
<span v-if="form.errors().has('password')">
{{ form.errors().get('password') }}
</span>
<input type='password' v-model='form.password_confirmation' placeholder="Confirm Password"/>
<button :disabled='form.empty()' @click='submit'>Submit</button>
</div>
</template>
import form from 'vuejs-form'
export default {
data: () => ({
form: form({
email: '',
password: '',
password_confirmation: ''
})
.rules({
email: 'email|min:5|required',
password: 'required|min:5|confirmed'
})
.messages({
'email.email': 'Email field must be an email (durr)',
'password.confirmed': 'Whoops, :attribute value does not match :confirmed value',
}),
}),
methods: {
submit() {
if (this.form.validate().errors().any()) return;
alert('Success, form is validated!');
console.log('form all(): ', this.form.all());
console.log('form except(): ', this.form.except('password_confirmation'));
},
}
}
Vue Example Two
Form API
- all
- boolean
- empty
- except
- fill
- filled
- forget
- has
- hasAny
- input
- keys
- make
- missing
- only
- set
- toArray
- wrap
- Extending Form Api
- macro
- localMacro
- forceMacro
- forceLocalMacro
Error Messages Api
- form.errors().any()
- form.errors().all()
- form.errors().list()
- form.errors().set(errors)
- form.errors().forget()
- form.errors().has(field)
- form.errors().get(field)
- form.errors().list(field)
- form.errors().add(field, message)
- form.errors().set(field, messages)
- form.errors().forget(field)
- form.errors().getValidator()
- Extending Errors Api
- form.errors().macro(name, fn)
- form.errors().forceMacro(name, fn)
- form.errors().localMacro(name, fn)
- form.errors().forceLocalMacro(name, fn)
Validator Api
- form.rules({...})
- form.messages({...})
- form.hasValidator()
- form.setValidator({...})
- form.validator()
- form.validate()
- Validator Hooks (Documentation Stored in Vuejs Validators Repository)
- form.validator().before(callbackHook) (see vuejs-validators repo)
- form.validator().after(callbackHook) (see vuejs-validators repo)
- form.validator().passed(callbackHook) (see vuejs-validators repo)
- form.validator().failed(callbackHook) (see vuejs-validators repo)
- form.validator().validateWithoutHooks() (see vuejs-validators repo)
- Extend Validator Api
- form.validator().macro(name, fn)
- form.validator().forceMacro(name, fn)
- form.validator().localMacro(name, fn)
- form.validator().forceLocalMacro(name, fn)
Rules Api
- accepted
- alpha
- alpha_dash
- alpha_num
- array
- between
- boolean
- confirmed
- date
- date_equals
- before (date)
- before_or_equal (date)
- after (date)
- after_or_equal (date)
- greater_than (numeric)
- gte (Greater than or equal numeric)
- less_than (numeric)
- lte (Less than or equal numeric)
- different
- digits
- digits_between
- distinct
- ends_with
- integer
- ip
- ipv4
- ipv6
- json
- max
- min
- not_regex
- not_within
- number
- numeric
- phone
- regex
- required
- same
- starts_with
- string
- url
- within
Quick Vue Example
<template>
<div>
<div v-if="form.errors().any()" v-for="(message, key) in form.errors().list()" :key="`${key}.error`">
{{ message }}
</div>
<input type='text' v-model='form.name' /> <br>
<input type='email' v-model='form.email' /> <br>
<input type='password' v-model='form.password' /> <br>
<input type='password' v-model='form.confirm_password' /> <br>
<hr>
<button :disabled='form.empty()' @click='submit'>
Complete
</button>
</div>
</template>
import form from 'vuejs-form'
export default {
data: () => ({
form: form({
email: '',
password: '',
confirm_password: ''
})
.rules({
email: 'email|min:5|required',
password: 'same:confirm_password',
confirm_password: 'min:6|required',
})
.messages({
'email.required': ':attribute is required',
'email.email': ':attribute must be a valid email',
'email.min': ':attribute may not have less than :min characters',
'password.same': 'Whoops, :attribute does not match the :same field',
}),
}),
watch: {
/*--------------------------------------------------------------
* When Should Your Form "Validate", Providing Error Messages?
*--------------------------------------------------------------
* Form validates every time form data is updated. To
* display errors on form submit, remove watcher &
* move "this.form.validate()" over to submit()
*--------------------------------------------------------------
*/
['form.data']: {
deep: true,
immediate: false,
handler: (now, old) => { this.form.validate(); },
}
},
methods: {
failed() {
console.log('errors: ', this.form.errors().all());
},
passed() {
console.log('data: ', this.form.all());
console.log('wrapped data: ', this.form.wrap('data'));
},
submit() {
return this.form.errors().any() ? this.failed() : this.passed();
},
}
}
Validator Api
- form.rules({...})
- form.messages({...})
- form.validator(...)
- form.validate(...)
- form.hasValidator()
- form.setValidator({...})
Form Register Rules
form(data).rules({
name: 'required|min:4',
link: 'required|url',
category: 'required|within:blue,reg,green'
});
Optionally Use Arrays Syntax Instead Of Pipes
form(data).rules({
name: ['required', 'min:4'],
link: ['required', 'url'],
category: ['required', 'within:blue,reg,green']
});
Form Customize Error Messages
- All rules have global default error messages shown when rule fails validation.
- Optionally, you are able to override the global defaults rule messages
- Simply use the form(data).rules(set)
.messages({ '{field}.{rule}': 'custom message for failing rule on field' });
let data = { email: ['required', 'email'] }
form({ email: 'chad'}).rules({
email: ['required', 'email']
})
.messages({
'email.required': 'Email field is called email, needa make it an email (Hence Email Field Name, dont worry ~ we added validation just in case you forgot to make the email field an email)'
})
Form Validator Instance
form(data).rules(options).messages(customMessages);
// form.validator().addMessage(field, error)
// form.validator().addRule(field, rules)
// etc...
Validate Form Data
- Check current form data against associated form rules
- IMPORTANT: form MUST call validate() method before retrieving current errors
COMMON GOTCHA!!!!
- This wont get the current form errors
- The
form.validate()
method was Never called
let data = { name: '' };
let rules = { name: 'required' };
form(data).rules(rules).errors().list();
// --------------------------------------------
// Form SHOULD fail, but errors list is empty
// --------------------------------------------
// Output: []
// --------------------------------------------
What's the reason?
Retrieving errors before validating form data
would retrieve our error messages Api instance, but it hasn't been filled with our forms error messages.
form.validate() compares form data against form rules, populating our form errors with failing rule messages.
Validate THEN resolve the errors (Using forms fluent api)
let data = { name: '' };
let rules = { name: 'required' };
form(data).rules(rules).validate().errors().list();
// Output: ['Name field is required']
// Again, we'll need to validate before retrieving our
// errors to validate that the values passes our given rules
form.name = 'hello world';
form.errors().list();
// Output: ['Name field is required']
form.validate().errors().list();
// Output: [];
Fluently call validate() before calling errors() is simple and to the point.
At first, this may seem like a tedious extra step. Many may wonder why we don't simply auto-validate the data?
Reason for form.validate().errors()
Instead of simply form.errors()
triggering the validation.
- Reactive frameworks may use
errors()
and the associated Error Messages Api (@See Form Error Messages Api) - Without providing the option for the end developer to determine when the form validates
- Async requests, only validate once we've resolved some given data
- Immediate display of errors (Not always wanted)
- Option Open To Immediately show error messages (@See Vue Watcher Example)
- Some other developers may only want to validate data on form submission
- Many validation rules can be abstracted using the form Api to simply disable the ability to submit a button
- EX:
<button :disabled='form.empty()' @click='submit'> Done </button>
- Then within
submit() method
simply runif (this.form.validate().errors().any()) return;
- That allows the option to set up vuejs-form more like a traditional Form, and avoid many complexities that come along with maintaining the status of our reactive state
- etc...
Form Has Validator
Determine if form has a validator instance attached to it
form.hasValidator(); // true or false
Form Set Validator
- Set Validator Instance
- Optionally import the validator instance itself, and extend its functionality validator().macro(add_method, method).
- Then use form macros to track the current step form.macro(add_method, method).
- vuejs-validators.js Also has validator life cycle hooks documented that are available here, but only documented within vuejs-form.js. Very helpful for multi-step forms
const { form, validator } = require('vuejs-form');
form().macro('setSwitchableValidators', (first, second) => {
this.toggleValidators =
this.toggleBetween = first
});
Rules Api
- accepted
- date
- date_equals
- before (date)
- before_or_equal (date)
- after (date)
- after_or_equal (date)
- greater_than (numeric)
- gte (Greater than or equal numeric)
- less_than (numeric)
- lte (Less than or equal numeric)
- alpha
- alpha_dash
- alpha_num
- array
- between
- boolean
- confirmed
- different
- digits
- digits_between
- distinct
- ends_with
- integer
- ip
- ipv4
- ipv6
- json
- max
- min
- not_regex
- not_within
- number
- numeric
- phone
- regex
- required
- same
- starts_with
- string
- url
- within
Date Equals Rule
(Date) The field under validation must be the same date as the rules date
Passes Date Equals Rule
let form = {
one: '4-22-1997',
two: 'April 22 2025'
}
let rules = {
one: 'date_equals:4-22-1997',
two: 'date_equals:April 22 2025',
}
Fails Date Equals Rule
let form = {
one: '4-22-1997',
two: '2-12-1997'
}
let rules = {
one: 'date_equals:4-24-1998',
two: 'date_equals:1-11-1996',
}
Before Rule
(Date)
The Field under evaluation must be before the compared date
Passes Before (Date) Rule
let form = {
one: '4-22-1997',
two: '2-12-1997'
}
let rules = {
one: 'before:4-22-1998',
two: 'before:2-12-1997',
}
Fails Before (Date) Rule
let form = {
one: '4-22-1997',
two: '3-12-1997'
}
let rules = {
one: 'before:4-22-1997',
two: 'before:2-3-1996',
}
Before Or Equal Rule
(Date) The field under validation must be before or equal to the compared date.
Passes Before Or Equal (Date) Rule
let form = {
one: '4-22-1997',
two: '2-12-1997'
}
let rules = {
one: 'before_or_equal:3-21-1998',
two: 'before_or_equal:2-12-1997',
}
Fails Before Or Equal (Date) Rule
let form = {
one: '4-22-1997',
two: '2-3-1997'
}
let rules = {
one: 'before_or_equal:4-23-1997',
two: 'before_or_equal:2-3-1996',
}
After Rule
(Date)
The Field under evaluation must be after the compared date
Passes After (Date) Rule
let form = {
one: '4-22-1997',
two: '2-2-1997'
}
let rules = {
one: 'after:4-23-1997',
two: 'after:2-3-1996',
}
Date Rule
(Date) The field under validation must be a valid, non-relative date according to the new Date js constructor.
Passes Date Rule
- 4.22.1997
- 4-22-1997
- 4/22/1997
- April 22 1997
- Tuesday April 22 1997
Fails Date Rule
- asdfweadf
- 23423423
- []
Fails After (Date) Rule
let form = {
one: '4-22-1997',
two: '2-12-1997'
}
let rules = {
one: 'after:4-22-1998',
two: 'after:1-11-1996',
}
After Or Equal Rule
(Date) The field under validation must be after or equal to the compared date.
Passes After Or Equal (Date) Rule
let form = {
one: '4-22-1997',
two: '1-11-2013',
}
let rules = {
one: 'after_or_equal:4-22-1997',
two: 'after_or_equal:2-12-2014',
}
Fails After Or Equal (Date) Rule
let form = {
one: '4-22-1997',
two: '2-12-1997'
}
let rules = {
one: 'after_or_equal:4-23-1997',
two: 'after_or_equal:2-3-1996',
}
Accepted Rule
The field under form must be yes, on, 1, or true. This is useful for validating "Terms of Service" acceptance.
Passing Accepted Rule
let data = { terms_of_service: 'no' };
let rules = { terms_of_service: 'accepted' };
// false
form(data).rules(rules).validate().errors().has('terms_of_service');
Failing Accepted Rule
let data = { terms_of_service: null }
let rules = { terms_of_service: 'accepted' }
// true
form(data).rules(rules).validate().errors().has('terms_of_services');
Alpha Rule
The field under form must be entirely alphabetic characters.
Passing Alpha Rule
let data = { letters: 'asdeddadfjkkdjfasdf' };
let rules = { letters: ['alpha'] };
// false
form(data).rules(rules).validate().errors().has('letters');
Failing Alpha Rule
let data = { letters: '5-@'}
let rules = { letters: ['alpha'] }
// true
form(data).rules(rules).validate().errors().has('letters');
Alpha Dash Rule
The field under form may have alpha-numeric characters, as well as dashes and underscores.
Passing Alpha Dash Rule
let data = { slug: 'user_name' };
let rules = { slug: ['alpha_dash'] };
// false
form(data).rules(rules).validate().errors().has('slug');
Failing Alpha Dash Rule
let data = { words: 'hello world'}
let rules = { words: ['alpha_dash'] }
// true
form(data).rules(rules).validate().errors().has('words');
Alpha Num Rule
The field under form must be entirely alpha-numeric characters.
Passing Alpha Num Rule
let data = { key: '4asdasdfe4d23545w634adf' };
let rules = { key: ['alpha_num'] };
// false
form(data).rules(rules).validate().errors().any();
Failing Alpha Num Rule
let data = { identifier: '1-asdf4adf_d_42'}
let rules = { identifier: ['alpha_num'] }
// true
form(data).rules(rules).validate().errors().any();
Array Rule
The field under form must be a JS array.
Passing Array Rule
let data = { list: ['banana', 'broccoli', 'carrot'] };
let rules = { list: 'array' };
// false
form(data).rules(rules).validate().errors().any();
Failing Array Rule
let data = { options: { name: 'hey world' } }
let rules = { options: 'array' }
// true
form(data).rules(rules).validate().errors().any();
Email Rule
The given field value must be an email
Passing Email Rule
let data = { email: '[email protected]' };
let rules = { email: ['email'] };
// false
form(data).rules(rules).validate().errors().any();
Failing Email Rule
let data = { email: '[email protected]'}
let rules = { email: ['email'] }
// true
form(data).rules(rules).validate().errors().any();
Boolean Rule
- Boolish form, not strict boolean check
- Validates that field value is "truthy" or "falsy"
|Truthy|Falsy| |---|---| |1|0| |"1"|"0"| |"on"|"off"| |"On"|"No"| |"ON"|"OFF"| |"yes"|"no"| |"Yes"|"Off"| |"YES"|"NO"| |true|false| |"true"|"false"| |"True"|"False"| |"TRUE"|"FALSE"|
Passing Boolean Rule
let data = { selected: 'Yes' };
let rules = { selected: ['boolean'] };
// false
form(data).rules(rules).validate().errors().any();
Failing Boolean Rule
form = { selected: null };
rules = { selected: ['boolean'] };
// true
form(data).rules(rules).validate().errors().any();
Confirmed form Rule
{field}
value must match{field}_confirmation
value- Example
password
must matchpassword_confirmation
value to passconfirmed
ruled
Passing Confirmed Rule
let data = { password: 'secret', password_confirmation: 'secret' }
let rules = { password: 'confirmed' }
// false
form(data).rules(rules).validate().errors().any();
Failing Confirmed Rule
let data = { password: 'secret' };
let rules = { password: 'confirmed' };
// true
form(data).rules(rules).validate().errors().any();
form.password_confirmation = 'something_something';
// true
form.validate().errors().any();
Passing Confirmed Rule Again
form.password_confirmation = 'secret';
// false
form.validate().errors().any();
Greater Than Rule
(Numeric)
Number must be greater than compared value
Passing greater than rule
let form = {
age: 24,
members: 19,
percentage: 0.4,
};
let rules = {
age: 'greater_than:13',
members: 'greater_than:10',
percentage: 'greater_than:0.35',
};
Failing greater than rule
let form = {
age: 24,
members: 19,
percentage: 0.4,
};
let rules = {
age: 'greater_than:24',
members: 'greater_than:100',
percentage: 'greater_than:0.9',
};
Gte Rule
(Greater Than Or Equal - Numeric) Number must be greater than or equal to compared value
Passing greater than or equal rule (gte)
let form = {
age: 24,
members: 19,
percentage: 0.4,
};
let rules = {
age: 'gte:24',
members: 'gte:10',
percentage: 'gte:0.35',
};
Failing greater than or equal rule (gte)
let form = {
age: 24,
members: 19,
percentage: 0.4,
};
let rules = {
age: 'greater_than:25',
members: 'greater_than:100',
percentage: 'greater_than:0.9',
};
Less Than Rule
(Numeric)
Number must be less than compared value
Passing less than rule
let form = {
age: 24,
members: 19,
percentage: 0.4,
} ;
let rules = {
age: 'less_than:25',
members: 'less_than:20',
percentage: 'less_than:0.8',
}
Failing less than rule
let form = {
age: 24,
members: 19,
percentage: 0.4,
};
let rules = {
age: 'less_than:24',
members: 'less_than:10',
percentage: 'less_than:0.1',
}
Lte Rule
(Less than or equal - Numeric)
Number must be less than or equal to compared value
Passing Less than or equal (lte) rule
let form = {
age: 24,
members: 19,
percentage: 0.4,
} ;
let rules = {
age: 'lte:24',
members: 'lte:20',
percentage: 'lte:0.8',
}
Failing less than or equal (lte) rule
let form = {
age: 24,
members: 19,
percentage: 0.4,
};
let rules = {
age: 'less_than:24',
members: 'less_than:10',
percentage: 'less_than:0.5',
}
Different form Rule
The given field value is different than another field value
Passing Different Rule
let data = { password: 'asdfasdfasdf', confirm_password: 'secret' };
let rules = { password: 'different:confirm_password' };
form(data).rules(rules).validate().errors().any();
Failing Different Rule
let data = { password: 'secret', confirm_password: 'secret' }
let rules = { password: 'different:confirm_password' }
form(data).rules(rules).validate().errors().any();
Digits Rule
The field under form must be numeric and must have an exact length of value.
Passing Digits Rule
let data = { amount: '10000' }
let rules = { amount: 'digits:6' }
form(data).rules(rules).validate().errors().any();
Failing Digits Rule
let data = { amount: '10000' }
let rules = { amount: 'digits:4' }
form(data).rules(rules).validate().errors().any();
Digits Between Rule
The field under form must be numeric and have a length between the lower and upper limit defined.
Passing Digits Between Rule
let data = { amount: '10000' }
let rules = { amount: 'digits_between:4,6' }
form(data).rules(rules).validate().errors().any();
Failing Digits Between Rule
let data = { amount: '10000' }
let rules = { amount: 'digits_between:3,5' }
form(data).rules(rules).validate().errors().any();
Distinct Rule
The field under form must be an array with no duplicate values.
Passing Distinct Rule
let data = { shopping_list: ['ham', 'eggs', 'milk', 'turkey'] }
let rules = { shopping_list: 'distinct' }
form(data).rules(rules).validate().errors().any();
Failing Distinct Rule
let data = { shopping_list: ['ham', 'ham', 'eggs', 'milk', 'turkey'] }
let rules = { shopping_list: 'distinct' }
form(data).rules(rules).validate().errors().any();
Email Rule
The given field value must be an email
Passing Email Rule
let data = { email: '[email protected]' };
let rules = { email: ['email'] };
form(data).rules(rules).validate().errors().any();
Failing Email Rule
let data = { email: '[email protected]'}
let rules = { email: ['email'] }
form(data).rules(rules).validate().errors().any();
Ends With Rule
The field under form must end with one of the given values.
Passing Ends With Rule
let data = { name: 'sammie' };
let rules = { name: 'ends_with:sl,ie,asx' };
form(data).rules(rules).validate().errors().any();
Failing Ends With Rule
let data = { name: 5 };
let rules = { name: 'ends_with:sl,ie,asx' };
form(data).rules(rules).validate().errors().any();
form.setData({ name: 'azure' }).setRules({ name: 'ends_with:sl,ie,asx' })
form.validate().errors().any();
Integer Rule
This form rule does not verify that the input is of the "integer" variable type, only that the input is a string or numeric value that contains an integer.
Passing Integer Rule
let data = { students: 25 }
let rules = { students: ['integer'] }
form(data).rules(rules).validate().errors().any();
Failing Integer Rule
let data = { students: 'yes' }
let rules = { students: ['integer'] }
form(data).rules(rules).validate().errors().any();
IP Rule
This form rule confirms that value is an IP address.
Passing IP Rule
- "115.42.150.37"
- "192.168.0.1"
- "110.234.52.124"
- "2001:0db8:85a3:0000:0000:8a2e:0370:7334" (Ipv6)
Failing IP Rule
- "210.110" – must have 4 octets
- "255" – must have 4 octets
- "y.y.y.y" – the only digit has allowed
- "255.0.0.y" – the only digit has allowed
- "666.10.10.20" – digit must between [0-255]
- "4444.11.11.11" – digit must between [0-255]
- "33.3333.33.3" – digit must between [0-255]
IPv4 Rule
This form rule confirms that value is an IPv4 address.
Passing IPv4 Rule
- "115.42.150.37"
- "192.168.0.1"
- "110.234.52.124"
Failing IPv4 Rule
- "210.110" – must have 4 octets
- "255" – must have 4 octets
- "y.y.y.y" – the only digit has allowed
- "255.0.0.y" – the only digit has allowed
- "666.10.10.20" – digit must between [0-255]
- "4444.11.11.11" – digit must between [0-255]
- "33.3333.33.3" – digit must between [0-255]
- "2001:0db8:85a3:0000:0000:8a2e:0370:7334" (Ipv6)
IPv6 Rule
This form rule confirms that value is an IPv6 address.
Passing IPv6 Rule
- "2001:0db8:85a3:0000:0000:8a2e:0370:7334" (Ipv6)
Failing IPv6 Rule
- "210.110" – must have 4 octets
- "255" – must have 4 octets
- "y.y.y.y" – the only digit has allowed
- "255.0.0.y" – the only digit has allowed
- "666.10.10.20" – digit must between [0-255]
- "4444.11.11.11" – digit must between [0-255]
- "33.3333.33.3" – digit must between [0-255]
- "110.234.52.124"
- "192.168.0.1"
- "115.42.150.37"
Json Rule
The given field value must be a Json String
Passing Json Rule
let data = { content: JSON.stringify({ inspire: 'love' }) };
let rules = { content: 'json' };
form(data).rules(rules).validate().errors().any();
Failing Json Rule
let data = { content: 'fasdf' }
let rules = { content: 'json' }
form(data).rules(rules).validate().errors().any();
Max Rule
The given field must not be more than the defined maximum limit
Passing Max Limit Rule
let data = { password: 'secret' }
let rules = { password: 'max:10' }
form(data).rules(rules).validate().errors().any();
Failing Max Limit Rule
let data = { password: 'secret'}
let rules = { password: 'max:4' }
form(data).rules(rules).validate().errors().any();
Min Rule
The given field must not be less than the defined minimum limit
Passing Min Limit Rule
let data = { password: 'secret' }
let rules = { password: 'min:6' }
form(data).rules(rules).validate().errors().any();
Failing Min Limit Rule
let data = { password: 'secret'}
let rules = { password: 'min:8' }
form(data).rules(rules).validate().errors().any();
Not Regex Rule
The given field value must NOT match the regular expression pattern
Passing Not Regex Rule
let data = { email: 'ex.-fn' };
let rules = { email: ['not_regex:/^.+@.+$/i'] };
form(data).rules(rules).validate().errors().any();
Failing Not Regex Rule
let data = { email: '[email protected]'}
let rules = { email: ['not_regex:/^.+@.+$/i'] }
form(data).rules(rules).validate().errors().any();
Not Within Rule
The given field must NOT be "within" the comma delimited list of items
Passing Not Within Rule
let data = { language: 'PigLatin' }
let rules = { language: 'not_within:German,Spanish,English,Latin' }
form(data).rules(rules).validate().errors().any();
Failing Not Within Rule
let data = { pencil: '2a'};
let rules = { pencil: 'not_within:notebook,pencil,2a,marker,sharpie,whiteboard' };
form(data).rules(rules).validate().errors().any();
Number Rule
The given field must be a Number (Strict Typed Check). See Numeric For Looser Type Checking
Passing Number Rule
let data = { id: 15 };
let rules = { id: ['number'] };
form(data).rules(rules).validate().errors().any();
Failing Number Rule
let data = { id: '15'}
let rules = { id: ['number'] }
form(data).rules(rules).validate().errors().any();
Numeric Rule
Determine if a value is numeric, or is a string that can properly represent a numeric
- Numerical value, not strict number check
- Automatically attempts to cast value to numerical value.
- Validates that field value an integer, decimal, or bigInt.
Passing Numeric Rule
let data = { members: '25' }
let rules = { member: ['numeric'] }
form(data).rules(rules).validate().errors().any();
Failing Numeric Rule
let data = { members: 'yes' }
let rules = { member: ['numeric'] }
form(data).rules(rules).validate().errors().any();
Phone Rule
The given field value must be a phone number
Passing Phone Rule
let data = { send_sms: ['555-555-5555'] }
let rules = { send_sms: ['phone'] }
form(data).rules(rules).validate().errors().any();
Failing Phone Rule
let data = { send_sms: '+(3) - 4 32'}
let rules = { send_sms: ['phone'] }
form(data).rules(rules).validate().errors().any();
Phone Number Formats Within Testing Coverage
- +61 1 2345 6789
- +61 01 2345 6789
- 01 2345 6789
- 01-2345-6789
- (01) 2345 6789
- (01) 2345-6789
- 5555555555
- (555) 555 5555
- 555 555 5555
- +15555555555
- 555-555-5555
(Any contributions welcome (vuejs-validators.js repo) for improving regex form patterns for current rules as well as adding new rules)
Regex Rule
The given field value must match the regular expression pattern
Passing Regex Rule
let data = { email: '[email protected]' };
let rules = { email: ['regex:/^.+@.+$/i'] };
form(data).rules(rules).validate().errors().any();
Failing Regex Rule
let data = { email: 'ex.-fn'}
let rules = { email: ['regex:/^.+@.+$/i'] }
form(data).rules(rules).validate().errors().any();
Required Rule
Validates that a given field exists and its value is set
Passing Required Rule
let data = { name: 'jules' };
let rules = { name: ['required'] };
form(data).rules(rules).validate().errors().any();
Failing Required Rule
let data = { name: '' };
let rules = { name: ['required'] };
form(data).rules(rules).validate().errors().any();
Same form Rule
The given field value is the same as another field value
Passing Same Rule
let data = { password: 'secret', confirm_password: 'secret' }
let rules = { password: 'same:confirm_password' }
form(data).rules(rules).validate().errors().any();
Failing Same Rule
let data = { password: 'asdfasdfasdf', confirm_password: 'secret' };
let rules = { password: 'same:confirm_password' };
form(data).rules(rules).validate().errors().any();
Starts With Rule
The field under form must start with one of the given values.
Passing Starts With Rule
let data = { name: 'sammie' };
let rules = { name: 'starts_with:joe,sam,tom' };
form(data).rules(rules).validate().errors().any();
Failing Starts With Rule
let data = { name: 5 };
let rules = { name: 'starts_with:sl,ie,asx' };
form(data).rules(rules).validate().errors().any();
form.setData({ name: 'azure' })
.setRules({ name: 'starts_with:joe,sam,tom'})
.validate()
.errors()
.any();
String Rule
The given field value must be a String
Passing String Rule
let data = { name: 'sammie' };
let rules = { name: 'string' };
form(data).rules(rules).validate().errors().any();
Failing String Rule
let data = { name: 54345 }
let rules = { name: 'string' }
form(data).rules(rules).validate().errors().any();
Url Rule
The given field value must be an http(s) url
Passing Url Rule
let data = { link: 'https://cleancode.studio' };
let rules = { link: 'url' };
form(data).rules(rules).validate().errors().any();
Failing Url Rule
let data = { link: 'httP/[email protected]'}
let rules = { link: 'url' }
form(data).rules(rules).validate().errors().any();
Within Rule
The given field must be "within" the comma delimited list of items
Passing Within Rule
let data = { name: 'Sam' }
let rules = { name: 'within:James,Boronica,Sam,Steve,Lenny' }
form(data).rules(rules).validate().errors().any();
Failing Within Rule
let data = { name: 'jake'};
let rules = { name: 'within:patricia,veronica,samuel,jeviah' };
form(data).rules(rules).validate().errors().any();
Form Error Messages Api
form.errors() Methods
- any()
- all()
- list()
- set(errors)
- forget()
- has(field)
- get(field)
- list(field)
- add(field, message)
- set(field, messages)
- forget(field)
- getValidator()
Any Errors
Determine if there are "any" errors (bool)
let data = { name: '' };
let rules = { name: 'required'};
form(data).rules(rules).errors().any();
Output: true
All Errors
Retrieve all errors within the errors object
let data = { name: '', email: '' };
let rules = { name: 'required', email: 'email|required' };
form(data).rules(rules).validate().errors().all();
Output:
{
name: [
'name field is required'
],
email: [
'email field must be an email address',
'email field is required'
]
}
List Errors
Retrieve all errors within the errors object
let data = { name: '', email: '' };
let rules = { name: 'required', email: 'email|required' };
form(data).rules(rules).validate().errors().list();
Output:
[
'name field is required',
'email field must be an email address',
'email field is required'
]
Set Errors
Set all errors
let data = { name: '' };
let rules = { name: 'required' };
form(data).rules(rules).validate();
form.errors().list();
// Output: ['name is a required field']
form.errors().set({ notice: ['set this random error message'] });
form.errors().list()
Output: ['set this random error message']
Forget Errors
Forget errors and reset them to empty
let data = { name: '' };
let rules = { name: 'required' };
form(data).rules(rules).validate().errors().list();
// Output: ['Name is a required field']
form.errors().forget();
form.errors().list();
Output: []
Has Error
Determine if a specific field has error messages
let data = { name: '', email: '[email protected]' };
let rules = { name: 'required', email: 'email|required' };
form(data).rules(rules).validate();
form.errors().has('name');
form.errors().has('email');
form.errors().has('something_else');
Output:
has name: true
has email: false
has something_else: false
Get Error
Get first error message for a specific field
let data = { name: '' };
let rules = { name: 'required|min:3'};
form(data).rules(rules).validate().errors().get('name');
Output: "Name is a required field"
List Error
List errors for a specific field
let data = { name: '' };
let rules = { name: 'required|min:3'};
form(data).rules(rules).validate().errors().list('name');
Output: ['name is a required field', 'name must be longer than 3 characters']
Add Error
Add error message for a specific field
let data = { name: '' };
let rules = { name: 'required|min:3'};
form(data).rules(rules).validate().add(
'name', 'four failures in a row. Two more failures before your locked out'
);
form.errors().list('name');
Output: ['name is a required field', 'name must be longer than 3 characters', 'four failures in a row. Two more failures before your locked out']
Set Error
Set error messages for a specific field
let data = { name: '' };
let rules = { name: 'required' };
form(data).rules(rules).validate().list('name');
Output: ['name is a required field']
form.errors().set('name', ['random messages', 'set on', 'the name field']);
form.errors().list('name');
Output: ['random messages', 'set on', 'the name field']
Forget Error
Forget error messages for a specific field
let data = { name: '' };
let rules = { name: 'required' };
form(data).rules(rules).validate().list('name');
Output: ['name is a required field']
form.errors().forget('name');
form.errors().list('name');
Output: []
Extending the Error Bag
Error Bag Macro
Adds custom method on form error bag instance
let example = form(data).rules(rules);
let example.errors().macro('count', function () {
return this.list().length;
});
// example.errors().count() === example.errors().list().length
Error Bag ForceMacro
Allows you to overwrite pre-defined macro and over write core error message bag functions (Use With Caution)
let example = form({ name: '' }).rules({ name: 'required|min:3' }).validate();
example.errors().get('name'); // Outputs: "Name is a required field"
example.errors().forceMacro('get', function (field) {
if (this.has(field)) {
return this.list(field).join(', ') + '.';
}
});
example.errors().get('name'); // Outputs: "Name is a required field, name must have at least 3 characters."
- [all](#all
- [boolean](#boolean
- [empty](#empty
- [except](#except
- [fill](#fill
- [filled](#filled
- [forceMacro](#forcemacro
- [forget](#forget
- [has](#has
- [hasAny](#hasany
- [input](#input
- [keys](#keys
- [macro](#macro
- [make](#make
- [missing](#missing
- [only](#only
- [set](#set
- [toArray](#toarray
- [wrap](#wrap
all()
The all method returns the underlying input object represented by the form:
form({ name: 'sarah', email: '[email protected]' }).all();
// { name: 'sarah', email: '[email protected]' }
boolean(property)
The boolean method determines if the given field has a truthy or falsy values:
Truthy values: true, "true", "yes", "on", "1", 1
Falsy values: Everything else
const LoginForm = form({
name: '',
email: '',
terms: ''
})
LoginForm.terms = true
LoginForm.boolean('terms') // true
LoginForm.terms = 'true'
LoginForm.boolean('terms') // true
LoginForm.terms = 'yes'
LoginForm.boolean('terms') // true
LoginForm.terms = 'on'
LoginForm.boolean('terms') // true
LoginForm.terms = "1"
LoginForm.boolean('terms') // true
LoginForm.terms = 1
LoginForm.boolean('terms') // true
empty(one, two, three, ...)
The empty method determines if the input property exists but the value is empty:
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.empty('name') // false
ExampleForm.empty('name', 'email') // false
ExampleForm.empty('id') // true
except(one, two, three, ...)
The except method grabs all of the inputs except the properties passed in:
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.except('id')
/**
* { name: 'sarah', email: '[email protected]' }
*/
ExampleForm.except('id', 'name')
/**
* { email: '[email protected]' }
*/
fill({ key: value, keyTwo: valueTwo, etc... })
The fill method allows you to fill in new or empty values without overriding existing values:
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.fill({
id: 2,
name: 'tim',
email: '[email protected]'
})
ExampleForm.all()
// { id: 2, name: 'sarah', email: '[email protected]' }
filled(propertyOne, propertyTwo, etc...)
The filled method determine if a value is filled (AKA not empty):
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.filled('id', 'name') // false
ExampleForm.filled('name', 'email') // true
forceMacro(key, fn)
forceMacro can be used to extend form object and FORCIBLY OVERWRITE base form behavior (Use VERY cautiously and prefer macro over forceMacro)
NOTE: Use forceLocalMacro if you only want to extend a specific form instance instead of all form instances.
import form from 'vuejs-form';
form().forceMacro('all', function () {
return this.keys().reduce((list, field) => ({
...list,
[field]: {
name: field,
value: this.data[field],
errors: this.errors().list(field),
}
}),
{});
})
form({ name: 'sam' }).rules({ name: 'required' }).validate();
# forceMacro implementation of form.all() Outputs
{
name: {
value: 'sam',
name: 'name',
errors: ['Name field is required']
}
}
forget(propertyOne, propertyTwo, etc...)
The forget method will remove or "forget" a key value pair from the form input data
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.forget('id', 'name')
ExampleForm.all() // { email: '[email protected]' }
has(propertyOne, propertyTwo, etc...)
The has method will determine if a key exists within the form input data
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.has('id', 'name') // true
ExampleForm.has('something', 'id', 'name') // false
hasAny(propertyOne, propertyTwo, etc...)
The hasAny method will determine if a key has any of the given properties within the form input data
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.hasAny('id', 'name') // true
ExampleForm.hasAny('something', 'id', 'name') // true
input(property, default = false)
The input method will resolve a given input value or default to false. You can define a default as the second parameter
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.input('id') // false
ExampleForm.input('id', 1) // 1
ExampleForm.input('name', 'tim') // sarah
keys()
The keys method will resolve an array of the input keys
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.keys() // ['id', 'name', 'email']
macro(key, fn)
The macro method can be used to extend the forms base behavior with custom methods/functions
NOTE: Use localMacro if you only want to extend a specific form instance instead of all form instances.
import form from 'vuejs-form';
form(data).macro('count', () => {
return this.keys().length;
});
// form.count() === form.keys().length
make({ ... })
The make method will "make" a new form when used on the underlying class (With the proxy used on all forms)
import { VueForm } from 'vuejs-form'
const ExampleForm = VueForm.make({ id: '', name: 'sarah', email: '[email protected]' })
ExampleForm.all() // { id: '', name: 'sarah', email: '[email protected]' }
missing(propertyOne, propertyTwo, ...)
The missing method will determine if the form is missing the following properties
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' })
ExampleForm.missing('id') // false
ExampleForm.missing('something') // true
ExampleForm.missing('name', 'email') // false
ExampleForm.missing('name', 'email', 'something') // true
only(propertyOne, propertyTwo, ...)
The only method will return an object of "only" the input properties you defined
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' })
ExampleForm.only('name', 'email') // { name: 'sarah', email: '[email protected]' }
ExampleForm.only('id', 'name') // { id: '', name: 'sarah' }
ExampleForm.only('id') // { id: '' }
set({ key: value, keyTwo: valueTwo, etc... })
The set method allows you to set new and override previous values:
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.set({
id: 2,
name: 'tim',
email: '[email protected]',
password: 'secret',
})
ExampleForm.all()
// { id: 2, name: 'tim', email: '[email protected]', password: 'secret' }
toArray()
The toArray method transforms the input into an array of key value pair objects:
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.toArray()
/**
[
{ key: 'id', value: '' },
{ key: 'name', value: 'sarah' },
{ key: 'email', value: '[email protected]' }
]
*/
wrap(key)
The wrap method allows you to wrap the input within a given object key:
const ExampleForm = form({ id: '', name: 'sarah', email: '[email protected]' });
ExampleForm.wrap('data')
/**
{
data: {
id: '',
name: 'sarah',
email: '[email protected]'
}
}
*/
Extend Api
Extend and append functionality to just about every single major service this package provides
- Extend Form Using Macros
- Extend Validator Using Macros
- Extend Error Messages Bag Using Macros
- Add Custom Error Messages
- Create Custom Validation Rule
- Create Custom Validation Rules
- Extend Into Multi Step Form Example
Extend Form Using Macros
const form = require('vuejs-form');
form().macro('shortcut', () => {
return this.validate().errors().list();
});
let example = form({ name: '' }).rules({ name: 'required' });
example.shortcut();
// Output: ['Name is a required field'];
Extend Validator Using Macros
const { form, validator } = require('vuejs-form');
validator().macro('translate', ({ dictionary, locale }) => {
if (!Object.keys(dictionary).includes(locale)) {
console.warn(`Translation dictionary does not include passed ${locale}`);
return this;
}
const language = Object.keys(this.messages);
const dictionary_words = key => Object.keys(dictionary[locale]).includes(key);
language.filter(dictionary_words).forEach(key => { this.messages[key] = dictionary[`${locale}.${key}`] });
return this;
});
let example = form({ name: '' }).rules({ name: 'required' });
let locale = 'ru';
let dictionary = { ru: { email: "Эл.почта" } };
example.validator().translate({ locale, dictionary });
Extending Error Messages Api
Error Bag Macro
Adds custom method on form error bag instance
let example = form(data).rules(rules);
let example.errors().macro('count', function () {
return this.list().length;
});
// example.errors().count() === example.errors().list().length
Error Bag ForceMacro
Allows you to overwrite pre-defined macro and over write core error message bag functions (Use With Caution)
let example = form({ name: '' }).rules({ name: 'required|min:3' }).validate();
example.errors().get('name'); // Outputs: "Name is a required field"
example.errors().forceMacro('get', function (field) {
if (this.has(field)) {
return this.list(field).join(', ') + '.';
}
});
example.errors().get('name'); // Outputs: "Name is a required field, name must have at least 3 characters."
Extending: Custom Error Messages
This has nothing to do with the error messages Api
These are the literal string output a rule message will display when it fails
Here we'll customize error messages for specific rules on any given field
Globally, each rule provides a default error message
Easily override rule's default error message
Simply pass 'messages' to our validator
Only override messages you want to
let data = { name: '', email: '' };
let rules = {
name: ['min:3', 'max:12', 'string', 'required'],
email: ['email', 'required']
};
let customMessages = {
'name.min': 'Whoops! :attribute is less than :min characters',
'name.required': 'Wha oh, doesnt look like there any value for your :attribute field',
'email.email': 'Really? Email is called Email...it has to be an email...',
};
form(data).rules(rules).messages(customMessages).validate().errors().all();
Extending: Custom Rules
Add Your Own Validation Rules
- Easily add, or override, validation rules
- Add a group of rules at a time
- Add a single rule add a time
Extending: Custom Rules - Single Rule
form().validator().extend(rule_name, [message, rule])`
let example = form({ name: 'timmy' }).rules({ name: 'uppercase' });
example.validator().extend('uppercase', [
':attribute must be uppercase',
({ value, validator, parameters }) => value === value.toUpperCase(),
]);
// true
example.validate().errors().has('name');
// "Name must be uppercase"
example.errors().get('name');
Extending: Custom Rules - multiple rules
form.validator().extend({ first: [message, rule], second: [message, rule], etc... })
let example = form({ name: '' }).rules({ name: ['required_with:last_name', 'required' ] });
example.validator().extend({
uppercase: [
':attribute must be uppercase',
({ value }) => value === value.toUpperCase(),
],
not_uppercase: [
':attribute must not be uppercase',
({ value }) => value !== value.toUpperCase()
],
required_without: [
':attribute is only required when form is missing :required_without field',
({ validator, parameters }) => !Object.keys(validator.data).includes(parameters[0])
],
required_with: [
':attribute is required with the :required_with field',
({ validator, parameters }) => Object.keys(validator.data).includes(parameters[0])
],
});
Extend Form Into Multi Step Form (Not tested, but good base to provide some ideas)
- Not actually tested outside of these docs, but solid starting point
<template>
<div class="form-container">
<small>
Step {{ multi.steps().currentStep }} of {{ multi.steps().count() }}
</small>
<!-- Pass form data as props, via vuex, emit event on any data change from all form field children, or if your safe wit it simply reference this.$parent.multi.steps().current from the child field. If you do so, don't plan on using the child component outside of multi-step forms. this.$parent is traditionally bad practice -->
<component :is="multi.steps().current().getComponent()"></component>
<button class="btn-default" v-if="multi.steps().hasPrev()" @click="multi.steps().prev()">
Prev
</button>
<button class="btn-primary" :disabled='multi.steps().current().errors().any()' v-if="multi.steps().hasNext()" @click="multi.steps().next()">
Next
</button>
<button class="btn-success" :disabled='multi.steps().current().errors().any()' v-if="multi.steps().isLast()" @click="submit">
Done
</button>
</div>
</template>
const MultiStep = function (form) {
this.sections = {};
this.currentStep = 0;
this.parent = function () {
return form;
};
this.current = function () {
if (this.has(this.currentStep)) {
return this.get(this.currentStep);
} else {
console.error("No current step found");
}
};
this.currentComponent = function () {
return this.current().component_is
};
this.count = function () {
return this.list().length;
};
this.travel = function (to) {
if (this.has(to)) {
this.currentStep = to;
return this.current();
} else {
return console.error(`form step ${to} not found`);
}
};
this.prev = function () {
if (!this.isFirst()) {
this.currentStep = this.currentStep - 1;
return this.current();
} else {
console.error('already on the very first step')
}
};
this.next = function () {
if (!this.isLast()) {
this.currentStep = this.currentStep + 1;
return this.current();
} else {
console.log('last step')
}
};
this.hasPrev = function () {
return this.has(this.currentStep + 1);
};
this.hasCurrent = function () {
return this.has(this.currentStep);
};
this.isFirst = function () {
return this.hasCurrent() && !this.hasPrev()
};
this.isLast = function () {
return this.hasCurrent() && !this.hasNext();
};
this.hasNext = function () {
return this.has(this.currentStep + 1)
};
this.any = function () {
const isEmpty = value => ([
value === null || value === '',
Array.isArray(value) && value.length === 0,
typeof value === 'object' && Object.keys(value).length === 0
].includes(true));
return !isEmpty(this.list());
};
this.has = function (group) {
return Object.keys(this.sections).includes(group)
&& this.sections[group].length > 0
};
this.all = function () {
return this.sections;
};
this.list = function (group = false) {
return group
? this.sections[group]
: Object.keys(this.sections)
.map(group => this.sections[group])
.reduce((list, groups) => [ ...list, ...groups ], []);
};
this.get = function (group) {
if (this.has(group)) {
return this.sections[group][0];
}
};
this.add = function(group, item) {
this.sections[group] = Array.isArray(this.sections[group])
? this.sections[group]
: [];
this.sections[group].push(item);
return this;
};
this.set = function (group, items = []) {
if (typeof items === 'object') {
this.sections = items;
} else {
this.sections[group] = items;
}
};
this.forget = function (group) {
if (typeof group === 'undefined') {
this.sections = {};
} else {
this.sections[group] = [];
}
};
};
const steppable = function (form = {}) {
return new MultiStep(validator);
};
form().macro('multiple', () => {
this.steppables = steppable(this);
this.steps = function () {
return this.steppables;
};
this.first = function () {
return this.steps().get('0')
}
this.last = function () {
return this.steps().list(this.steps().count() - 1);
};
this.current = function () {
return this.steps().current();
};
return this;
});
form().multiple().steps();
/** Use macro to extend form and append vue component instance to each form step **/
form().macro('hasComponent', () => typeof this.component_is !== 'undefined');
form().macro('getComponent', () => {
this.hasComponent() ? this.component_is : `<template><div>No Component Registered On This Form Instance</div></template>`
});
form().macro('is', (vue_instance) => {
this.component_is = vue_instance;
return this;
});
form().multiple().steps();
const { name_fields, password_fields, final_step } = require('./components/forms/steps/index.js');
let multi = form({}).multiple();
multi.steps().add(0,
form({
last_name: '',
first_name: ''
})
.rules({
last_name: ['required', 'min:3', 'string', 'different:first_name'],
first_name: ['required', 'min:3', 'string', 'different:last_name']
})
.messages({
'last_name.required': 'Last name is required',
'last_name.min': 'Last name may not be less than :min characters',
'last_name.different': 'Last Name must be different than first name',
'last_name.string': 'Last Name must be a string',
'first_name.required': 'First name is required',
'first_name.min': 'First name may not be less than :min characters',
'first_name.different': 'Last Name must be different than last name',
'first_name.string': 'First name must be of the string type'
})
.is(name_fields)
);
multi.steps().add(1,
form({
password: '',
password_confirmation: '',
})
.rules({
password: ['required', 'min:5', 'string', 'confirmed'],
})
.is(password_fields)
);
multi.steps().add(2,
form({ terms_of_service: '' })
.rules({ terms_of_service: 'accepted|required' })
.messages({
'terms_of_service.accepted': "Must accept terms of service before moving on",
'terms_of_service.required': "Must accept terms of service before submitting form",
})
.is(final_step)
);
export default {
name: 'multi-step-form',
data: () => ({ multi }),
methods: {
submit() {
let data = this.multi.steps().list().reduce((data, step) => ({ ...data, ...step.all() }), {});
console.log('all data: ', form(data).all());
}
}
};
Macros
Macros Overview
- Macros Are The Primary Way This Package And All Major Apis Are Extendable
- One of this packages primary goals is to be unbiased, but provide extreme extendability
- Macros, and specifically four types of macros, are the primary way we are able to obtain this goal
- Macros, in concept are extremely simple.
- They simply add functions, extending behavior
- Macros can accept arguments
- Macros have access to
this
within the callback you are registering - There are four types of macros.
- localMacro(name, fn) - extend instance with custom function (only the specific ) Simplest Example
form().hello(); // undefined
form().macro('hello', function () {
return 'world';
});
form().hello(); // 'world'
Extend Apis Via Macros
macro(name, fn)
- extend api with function
- instance gains functions and constructor prototype gain function
- applied to all new instances
- can not overwrite core functions
- can not overwrite pre-defined macros
- can not be applied