├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── npm-publish-github-packages.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── build ├── vuejs-form.js └── vuejs-form.min.js ├── bundler ├── badges.md ├── bundle.js ├── change_log.md ├── contribute.md ├── extend.md ├── header.md ├── highlight.md ├── installation.md ├── license.md ├── macros.md ├── markdown.js ├── purpose.md ├── security_vulnerabilities.md ├── setup.md ├── utilization.md ├── validator.md ├── versioning.md ├── vue.md └── vuejs-form-purpose-statement.png ├── code_of_conduct.md ├── dist ├── helpers │ ├── accessor.js │ ├── dataGet.js │ ├── dataSet.js │ ├── exists.js │ ├── fieldsOf.js │ ├── isEmpty.js │ ├── nestedValue.js │ ├── setKeys.js │ ├── setProperties.js │ └── variadic.js ├── index.js └── methods │ ├── all.js │ ├── anyFilled.js │ ├── boolean.js │ ├── build.js │ ├── empty.js │ ├── except.js │ ├── extend.js │ ├── fill.js │ ├── filled.js │ ├── forceLocalMacro.js │ ├── forceMacro.js │ ├── forget.js │ ├── has.js │ ├── hasAny.js │ ├── input.js │ ├── keys.js │ ├── localMacro.js │ ├── macro.js │ ├── make.js │ ├── missing.js │ ├── only.js │ ├── override.js │ ├── proxy.js │ ├── set.js │ ├── toArray.js │ ├── use.js │ └── wrap.js ├── docs ├── .vuepress │ └── config.js ├── README.md └── api │ ├── all.md │ ├── boolean.md │ ├── empty.md │ ├── except.md │ ├── fill.md │ ├── filled.md │ ├── forceMacro.md │ ├── forget.md │ ├── has.md │ ├── hasAny.md │ ├── input.md │ ├── keys.md │ ├── macro.md │ ├── make.md │ ├── missing.md │ ├── only.md │ ├── set.md │ ├── toArray.md │ └── wrap.md ├── mix-manifest.json ├── package.json ├── src ├── helpers │ ├── accessor.js │ ├── dataGet.js │ ├── dataSet.js │ ├── exists.js │ ├── fieldsOf.js │ ├── isEmpty.js │ ├── nestedValue.js │ └── variadic.js ├── index.js └── methods │ ├── all.js │ ├── anyFilled.js │ ├── boolean.js │ ├── build.js │ ├── empty.js │ ├── except.js │ ├── extend.js │ ├── fill.js │ ├── filled.js │ ├── forceLocalMacro.js │ ├── forceMacro.js │ ├── forget.js │ ├── has.js │ ├── hasAny.js │ ├── input.js │ ├── keys.js │ ├── localMacro.js │ ├── macro.js │ ├── make.js │ ├── missing.js │ ├── only.js │ ├── proxy.js │ ├── set.js │ ├── toArray.js │ ├── use.js │ └── wrap.js └── test ├── methods ├── accessor_test.js ├── all_test.js ├── anyFilled_test.js ├── boolean_test.js ├── empty_test.js ├── except_test.js ├── fill_test.js ├── filled_test.js ├── forceLocal_macro.test.js ├── forceMacro_test.js ├── forget_test.js ├── hasAny_test.js ├── has_test.js ├── input_test.js ├── keys_test.js ├── localMacro_test.js ├── macro_test.js ├── missing_test.js ├── only_test.js ├── set_test.js ├── toArray_test.js ├── use_test.js └── wrap_test.js └── tests.js /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish-github-packages.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: 16 18 | - run: npm ci 19 | - run: npm test 20 | 21 | publish-gpr: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: read 26 | packages: write 27 | steps: 28 | - uses: actions/checkout@v2 29 | - uses: actions/setup-node@v2 30 | with: 31 | node-version: 16 32 | registry-url: https://npm.pkg.github.com/ 33 | - run: npm ci 34 | - run: npm publish 35 | env: 36 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | .npm-debug.log 4 | npm-debug.log 5 | test/benchmark.js 6 | test/implementations.js 7 | coverage 8 | .nyc_output 9 | .DS_Store 10 | package-lock.json 11 | docs/.vuepress/dist/* -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | test 3 | coverage 4 | .nyc_output 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '12' 4 | - '10' 5 | - '8' 6 | script: 7 | - npm run coverage 8 | 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Change Log 4 | 5 | --- 6 | - [Release 1.3.0 (Minor)](#release-130) 7 | - [Release 1.2.9 (Minor)](#release-129) 8 | - [Release 1.2.8 (Minor)](#release-128) 9 | - [Release 1.2.7 (Minor)](#release-127) 10 | - [Release 1.2.6 (Minor)](#release-126) 11 | - [Release 1.2.5 (Minor)](#release-125) 12 | - [Release 1.2.4 (Minor)](#release-124) 13 | - [Release 1.2.3 (Minor)](#release-123) 14 | - [Release 1.2.2 (Minor)](#release-122) 15 | - [Release 1.2.1 (Minor)](#release-121) 16 | - [Release 1.2.0 (Major)](#release-120) 17 | - [Release 1.1.1](#release-111) 18 | - [Release 1.1.0](#release-110) 19 | 20 | --- 21 | 22 | ### Release 1.3.0 23 | 24 | --- 25 | - Fixed [first keypress not working bug](https://github.com/zhorton34/vuejs-form/issues/20) 26 | 27 | --- 28 | 29 | --- 30 | 31 | ### Release 1.2.9 32 | 33 | --- 34 | - Removed unused documentation assets 35 | 36 | --- 37 | 38 | ### Release 1.2.8 39 | 40 | --- 41 | - date validation rule 42 | - date equals validation rule 43 | - before (date) validation rule 44 | - before_or_equal (date) validation rule 45 | - after (date) validation rule 46 | - after_or_equal (date) validation rule 47 | - less_than (numeric) validation rule 48 | - greater_than (numeric) validation rule 49 | - lte (less than or equal numeric) validation rule 50 | - gte (greater than or equal numeric) validation rule 51 | 52 | 53 | --- 54 | 55 | ### Release 1.2.7 56 | 57 | --- 58 | - overwrite method outdated in place of forceMacro 59 | - macro method refactored, in depth testing and documentation 60 | - localMacro method created, in depth testing, and documentation 61 | - forceMacro method created, in depth testing, and documentation 62 | - forceLocalMacro method created, in depth testing, and documentation 63 | - Form Api is macro capable 64 | - Validator Api is macro capable 65 | - Error Messages Api is macro capable 66 | - Macro capabilities documentation for each macro type 67 | 68 | --- 69 | 70 | ### Release 1.2.6 71 | 72 | --- 73 | 74 | - Beautified Docs A Bit 75 | - Vuejs Validations Ref Updated 76 | - MessageBag Added to Package Exports 77 | - MessageBagFactory Added to Package Exports 78 | - Error Messages Api Documentation Readme Updated 79 | 80 | --- 81 | 82 | ### Release 1.2.5 83 | 84 | --- 85 | 86 | - Updated Cdn Documented Link Examples To Reference Latest Instead Of Specific Version 87 | 88 | 89 | --- 90 | 91 | ### Release 1.2.4 92 | 93 | --- 94 | 95 | - Updated Purpose.md Documentation To Us Image Notepad Message 96 | 97 | 98 | --- 99 | 100 | ### Release 1.2.3 101 | 102 | --- 103 | 104 | - Updated Change Log Release Link References 105 | - Updated Purpose.md Documentation To Us Image Notepad Message 106 | 107 | 108 | --- 109 | 110 | ### Release 1.2.2 111 | 112 | --- 113 | 114 | - Updated Document Headers 115 | - Removed api.md section of Documentation 116 | - Removed bloated docs from setup.md 117 | - Added cdn installation and npm installation examples 118 | 119 | --- 120 | 121 | ### Release 1.2.1 122 | 123 | --- 124 | 125 | - Updated Documentation To Start With "Purpose" Of Package 126 | - Removed Documentation Content From Header.md 127 | - Caught Change Log Up 128 | 129 | --- 130 | 131 | ### Release 1.2.0 132 | 133 | --- 134 | 135 | - Documentation Updated 136 | - First Official Stable Release 137 | - Semantic Versioning Officially Supported 138 | 139 | 140 | 141 | --- 142 | 143 | ### Release 1.1.1 144 | 145 | --- 146 | 147 | - CDN Setup 148 | - CDN Documentation Added 149 | - Added markdown.js for internal markup creation 150 | - Added Security Vulnerabilities Documentation 151 | - Added Versioning To Documentation 152 | - Added Code Of Conduct To Documentation 153 | - Extensive Documentation 154 | - Security Vulnerabilities Docs 155 | - Code Of Conduct Docs 156 | - Markdown Support Class 157 | - highlight.md 158 | - Versioning implementation documented 159 | 160 | 161 | 162 | --- 163 | 164 | ### Release 1.1.0 165 | 166 | --- 167 | 168 | - "form.getErrors()" replaced with "form.errors()" 169 | - "form.getValidator()" replaced with "form.validator()" 170 | - "vuejs-validators" setup as dev dependency 171 | - "ValidatableForm" Export ~ (Ex: const { ValidatableForm } = require('vuejs-form')) 172 | - Default import is ValidatableForm (Ex: import form from 'vuejs-form' has validator || import { form } from 'vuejs-form' does not have validator) 173 | 174 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ### Code Of Conduct 2 | 3 | The Clean Code Studio code of conduct is derived from Laravel code of of conduct. Any violations 4 | of the code of conduct may be reported to Zachary Horton (zak@cleancode.studio) 5 | 6 | - Participants will be tolerant of opposing views. 7 | 8 | - Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks. 9 | 10 | - When interpreting the words and actions of others, participants should always assume good intentions. 11 | 12 | - Behavior that can be reasonably considered harassment will not be tolerated. 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contribute 2 | 3 | PRs are welcomed to this project. 4 | If you want to improve the vuejs-form library, add 5 | functionality or improve the docs please feel free to submit a PR. 6 | 7 | 8 | Clean Code Studio 9 | Clean Code Studio ~ Simplify 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zak Horton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bundler/badges.md: -------------------------------------------------------------------------------- 1 | [![Travis](https://img.shields.io/travis/zhorton34/vuejs-form/master.svg?style=flat-square)](https://travis-ci.org/zhorton34/vuejs-form/builds) 2 | [![npm downloads](https://img.shields.io/npm/dm/vuejs-form.svg?style=flat-square)](http://badge.fury.io/js/vuejs-form) 3 | [![npm license](https://img.shields.io/npm/l/vuejs-form.svg?style=flat-square)](http://badge.fury.io/js/vuejs-form) 4 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 5 | [![dependencies](https://img.shields.io/badge/dependencies-none-brightgreen.svg?style=flat-square)](https://github.com/zhorton34/vuejs-form/blob/master/package.json) 6 | [![npm version](https://img.shields.io/npm/v/vuejs-form.svg?style=flat-square)](http://badge.fury.io/js/vuejs-form) 7 | [![HitCount](http://hits.dwyl.com/zhorton34/vuejs-form.svg)](http://hits.dwyl.com/zhorton34/vuejs-form) 8 | [![Open Source Love svg1](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/) 9 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity) -------------------------------------------------------------------------------- /bundler/bundle.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | const { readFileSync, readdirSync, writeFileSync } = require('fs'); 5 | 6 | const methods = readdirSync('docs/api', 'utf-8'); 7 | const tableOfContents = methods.map((file) => `- [${file.replace('.md', '')}](#${file.replace('.md', '').toLowerCase()}`).join('\n'); 8 | const methodDocumentation = methods.map((file) => { 9 | let content = readFileSync(`docs/api/${file}`, 'utf-8'); 10 | 11 | const lines = content.split('\n'); 12 | 13 | lines[0] = `###${lines[0]}`; 14 | lines.pop(); 15 | lines.pop(); 16 | 17 | content = lines.join('\n'); 18 | content = content.replace(/(\r\n|\r|\n){2,}/g, '$1\n'); 19 | 20 | return content; 21 | }).join('\n\n'); 22 | 23 | const bundle = file => readFileSync(`bundler/${file}.md`, 'utf-8'); 24 | const ReadMe = (content = []) => writeFileSync('README.md', content.join('\n\n')); 25 | const ChangeLog = (content = []) => writeFileSync('CHANGELOG.md', content.join('\n\n')); 26 | 27 | ChangeLog([bundle('change_log')]); 28 | ReadMe([ 29 | ...['badges', 'purpose', 'header', 'installation', 'highlight', 'vue', 'validator'].map(bundle), 30 | ...[tableOfContents, methodDocumentation], 31 | ...['extend', 'macros', 'utilization'].map(bundle), 32 | 33 | ...['contribute', 'security_vulnerabilities', 'change_log', 'versioning', 'license'].map(bundle), 34 | ]); 35 | 36 | -------------------------------------------------------------------------------- /bundler/change_log.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Change Log 4 | 5 | --- 6 | - [Release 1.3.0 (Minor)](#release-130) 7 | - [Release 1.2.9 (Minor)](#release-129) 8 | - [Release 1.2.8 (Minor)](#release-128) 9 | - [Release 1.2.7 (Minor)](#release-127) 10 | - [Release 1.2.6 (Minor)](#release-126) 11 | - [Release 1.2.5 (Minor)](#release-125) 12 | - [Release 1.2.4 (Minor)](#release-124) 13 | - [Release 1.2.3 (Minor)](#release-123) 14 | - [Release 1.2.2 (Minor)](#release-122) 15 | - [Release 1.2.1 (Minor)](#release-121) 16 | - [Release 1.2.0 (Major)](#release-120) 17 | - [Release 1.1.1](#release-111) 18 | - [Release 1.1.0](#release-110) 19 | 20 | --- 21 | 22 | ### Release 1.3.0 23 | 24 | --- 25 | - Fixed [first keypress not working bug](https://github.com/zhorton34/vuejs-form/issues/20) 26 | 27 | --- 28 | 29 | --- 30 | 31 | ### Release 1.2.9 32 | 33 | --- 34 | - Removed unused documentation assets 35 | 36 | --- 37 | 38 | ### Release 1.2.8 39 | 40 | --- 41 | - date validation rule 42 | - date equals validation rule 43 | - before (date) validation rule 44 | - before_or_equal (date) validation rule 45 | - after (date) validation rule 46 | - after_or_equal (date) validation rule 47 | - less_than (numeric) validation rule 48 | - greater_than (numeric) validation rule 49 | - lte (less than or equal numeric) validation rule 50 | - gte (greater than or equal numeric) validation rule 51 | 52 | 53 | --- 54 | 55 | ### Release 1.2.7 56 | 57 | --- 58 | - overwrite method outdated in place of forceMacro 59 | - macro method refactored, in depth testing and documentation 60 | - localMacro method created, in depth testing, and documentation 61 | - forceMacro method created, in depth testing, and documentation 62 | - forceLocalMacro method created, in depth testing, and documentation 63 | - Form Api is macro capable 64 | - Validator Api is macro capable 65 | - Error Messages Api is macro capable 66 | - Macro capabilities documentation for each macro type 67 | 68 | --- 69 | 70 | ### Release 1.2.6 71 | 72 | --- 73 | 74 | - Beautified Docs A Bit 75 | - Vuejs Validations Ref Updated 76 | - MessageBag Added to Package Exports 77 | - MessageBagFactory Added to Package Exports 78 | - Error Messages Api Documentation Readme Updated 79 | 80 | --- 81 | 82 | ### Release 1.2.5 83 | 84 | --- 85 | 86 | - Updated Cdn Documented Link Examples To Reference Latest Instead Of Specific Version 87 | 88 | 89 | --- 90 | 91 | ### Release 1.2.4 92 | 93 | --- 94 | 95 | - Updated Purpose.md Documentation To Us Image Notepad Message 96 | 97 | 98 | --- 99 | 100 | ### Release 1.2.3 101 | 102 | --- 103 | 104 | - Updated Change Log Release Link References 105 | - Updated Purpose.md Documentation To Us Image Notepad Message 106 | 107 | 108 | --- 109 | 110 | ### Release 1.2.2 111 | 112 | --- 113 | 114 | - Updated Document Headers 115 | - Removed api.md section of Documentation 116 | - Removed bloated docs from setup.md 117 | - Added cdn installation and npm installation examples 118 | 119 | --- 120 | 121 | ### Release 1.2.1 122 | 123 | --- 124 | 125 | - Updated Documentation To Start With "Purpose" Of Package 126 | - Removed Documentation Content From Header.md 127 | - Caught Change Log Up 128 | 129 | --- 130 | 131 | ### Release 1.2.0 132 | 133 | --- 134 | 135 | - Documentation Updated 136 | - First Official Stable Release 137 | - Semantic Versioning Officially Supported 138 | 139 | 140 | 141 | --- 142 | 143 | ### Release 1.1.1 144 | 145 | --- 146 | 147 | - CDN Setup 148 | - CDN Documentation Added 149 | - Added markdown.js for internal markup creation 150 | - Added Security Vulnerabilities Documentation 151 | - Added Versioning To Documentation 152 | - Added Code Of Conduct To Documentation 153 | - Extensive Documentation 154 | - Security Vulnerabilities Docs 155 | - Code Of Conduct Docs 156 | - Markdown Support Class 157 | - highlight.md 158 | - Versioning implementation documented 159 | 160 | 161 | 162 | --- 163 | 164 | ### Release 1.1.0 165 | 166 | --- 167 | 168 | - "form.getErrors()" replaced with "form.errors()" 169 | - "form.getValidator()" replaced with "form.validator()" 170 | - "vuejs-validators" setup as dev dependency 171 | - "ValidatableForm" Export ~ (Ex: const { ValidatableForm } = require('vuejs-form')) 172 | - Default import is ValidatableForm (Ex: import form from 'vuejs-form' has validator || import { form } from 'vuejs-form' does not have validator) 173 | 174 | -------------------------------------------------------------------------------- /bundler/contribute.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Contribute 4 | 5 | --- 6 | 7 | PRs are welcomed to this project. 8 | If you want to improve the vuejs-form library, add 9 | functionality or improve the docs please feel free to submit a PR. 10 | -------------------------------------------------------------------------------- /bundler/extend.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Extend Api 4 | 5 | --- 6 | 7 | Extend and append functionality to just about every single major service this package provides 8 | 9 | - [Extend Form Using Macros](#extend-form-using-macros) 10 | - [Extend Validator Using Macros](#extend-validator-using-macros) 11 | - [Extend Error Messages Bag Using Macros](#extend-error-messages-api) 12 | - [Add Custom Error Messages](#extending-custom-error-messages) 13 | - [Create Custom Validation Rule](#extending-custom-rules---single-rule) 14 | - [Create Custom Validation Rules](#extending-custom-rules---multiple-rules) 15 | - [Extend Into Multi Step Form Example](#extend-form-into-multi-step-form-not-tested-but-good-base-to-provide-some-ideas) 16 | 17 | #### Extend Form Using Macros 18 | ```javascript 19 | const form = require('vuejs-form'); 20 | 21 | form().macro('shortcut', () => { 22 | return this.validate().errors().list(); 23 | }); 24 | 25 | let example = form({ name: '' }).rules({ name: 'required' }); 26 | 27 | example.shortcut(); 28 | // Output: ['Name is a required field']; 29 | ``` 30 | 31 | #### Extend Validator Using Macros 32 | ```javascript 33 | const { form, validator } = require('vuejs-form'); 34 | 35 | validator().macro('translate', ({ dictionary, locale }) => { 36 | if (!Object.keys(dictionary).includes(locale)) { 37 | console.warn(`Translation dictionary does not include passed ${locale}`); 38 | 39 | return this; 40 | } 41 | 42 | const language = Object.keys(this.messages); 43 | const dictionary_words = key => Object.keys(dictionary[locale]).includes(key); 44 | language.filter(dictionary_words).forEach(key => { this.messages[key] = dictionary[`${locale}.${key}`] }); 45 | 46 | return this; 47 | }); 48 | 49 | let example = form({ name: '' }).rules({ name: 'required' }); 50 | 51 | let locale = 'ru'; 52 | let dictionary = { ru: { email: "Эл.почта" } }; 53 | 54 | example.validator().translate({ locale, dictionary }); 55 | ``` 56 | 57 | 58 | --- 59 | 60 | #### Extending Error Messages Api 61 | 62 | --- 63 | 64 | --- 65 | 66 | ##### Error Bag Macro 67 | 68 | --- 69 | Adds custom method on form error bag instance 70 | 71 | ```js 72 | let example = form(data).rules(rules); 73 | 74 | let example.errors().macro('count', function () { 75 | return this.list().length; 76 | }); 77 | 78 | 79 | // example.errors().count() === example.errors().list().length 80 | ``` 81 | 82 | 83 | --- 84 | 85 | ##### Error Bag ForceMacro 86 | 87 | --- 88 | Allows you to overwrite pre-defined macro and over write core error message bag functions (Use With Caution) 89 | 90 | ```js 91 | let example = form({ name: '' }).rules({ name: 'required|min:3' }).validate(); 92 | 93 | example.errors().get('name'); // Outputs: "Name is a required field" 94 | 95 | example.errors().forceMacro('get', function (field) { 96 | if (this.has(field)) { 97 | return this.list(field).join(', ') + '.'; 98 | } 99 | }); 100 | 101 | example.errors().get('name'); // Outputs: "Name is a required field, name must have at least 3 characters." 102 | ``` 103 | 104 | #### Extending: Custom Error Messages 105 | - This has nothing to do with the error messages Api 106 | - These are the literal string output a rule message will display when it fails 107 | - Here we'll customize error messages for specific rules on any given field 108 | 109 | - Globally, each rule provides a default error message 110 | - Easily override rule's default error message 111 | - Simply pass 'messages' to our validator 112 | - Only override messages you want to 113 | 114 | ```javascript 115 | let data = { name: '', email: '' }; 116 | 117 | let rules = { 118 | name: ['min:3', 'max:12', 'string', 'required'], 119 | email: ['email', 'required'] 120 | }; 121 | 122 | let customMessages = { 123 | 'name.min': 'Whoops! :attribute is less than :min characters', 124 | 'name.required': 'Wha oh, doesnt look like there any value for your :attribute field', 125 | 126 | 'email.email': 'Really? Email is called Email...it has to be an email...', 127 | }; 128 | 129 | form(data).rules(rules).messages(customMessages).validate().errors().all(); 130 | ``` 131 | 132 | #### Extending: Custom Rules 133 | > Add Your Own Validation Rules 134 | 135 | - Easily add, or override, validation rules 136 | - Add a group of rules at a time 137 | - Add a single rule add a time 138 | 139 | #### Extending: Custom Rules - Single Rule 140 | > form().validator().extend(rule_name, [message, rule])` 141 | ```js 142 | let example = form({ name: 'timmy' }).rules({ name: 'uppercase' }); 143 | 144 | example.validator().extend('uppercase', [ 145 | ':attribute must be uppercase', 146 | ({ value, validator, parameters }) => value === value.toUpperCase(), 147 | ]); 148 | 149 | // true 150 | example.validate().errors().has('name'); 151 | 152 | // "Name must be uppercase" 153 | example.errors().get('name'); 154 | ``` 155 | 156 | #### Extending: Custom Rules - multiple rules 157 | > `form.validator().extend({ first: [message, rule], second: [message, rule], etc... })` 158 | ```js 159 | let example = form({ name: '' }).rules({ name: ['required_with:last_name', 'required' ] }); 160 | 161 | example.validator().extend({ 162 | uppercase: [ 163 | ':attribute must be uppercase', 164 | ({ value }) => value === value.toUpperCase(), 165 | ], 166 | not_uppercase: [ 167 | ':attribute must not be uppercase', 168 | ({ value }) => value !== value.toUpperCase() 169 | ], 170 | required_without: [ 171 | ':attribute is only required when form is missing :required_without field', 172 | ({ validator, parameters }) => !Object.keys(validator.data).includes(parameters[0]) 173 | ], 174 | required_with: [ 175 | ':attribute is required with the :required_with field', 176 | ({ validator, parameters }) => Object.keys(validator.data).includes(parameters[0]) 177 | ], 178 | }); 179 | ``` 180 | 181 | #### Extend Form Into Multi Step Form (Not tested, but good base to provide some ideas) 182 | - Not actually tested outside of these docs, but solid starting point 183 | 184 | ```html 185 | 207 | ``` 208 | 209 | ```javascript 210 | 211 | const MultiStep = function (form) { 212 | this.sections = {}; 213 | this.currentStep = 0; 214 | 215 | this.parent = function () { 216 | return form; 217 | }; 218 | 219 | this.current = function () { 220 | if (this.has(this.currentStep)) { 221 | return this.get(this.currentStep); 222 | } else { 223 | console.error("No current step found"); 224 | } 225 | }; 226 | 227 | this.currentComponent = function () { 228 | return this.current().component_is 229 | }; 230 | this.count = function () { 231 | return this.list().length; 232 | }; 233 | 234 | this.travel = function (to) { 235 | if (this.has(to)) { 236 | this.currentStep = to; 237 | 238 | return this.current(); 239 | } else { 240 | return console.error(`form step ${to} not found`); 241 | } 242 | }; 243 | 244 | this.prev = function () { 245 | if (!this.isFirst()) { 246 | this.currentStep = this.currentStep - 1; 247 | 248 | return this.current(); 249 | } else { 250 | console.error('already on the very first step') 251 | } 252 | }; 253 | 254 | 255 | this.next = function () { 256 | if (!this.isLast()) { 257 | this.currentStep = this.currentStep + 1; 258 | 259 | return this.current(); 260 | } else { 261 | console.log('last step') 262 | } 263 | }; 264 | 265 | this.hasPrev = function () { 266 | return this.has(this.currentStep + 1); 267 | }; 268 | 269 | this.hasCurrent = function () { 270 | return this.has(this.currentStep); 271 | }; 272 | 273 | this.isFirst = function () { 274 | return this.hasCurrent() && !this.hasPrev() 275 | }; 276 | 277 | this.isLast = function () { 278 | return this.hasCurrent() && !this.hasNext(); 279 | }; 280 | 281 | this.hasNext = function () { 282 | return this.has(this.currentStep + 1) 283 | }; 284 | 285 | this.any = function () { 286 | const isEmpty = value => ([ 287 | value === null || value === '', 288 | Array.isArray(value) && value.length === 0, 289 | typeof value === 'object' && Object.keys(value).length === 0 290 | ].includes(true)); 291 | 292 | return !isEmpty(this.list()); 293 | }; 294 | 295 | this.has = function (group) { 296 | return Object.keys(this.sections).includes(group) 297 | && this.sections[group].length > 0 298 | }; 299 | 300 | this.all = function () { 301 | return this.sections; 302 | }; 303 | 304 | this.list = function (group = false) { 305 | return group 306 | ? this.sections[group] 307 | : Object.keys(this.sections) 308 | .map(group => this.sections[group]) 309 | .reduce((list, groups) => [ ...list, ...groups ], []); 310 | }; 311 | 312 | this.get = function (group) { 313 | if (this.has(group)) { 314 | return this.sections[group][0]; 315 | } 316 | }; 317 | 318 | this.add = function(group, item) { 319 | this.sections[group] = Array.isArray(this.sections[group]) 320 | ? this.sections[group] 321 | : []; 322 | 323 | this.sections[group].push(item); 324 | 325 | return this; 326 | }; 327 | 328 | this.set = function (group, items = []) { 329 | if (typeof items === 'object') { 330 | this.sections = items; 331 | } else { 332 | this.sections[group] = items; 333 | } 334 | }; 335 | 336 | this.forget = function (group) { 337 | if (typeof group === 'undefined') { 338 | this.sections = {}; 339 | } else { 340 | this.sections[group] = []; 341 | } 342 | }; 343 | }; 344 | 345 | 346 | const steppable = function (form = {}) { 347 | return new MultiStep(validator); 348 | }; 349 | 350 | form().macro('multiple', () => { 351 | this.steppables = steppable(this); 352 | 353 | this.steps = function () { 354 | return this.steppables; 355 | }; 356 | 357 | this.first = function () { 358 | return this.steps().get('0') 359 | } 360 | 361 | this.last = function () { 362 | return this.steps().list(this.steps().count() - 1); 363 | }; 364 | 365 | this.current = function () { 366 | return this.steps().current(); 367 | }; 368 | 369 | 370 | return this; 371 | }); 372 | 373 | form().multiple().steps(); 374 | 375 | 376 | /** Use macro to extend form and append vue component instance to each form step **/ 377 | form().macro('hasComponent', () => typeof this.component_is !== 'undefined'); 378 | form().macro('getComponent', () => { 379 | this.hasComponent() ? this.component_is : `` 380 | }); 381 | 382 | form().macro('is', (vue_instance) => { 383 | this.component_is = vue_instance; 384 | 385 | return this; 386 | }); 387 | 388 | form().multiple().steps(); 389 | 390 | const { name_fields, password_fields, final_step } = require('./components/forms/steps/index.js'); 391 | 392 | let multi = form({}).multiple(); 393 | 394 | multi.steps().add(0, 395 | form({ 396 | last_name: '', 397 | first_name: '' 398 | }) 399 | .rules({ 400 | last_name: ['required', 'min:3', 'string', 'different:first_name'], 401 | first_name: ['required', 'min:3', 'string', 'different:last_name'] 402 | }) 403 | .messages({ 404 | 'last_name.required': 'Last name is required', 405 | 'last_name.min': 'Last name may not be less than :min characters', 406 | 'last_name.different': 'Last Name must be different than first name', 407 | 'last_name.string': 'Last Name must be a string', 408 | 'first_name.required': 'First name is required', 409 | 'first_name.min': 'First name may not be less than :min characters', 410 | 'first_name.different': 'Last Name must be different than last name', 411 | 'first_name.string': 'First name must be of the string type' 412 | }) 413 | .is(name_fields) 414 | ); 415 | 416 | multi.steps().add(1, 417 | form({ 418 | password: '', 419 | password_confirmation: '', 420 | }) 421 | .rules({ 422 | password: ['required', 'min:5', 'string', 'confirmed'], 423 | }) 424 | .is(password_fields) 425 | ); 426 | 427 | multi.steps().add(2, 428 | form({ terms_of_service: '' }) 429 | .rules({ terms_of_service: 'accepted|required' }) 430 | .messages({ 431 | 'terms_of_service.accepted': "Must accept terms of service before moving on", 432 | 'terms_of_service.required': "Must accept terms of service before submitting form", 433 | }) 434 | .is(final_step) 435 | ); 436 | 437 | 438 | export default { 439 | name: 'multi-step-form', 440 | data: () => ({ multi }), 441 | 442 | methods: { 443 | submit() { 444 | let data = this.multi.steps().list().reduce((data, step) => ({ ...data, ...step.all() }), {}); 445 | 446 | console.log('all data: ', form(data).all()); 447 | } 448 | } 449 | }; 450 | ``` 451 | -------------------------------------------------------------------------------- /bundler/header.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bundler/highlight.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Playground Examples 4 | Curious, but not 100% on whether this is what you're looking for? 5 | Try vuejs-form out for yourself before installing -- here's some live examples ready for you to tinker away at! 6 | 7 | --- 8 | - [Example One (Validate Form On Each User Submit)](https://codepen.io/zhorton34/pen/zYvWZYz) 9 | - [Example One (Validate Form On Each User Update)](https://codepen.io/zhorton34/pen/xxwaYez) 10 | --- 11 | 12 | ## Simple Vue Example 13 | 14 | --- 15 | ```html 16 | 33 | ``` 34 | ```js 35 | import form from 'vuejs-form' 36 | 37 | export default { 38 | data: () => ({ 39 | form: form({ 40 | email: '', 41 | password: '', 42 | password_confirmation: '' 43 | }) 44 | .rules({ 45 | email: 'email|min:5|required', 46 | password: 'required|min:5|confirmed' 47 | }) 48 | .messages({ 49 | 'email.email': 'Email field must be an email (durr)', 50 | 'password.confirmed': 'Whoops, :attribute value does not match :confirmed value', 51 | }), 52 | }), 53 | 54 | methods: { 55 | submit() { 56 | if (this.form.validate().errors().any()) return; 57 | 58 | alert('Success, form is validated!'); 59 | 60 | console.log('form all(): ', this.form.all()); 61 | console.log('form except(): ', this.form.except('password_confirmation')); 62 | }, 63 | } 64 | } 65 | ``` 66 | 67 | --- 68 | 69 | ## Vue Example Two 70 | 71 | --- 72 | 73 | 74 | --- 75 | 76 | ## Form API 77 | 78 | --- 79 | 80 | - [all](#all) 81 | - [boolean](#boolean) 82 | - [empty](#empty) 83 | - [except](#except) 84 | - [fill](#fill) 85 | - [filled](#filled) 86 | - [forget](#forget) 87 | - [has](#has) 88 | - [hasAny](#hasany) 89 | - [input](#input) 90 | - [keys](#keys) 91 | - [make](#make) 92 | - [missing](#missing) 93 | - [only](#only) 94 | - [set](#set) 95 | - [toArray](#toarray) 96 | - [wrap](#wrap) 97 | - **Extending Form Api** 98 | - [macro](#macros) 99 | - [localMacro](#macros) 100 | - [forceMacro](#macros) 101 | - [forceLocalMacro](#macros) 102 | 103 | --- 104 | 105 | ## Error Messages Api 106 | 107 | --- 108 | 109 | - [form.errors().any()](#any-errors) 110 | - [form.errors().all()](#all-errors) 111 | - [form.errors().list()](#list-errors) 112 | - [form.errors().set(errors)](#set-errors) 113 | - [form.errors().forget()](#forget-errors) 114 | - [form.errors().has(field)](#has-error) 115 | - [form.errors().get(field)](#get-error) 116 | - [form.errors().list(field)](#list-error) 117 | - [form.errors().add(field, message)](#add-error) 118 | - [form.errors().set(field, messages)](#set-field-errors) 119 | - [form.errors().forget(field)](#forget-field) 120 | - [form.errors().getValidator()](#get-errors-validator) 121 | - **Extending Errors Api** 122 | - [form.errors().macro(name, fn)](#macros) 123 | - [form.errors().forceMacro(name, fn)](#macros) 124 | - [form.errors().localMacro(name, fn)](#macros) 125 | - [form.errors().forceLocalMacro(name, fn)](#macros) 126 | 127 | 128 | --- 129 | 130 | ## Validator Api 131 | 132 | --- 133 | 134 | - [form.rules({...})](#form-register-rules) 135 | - [form.messages({...})](#form-customize-error-messages) 136 | - [form.hasValidator()](#form-has-validator) 137 | - [form.setValidator({...})](#form-set-rules) 138 | - [form.validator()](#form-validator-instance) 139 | - [form.validate()](#validate-form-data) 140 | - **Validator Hooks (Documentation Stored in Vuejs Validators Repository)** 141 | - [form.validator().before(callbackHook) (see vuejs-validators repo)](https://github.com/zhorton34/vuejs-validators) 142 | - [form.validator().after(callbackHook) (see vuejs-validators repo)](https://github.com/zhorton34/vuejs-validators) 143 | - [form.validator().passed(callbackHook) (see vuejs-validators repo)](https://github.com/zhorton34/vuejs-validators) 144 | - [form.validator().failed(callbackHook) (see vuejs-validators repo)](https://github.com/zhorton34/vuejs-validators) 145 | - [form.validator().validateWithoutHooks() (see vuejs-validators repo)](https://github.com/zhorton34/vuejs-validators) 146 | - **Extend Validator Api** 147 | - [form.validator().macro(name, fn)](#macros) 148 | - [form.validator().forceMacro(name, fn)](#macros) 149 | - [form.validator().localMacro(name, fn)](#macros) 150 | - [form.validator().forceLocalMacro(name, fn)](#macros) 151 | 152 | --- 153 | 154 | ## Rules Api 155 | 156 | --- 157 | - [accepted](#accepted-rule) 158 | - [alpha](#alpha-rule) 159 | - [alpha_dash](#alpha_dash-rule) 160 | - [alpha_num](#alpha_num-rule) 161 | - [array](#array-rule) 162 | - [between](#between-rule) 163 | - [boolean](#boolean-rule) 164 | - [confirmed](#confirmed-rule) 165 | - [date](#date-rule) 166 | - [date_equals](#date-equals-rule) 167 | - [before (date)](#before-rule) 168 | - [before_or_equal (date)](#before-or-equal-rule) 169 | - [after (date)](#after-rule) 170 | - [after_or_equal (date)](#after-or-equal-rule) 171 | - [greater_than (numeric)](#greater-than-rule) 172 | - [gte (Greater than or equal numeric)](#gte-rule) 173 | - [less_than (numeric)](#less-then-rule) 174 | - [lte (Less than or equal numeric)](#lte-rule) 175 | - [different](#different-rule) 176 | - [digits](#digits-rule) 177 | - [digits_between](#digits_between-rule) 178 | - [distinct](#distinct-rule) 179 | - [email](#email-rule) 180 | - [ends_with](#ends_with-rule) 181 | - [integer](#integer-rule) 182 | - [ip](#ip-rule) 183 | - [ipv4](#ipv4-rule) 184 | - [ipv6](#ipv6-rule) 185 | - [json](#json-rule) 186 | - [max](#max-rule) 187 | - [min](#min-rule) 188 | - [not_regex](#not_regex-rule) 189 | - [not_within](#not_within-rule) 190 | - [number](#number-rule) 191 | - [numeric](#numeric-rule) 192 | - [phone](#phone-rule) 193 | - [regex](#regex-rule) 194 | - [required](#required-rule) 195 | - [same](#same-rule) 196 | - [starts_with](#starts_with-rule) 197 | - [string](#string-rule) 198 | - [url](#url-rule) 199 | - [within](#within-rule) 200 | 201 | 202 | -------------------------------------------------------------------------------- /bundler/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## coffee icon vuejs form installation title Installation 4 | 5 | 6 | 7 | ### NPM 8 | 9 | ```bash 10 | npm install --save-dev vuejs-form 11 | ``` 12 | 13 | ### Yarn 14 | 15 | ```bash 16 | yarn add vuejs-form --save 17 | ``` 18 | 19 | ### CDN 20 | 21 | ```bash 22 | 23 | ``` 24 | 25 | --- 26 | 27 | ## Four Official Apis 28 | 29 | --- 30 | 31 | - [Form Api](#form-api) 32 | - [Rules Api](#rules-api) 33 | - [Validator Api](#validator-api) 34 | - [Error Messages Api](#error-messages-api) 35 | -------------------------------------------------------------------------------- /bundler/license.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## License 4 | 5 | --- 6 | 7 | MIT © [Zachary Horton (Clean Code Studio)](https://www.youtube.com/c/cleancodestudio) 8 | [Clean Code Studio ~ Clean Code Clean Life ~ Simplify](https://cleancode.studio) -------------------------------------------------------------------------------- /bundler/macros.md: -------------------------------------------------------------------------------- 1 | ## Macros 2 | 3 | - [Overview](#macros-overview) 4 | - [Macros](#extend-apis-via-macros) 5 | - [Local Macros](#extend-instances-via-local-macros) 6 | - [Force Macros](#overwrite-a-given-apis-core-via-force-macros) 7 | - [Force Local Macros](#overwrite-a-given-instances-core-via-force-local-macros) 8 | - [Macro Capable Apis List](#macro-capable-apis-list) 9 | 10 | ### Macros Overview 11 | - Macros Are The Primary Way This Package And All Major Apis Are Extendable 12 | - One of this packages primary goals is to be unbiased, but provide _extreme_ extendability 13 | - Macros, and specifically four types of macros, are the primary way we are able to obtain this goal 14 | - Macros, in concept are extremely simple. 15 | - They simply add functions, extending behavior 16 | - Macros can accept arguments 17 | - Macros have access to `this` within the callback you are registering 18 | - There are four types of macros. 19 | - localMacro(name, fn) - extend instance with custom function (only the specific ) 20 | **Simplest Example** 21 | ``` 22 | form().hello(); // undefined 23 | 24 | form().macro('hello', function () { 25 | return 'world'; 26 | }); 27 | 28 | form().hello(); // 'world' 29 | ``` 30 | 31 | ### Extend Apis Via Macros 32 | `macro(name, fn)` 33 | - extend api with function 34 | - instance gains functions and constructor prototype gain function 35 | - applied to all new instances 36 | - can not overwrite core functions 37 | - can not overwrite pre-defined macros 38 | - can not be applied on more than a single instance (Technically you can try, it just won't actually do anything the methods already added) 39 | 40 | **Example: macro()** 41 | Simplify the validation process using a "completed" method wrapper 42 | 43 | ```js 44 | form().macro('completed', function () { 45 | this.validate(); 46 | 47 | return this.errors().any() === false; 48 | }); 49 | 50 | form({ name: '' }).rules({ name: 'required' }).completed(); // false 51 | form({ name: 'samuel' }).rules({ name: 'required' }).completed(); // true 52 | ``` 53 | 54 | ### Extend Instances Via LocalMacros 55 | `localMacro(name, fn)` 56 | **Example:** See multi-step form example provided for `forceLocalMacro` example. It provides a powerful example of possible ways to combine local and forceLocal macros. 57 | - extend instance with function 58 | - instance gains function 59 | - NOT applied to all new instances 60 | - can not overwrite core instances 61 | - can not overwrite pre-defined macros 62 | - can be applied on more than a single instance, but not automatically inherited globally 63 | **Example: localMacro** 64 | ```js 65 | ``` 66 | ### Overwrite A Given Apis Core Via Force Macros 67 | `forceMacro(name, fn)` 68 | - overwrite core api or existing macros 69 | - instance and constructor prototypes apply function 70 | - applies on all instances 71 | - can overwrite core functions 72 | - can overwrite pre-defined macros 73 | _NOTE: This is a very powerful function, use caution and when overriding core behavior understand things depend on other things under the hood. Only use this type of macro if no other macro solves your needs_ 74 | 75 | **Force Macro Example** 76 | > Overwrite Error Message Apis Get Method To Get A Comma List Of Messages Instead Of The First Message 77 | ```js 78 | let example = form({ name: '' }).rules({ name: 'required|min:3' }).validate(); 79 | 80 | example.errors().get('name'); // 'Name field is required' 81 | 82 | example.errors().forceMacro('get', function (field) { 83 | if (this.has(field)) { 84 | return this.list(field).join(', '); 85 | } 86 | }); 87 | 88 | example.errors().get('name'); // 'Name field is required, Name field must be more than 3 characters' 89 | ``` 90 | 91 | ### Overwrite A Given Instances Core Via Force Local Macros 92 | `forceLocalMacro(name, fn)` 93 | - overwrite instances core behavior or existing macros 94 | - instance applies function 95 | - not applied by all instances, only on single instance 96 | - can overwrite core functions 97 | - can overwrite pre-defined macros 98 | _NOTE: Not quite as dangerous as forceMacro (it's only applied on one instance instead of globally across the api), it is still powerful and should be used only if localMacro doesn't solve your needs_ 99 | 100 | **Create A Parent Form With Multiple Child Forms** 101 | _Example uses localMacro & forceLocalMacro (Advanced example)_ 102 | 103 | ```js 104 | 105 | let MultiStepForm = form(); 106 | 107 | MultiStepForm.localMacro('register', function (children = []) { 108 | this.current = 0; 109 | const $parent = this; 110 | 111 | // Set local macros on each one of the child forms 112 | this.children = children.map(child => { 113 | // 1. localMacro: parent() method references parent form will all steps 114 | child.localMacro('parent', () => $parent); 115 | 116 | // 2. forceLocalMacro: override validate() method to add in a step where child validate method populates parent() errors bag 117 | // Below we'll override the parent validate() method using a forceLocalMacro as well to two way bind this setup 118 | child.forceLocalMacro('validate', function () { 119 | const childForm = this; 120 | childForm.validator().validate(); 121 | 122 | childForm.parent().errors().set({ 123 | ...childForm.parent().errors().all(), 124 | ...childForm.errors().all(), 125 | }); 126 | 127 | return this; 128 | }) 129 | }); 130 | 131 | /** Add helper methods for managing the multi-step form process **/ 132 | this.step = () => this.steps(this.current); 133 | this.steps = index => index !== 'undefined' && this.hasStep(index) ? this.children(index) : this.children; 134 | this.hasStep = index => typeof this.children[index] !== 'undefined'; 135 | 136 | this.prevStep = () => { this.current = this.current - 1; }; 137 | this.stepNumber = () => this.current + 1; 138 | this.stepsCount = () => this.steps().length ; 139 | 140 | this.hasPrevStep = () => this.hasStep(this.current - 1); 141 | this.hasNextStep = () => this.hasStep(this.current + 1); 142 | this.isLastStep = () => this.step() === this.steps(this.stepCount()); 143 | this.isFirstStep = () => this.step() === this.steps(0); 144 | 145 | this.completedStepsCount = () => this.stepNumber(); 146 | this.remainingStepsCount = () => this.stepsCount() - this.stepNumber(); 147 | this.percentageCompleted = () => (this.completedStepsCount() / 100) * this.stepsCount(); 148 | this.percentageRemaining = () => (this.remainingStepsCount() / 100) * this.stepsCount(); 149 | this.next = () => { 150 | if (this.hasNextStep()) this.current = this.current + 1; 151 | }; 152 | this.prev = () => { 153 | if (this.hasPrevStep()) this.current = this.current - 1; 154 | }; 155 | this.to = index => { 156 | if (this.hasStep(index)) this.current = index; 157 | } 158 | 159 | return this; 160 | }); 161 | 162 | /** forceLocalMacro to overwrite default all method on MultiStepForm ~ aggregating all child form data **/ 163 | MultiStepForm.forceLocalMacro('all', function () { 164 | return this.children.reduce((data, child) => ({ 165 | ...data, 166 | ...child 167 | }), 168 | {}); 169 | }); 170 | 171 | /** forceLocalMacro to overwrite default validate method on MultiStepForm ~ setting all error messages **/ 172 | MultiStepForm.forceLocalMacro('validate', function () { 173 | this.errors().set( 174 | this.children.reduce((errors, child) => ({ 175 | ...errors, 176 | ...child.validate().errors().all() 177 | }), 178 | {}) 179 | ); 180 | 181 | return this; 182 | }); 183 | 184 | MultiStepForm.register([ 185 | form({ first_name: '', last_name: '' }).rules({ first_name: 'required', last_name: 'required' }), 186 | form({ email: '', phone_number: '' }).rules({ email: 'required|email', phone: 'required|phone' }), 187 | form({ password: null, password_confirmation: '' }).rules({ password: 'required|min:7|confirmed|string '}), 188 | form({ terms_of_services: null }).rules({ terms_of_services: 'required|accepted' }) 189 | ]); 190 | 191 | MultiStepForm.validate().errors().list(); 192 | ``` 193 | 194 | ``` 195 | Output: 196 | [ 197 | 'First name is a required field', 198 | 'Last name is a required field', 199 | 'Email is a required field', 200 | 'Email must be an email', 201 | 'Phone number is a required field', 202 | 'Phone number must be a valid phone number', 203 | 'Password is a required field', 204 | 'Password must be at least 7 characters', 205 | 'Password must have the same value as the password confirmation field', 206 | 'Password must be a string', 207 | 'Terms Of Service is a required field', 208 | 'Terms Of Service has not been accepted, please accept to continue', 209 | ] 210 | ``` 211 | ### Macro Capable Api List 212 | - [Form Api Macros](#form-api-macros) 213 | - [Validator Api Macros](#validator-api-macros) 214 | - [Error Message Api Macros](#error-message-api-macros) 215 | 216 | #### Form Api Macros 217 | - form().macro(name, fn) 218 | - form().localMacro(name, fn) 219 | - form().forceMacro(name, fn) 220 | - form().forceLocalMacro(name, fn) 221 | 222 | #### Validator Api Macros 223 | - form().validator().macro(name, fn) 224 | - form().validator().localMacro(name, fn) 225 | - form().validator().forceMacro(name, fn) 226 | - form().validator().forceLocalMacro(name, fn) 227 | 228 | #### Error Message Api Macros 229 | - form().errors().macro(name, fn) 230 | - form().errors().localMacro(name, fn) 231 | - form().errors().forceMacro(name, fn) 232 | - form().errors().forceLocalMacro(name, fn) 233 | -------------------------------------------------------------------------------- /bundler/markdown.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | const markdown = { 5 | make: (...lines) => { 6 | return [ 7 | '\n', 8 | lines.reduce((created, line) => [ 9 | ...created, 10 | line(markdown) 11 | ], []).join('\n'), 12 | '\n', 13 | ]; 14 | }, 15 | 16 | url: (text, url) => `[${text}](${url})`, 17 | link: (text, link) => `[${text}](#${link})`, 18 | 19 | li: text => `- ${text}`, 20 | list: list => list.map(item => `- ${item}`).join('\n'), 21 | ul: { 22 | li: text => `- ${text}`, 23 | url: (text, url) => markdown.url(text, url), 24 | link: (text, link) => markdown.link(text, link), 25 | }, 26 | 27 | table: ({ headers, rows }) => { 28 | const make = items => ['|', items, '|']; 29 | return [ 30 | make(headers.join('|')), 31 | make(headers.map(header => '---').join('|')), 32 | ...rows.reduce((rows, row) => [ ...rows, make(row.join('|')) ], []), 33 | ] 34 | .map(row => row.join('')) 35 | .join('\n'); 36 | }, 37 | hr: () => '---', 38 | br: () => `\n`, 39 | h1: text => `# ${text}`, 40 | h2: text => `## ${text}`, 41 | h3: text => `### ${text}`, 42 | h4: text => `#### ${text}`, 43 | h5: text => `##### ${text}`, 44 | 45 | pre: text => '```' + text + '```', 46 | bold: text => `**${text}**`, 47 | italic: text => `_${text}_`, 48 | quote: text => `> ${text}`, 49 | 50 | inline: text => "`" + text + "`", 51 | block: text => "```" + text + "```", 52 | js: text => "```javascript" + text + "```", 53 | py: text => "```python" + text + "```", 54 | bash: text => "```bash" + text + "```", 55 | youtube: ({ id, width = '240', height = '240', border = '10', alt = 'Tutorial', target = "_blank" }) => ` 56 | 57 | ${alt} 58 | 59 | `, 60 | }; 61 | 62 | module.exports = markdown; 63 | module.exports.default = markdown; 64 | module.exports.markdown = markdown; 65 | -------------------------------------------------------------------------------- /bundler/purpose.md: -------------------------------------------------------------------------------- 1 | 2 | ## waving that vuejs form introduction & purpose title That Vue Form 3 | 4 | ![Vue Form AKA Vuejs Form Mission Statement For Building Vue Validation & Javascript Validated Forms](https://github.com/zhorton34/vuejs-form/raw/master/bundler/vuejs-form-purpose-statement.png "Vue JS Form Package Purpose Journal Text") 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /bundler/security_vulnerabilities.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Security Vulnerabilities 4 | 5 | --- 6 | 7 | If you discover a security vulnerability within Clean Code Studio Packages Or Specifically within vuejs-form, please 8 | send an e-mail to Zachary Horton via zak@cleancode.studio. All security vulnerabilities will be promptly addressed. 9 | -------------------------------------------------------------------------------- /bundler/setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Setup 4 | 5 | --- 6 | 7 | ### Npm Setup 8 | ``` 9 | npm install vuejs-form --save-dev 10 | ``` 11 | 12 | ``` 13 | import from from 'vuejs-form'; // Or const form = require('vuejs-form'); 14 | 15 | let RegisterForm = form({ 16 | email: '', 17 | password: '', 18 | password_confirmation: '' 19 | }) 20 | .rules({ 21 | email: ['required', 'email', 'min:5'], 22 | password: ['min:5', 'required', 'confirmed'], 23 | }) 24 | .messages({ 25 | 'email.email': 'Email must be an email', 26 | 'email.required': 'Email is a required field', 27 | 'email.min': 'Email is less than :min characters', 28 | 29 | 'password.required': 'Password is required', 30 | 'password.confirmed': 'Password value must match password confirmation value', 31 | 'password.min': 'Password is not secure enough (Must be longer than :min characters)', 32 | }) 33 | .validate(); 34 | ``` 35 | 36 | ### CDN Setup 37 | 38 | ``` 39 | 40 | 41 | 62 | ``` 63 | -------------------------------------------------------------------------------- /bundler/utilization.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Utilization 4 | 5 | --- 6 | 7 | ```js 8 | import form from 'vuejs-form' 9 | 10 | const LoginForm = form({ 11 | name: '', 12 | email: '', 13 | password: '', 14 | }) 15 | 16 | LoginForm.name // '' 17 | LoginForm.name = 'sarah' 18 | LoginForm.name // 'sarah' 19 | 20 | form({ 21 | name: '', 22 | email: '', 23 | password: '', 24 | }).all() // { name: 'sarah', email: '', password: '' } 25 | form({ 26 | name: '', 27 | email: '', 28 | password: '', 29 | }).has('email', 'password') // true 30 | form({ 31 | name: '', 32 | email: '', 33 | password: '', 34 | }).has('email', 'something') // false 35 | form({ 36 | name: '', 37 | email: '', 38 | password: '', 39 | }).hasAny('email', 'something') // true 40 | form({ 41 | name: '', 42 | email: '', 43 | password: '', 44 | }).empty('email') // true 45 | form({ 46 | name: '', 47 | email: '', 48 | password: '', 49 | }).filled('email') // false 50 | form({ 51 | name: '', 52 | email: '', 53 | password: '', 54 | }).filled('name') // true 55 | form({ 56 | name: '', 57 | email: '', 58 | password: '', 59 | }).boolean('email') // false 60 | form({ 61 | name: '', 62 | email: '', 63 | password: '', 64 | }).only('email', 'name') // { email: '', name: '', } 65 | form({ 66 | name: '', 67 | email: '', 68 | password: '', 69 | }).except('password') // { email: '', name: '' } 70 | form({ 71 | name: '', 72 | email: '', 73 | password: '', 74 | }).input('password') // '' 75 | form({ 76 | name: '', 77 | email: '', 78 | password: '', 79 | }).input('email', 'example@gmail.com') // 'example@gmail.com' 80 | 81 | LoginForm.fill({ 82 | name: 'tim', 83 | email: 'tim@gmail.com', 84 | password: 'secret' 85 | }) 86 | 87 | LoginForm.all() // { name: 'sarah', email: 'tim@gmail.com', password: 'secret' } 88 | 89 | LoginForm.set({ 90 | name: 'jamie', 91 | email: 'jamie@gmail.com', 92 | password: 'password' 93 | }) 94 | 95 | LoginForm.all() // { name: 'jamie', email: 'tim@gmail.com', password: 'secret' } 96 | 97 | LoginForm.keys() // ['name', 'email', 'password'] 98 | 99 | LoginForm.missing('verified') // true 100 | LoginForm.missing('email') // false 101 | 102 | LoginForm.toArray() 103 | /** 104 | [ 105 | { key: 'name', value: 'jamie' }, 106 | { key: 'email', value: 'tim@gmail.com' }, 107 | { key: 'password', value: 'secret' } 108 | ] 109 | */ 110 | 111 | LoginForm.wrap('data') 112 | /** 113 | { 114 | data: { 115 | name: 'jamie', 116 | email: 'tim@gmail.com', 117 | password: 'secret' 118 | } 119 | } 120 | */ 121 | 122 | LoginForm.forget('password', 'email') 123 | LoginForm.all() // { name: 'jamie' } 124 | 125 | /** 126 | * When dealing with HTML elements like checkboxes, your application may receive "truthy" values that are actually strings. For example, "true" or "on". For convenience, you may use the boolean method to retrieve these values as booleans. The boolean method returns true for 1, "1", true, "true", "on", and "yes". All other values will return false: 127 | * Boolean checks for 128 | */ 129 | LoginForm.boolean('name') // false 130 | 131 | 132 | LoginForm.terms = true 133 | LoginForm.boolean('terms') // true 134 | LoginForm.terms = 'true' 135 | LoginForm.boolean('terms') // true 136 | LoginForm.terms = 'yes' 137 | LoginForm.boolean('terms') // true 138 | LoginForm.terms = 'on' 139 | LoginForm.boolean('terms') // true 140 | LoginForm.terms = "1" 141 | LoginForm.boolean('terms') // true 142 | LoginForm.terms = 1 143 | LoginForm.boolean('terms') // true 144 | 145 | /** Anything else will return false Ex: */ 146 | LoginForm.terms = 'asdfsdf' 147 | LoginForm.boolean('terms') // false 148 | ``` 149 | 150 | #### Extend Form Functionality 151 | ```js 152 | import form from 'vuejs-form' 153 | 154 | form().macro('count', () => { 155 | return this.keys().length 156 | }) 157 | 158 | form().macro('mapInto', into => { 159 | // NOTICE: this.data is where the input object is actually stored 160 | 161 | this.data = Object.entries(this.data).reduce((input, [key, value]) => ({ 162 | ...input, 163 | ...into(key, value) 164 | }), 165 | {}); 166 | 167 | return this 168 | }) 169 | 170 | 171 | 172 | const extendedForm = form({ 173 | email: 'example@gmail', 174 | password: 'secret', 175 | }) 176 | 177 | form().macro((key, value) => ({ [key]: value.split('@') })).all() 178 | /** 179 | * { email: ['example', 'gmail'], password: 'secret' } 180 | */ 181 | ``` 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /bundler/validator.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Validator Api 4 | 5 | --- 6 | 7 | - [form.rules({...})](#form-register-rules) 8 | - [form.messages({...})](#form-customize-error-messages) 9 | - [form.validator(...)](#form-validator-instance) 10 | - [form.validate(...)](#validate-form-data) 11 | - [form.hasValidator()](#form-has-validator) 12 | - [form.setValidator({...})](#form-set-rules) 13 | 14 | ### Form Register Rules 15 | > [@SeeAvailableValidationRules](#rules-api) 16 | ```js 17 | form(data).rules({ 18 | name: 'required|min:4', 19 | link: 'required|url', 20 | category: 'required|within:blue,reg,green' 21 | }); 22 | ``` 23 | 24 | > 25 | > Optionally Use Arrays Syntax Instead Of Pipes 26 | > 27 | 28 | ```js 29 | form(data).rules({ 30 | name: ['required', 'min:4'], 31 | link: ['required', 'url'], 32 | category: ['required', 'within:blue,reg,green'] 33 | }); 34 | ``` 35 | 36 | ### Form Customize Error Messages 37 | - All rules have global default error messages shown when rule fails validation. 38 | - Optionally, you are able to override the global defaults rule messages 39 | - Simply use the form(data).rules(set)`.messages({ '{field}.{rule}': 'custom message for failing rule on field' });` 40 | ```js 41 | let data = { email: ['required', 'email'] } 42 | form({ email: 'chad'}).rules({ 43 | email: ['required', 'email'] 44 | }) 45 | .messages({ 46 | '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)' 47 | }) 48 | ``` 49 | ### Form Validator Instance 50 | - Get [Validator Instance](https://github.com/zhorton34/vuejs-validators.js) 51 | ```js 52 | form(data).rules(options).messages(customMessages); 53 | 54 | // form.validator().addMessage(field, error) 55 | // form.validator().addRule(field, rules) 56 | // etc... 57 | ``` 58 | 59 | ### Validate Form Data 60 | - Check current form data against associated form rules 61 | - IMPORTANT: form MUST call validate() method before retrieving current errors 62 | 63 | _COMMON GOTCHA!!!!_ 64 | - This wont get the current form errors 65 | - The `form.validate()` method was Never called 66 | ```js 67 | let data = { name: '' }; 68 | let rules = { name: 'required' }; 69 | 70 | form(data).rules(rules).errors().list(); 71 | 72 | // -------------------------------------------- 73 | // Form SHOULD fail, but errors list is empty 74 | // -------------------------------------------- 75 | // Output: [] 76 | // -------------------------------------------- 77 | ``` 78 | 79 | > _What's the reason?_ 80 | > 81 | > Retrieving errors before validating form data 82 | > 83 | > would retrieve our error messages Api instance, 84 | > but it hasn't been filled with our forms error messages. 85 | > 86 | > form.validate() compares form data against form rules, populating our form errors with failing rule messages. 87 | > 88 | 89 | **Validate THEN resolve the errors (Using forms fluent api)** 90 | ```js 91 | let data = { name: '' }; 92 | let rules = { name: 'required' }; 93 | 94 | form(data).rules(rules).validate().errors().list(); 95 | // Output: ['Name field is required'] 96 | 97 | // Again, we'll need to validate before retrieving our 98 | // errors to validate that the values passes our given rules 99 | form.name = 'hello world'; 100 | 101 | form.errors().list(); 102 | // Output: ['Name field is required'] 103 | 104 | form.validate().errors().list(); 105 | // Output: []; 106 | ``` 107 | > 108 | > 109 | > Fluently call validate() before calling errors() is simple and to the point. 110 | > 111 | > At first, this may seem like a tedious extra step. Many may wonder why we don't simply auto-validate the data? 112 | 113 | Reason for `form.validate().errors()` Instead of simply `form.errors()` triggering the validation. 114 | - Reactive frameworks may use `errors()` and the associated Error Messages Api ([@See Form Error Messages Api](#form-error-messages-api)) 115 | - Without providing the option for the end developer to determine when the form validates 116 | - Async requests, only validate once we've resolved some given data 117 | - Immediate display of errors (Not always wanted) 118 | - Option Open To Immediately show error messages ([@See Vue Watcher Example](#vue-example-two)) 119 | - Some other developers may only want to validate data on form submission 120 | - Many validation rules can be abstracted using the form Api to simply disable the ability to submit a button 121 | - EX: `` 122 | - Then within `submit() method` simply run `if (this.form.validate().errors().any()) return;` 123 | - 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 124 | - etc... 125 | 126 | ### Form Has Validator 127 | Determine if form has a validator instance attached to it 128 | ```js 129 | form.hasValidator(); // true or false 130 | ``` 131 | 132 | ### Form Set Validator 133 | - Set Validator Instance 134 | - Optionally import the validator instance itself, and extend 135 | its functionality validator().macro(add_method, method). 136 | - Then use form macros to track the current step form.macro(add_method, method). 137 | - 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 138 | 139 | ```js 140 | const { form, validator } = require('vuejs-form'); 141 | 142 | 143 | form().macro('setSwitchableValidators', (first, second) => { 144 | this.toggleValidators = 145 | this.toggleBetween = first 146 | }); 147 | 148 | ``` 149 | ## Rules Api 150 | - [accepted](#accepted-rule) 151 | - [date](#date-rule) 152 | - [date_equals](#date-equals-rule) 153 | - [before (date)](#before-rule) 154 | - [before_or_equal (date)](#before-or-equal-rule) 155 | - [after (date)](#after-rule) 156 | - [after_or_equal (date)](#after-or-equal-rule) 157 | - [greater_than (numeric)](#greater-than-rule) 158 | - [gte (Greater than or equal numeric)](#gte-rule) 159 | - [less_than (numeric)](#less-then-rule) 160 | - [lte (Less than or equal numeric)](#lte-rule) 161 | - [alpha](#alpha-rule) 162 | - [alpha_dash](#alpha_dash-rule) 163 | - [alpha_num](#alpha_num-rule) 164 | - [array](#array-rule) 165 | - [between](#between-rule) 166 | - [boolean](#boolean-rule) 167 | - [confirmed](#confirmed-rule) 168 | - [different](#different-rule) 169 | - [digits](#digits-rule) 170 | - [digits_between](#digits_between-rule) 171 | - [distinct](#distinct-rule) 172 | - [email](#email-rule) 173 | - [ends_with](#ends_with-rule) 174 | - [integer](#integer-rule) 175 | - [ip](#ip-rule) 176 | - [ipv4](#ipv4-rule) 177 | - [ipv6](#ipv6-rule) 178 | - [json](#json-rule) 179 | - [max](#max-rule) 180 | - [min](#min-rule) 181 | - [not_regex](#not_regex-rule) 182 | - [not_within](#not_within-rule) 183 | - [number](#number-rule) 184 | - [numeric](#numeric-rule) 185 | - [phone](#phone-rule) 186 | - [regex](#regex-rule) 187 | - [required](#required-rule) 188 | - [same](#same-rule) 189 | - [starts_with](#starts_with-rule) 190 | - [string](#string-rule) 191 | - [url](#url-rule) 192 | - [within](#within-rule) 193 | 194 | 195 | 196 | ### Date Equals Rule 197 | (Date) 198 | The field under validation must be the same date as the rules date 199 | 200 | > Passes Date Equals Rule 201 | ```js 202 | let form = { 203 | one: '4-22-1997', 204 | two: 'April 22 2025' 205 | } 206 | 207 | let rules = { 208 | one: 'date_equals:4-22-1997', 209 | two: 'date_equals:April 22 2025', 210 | } 211 | ``` 212 | 213 | > Fails Date Equals Rule 214 | ```js 215 | let form = { 216 | one: '4-22-1997', 217 | two: '2-12-1997' 218 | } 219 | 220 | let rules = { 221 | one: 'date_equals:4-24-1998', 222 | two: 'date_equals:1-11-1996', 223 | } 224 | ``` 225 | 226 | ### Before Rule 227 | (Date) 228 | 229 | The Field under evaluation must be before the compared date 230 | 231 | > Passes Before (Date) Rule 232 | ``` 233 | let form = { 234 | one: '4-22-1997', 235 | two: '2-12-1997' 236 | } 237 | 238 | let rules = { 239 | one: 'before:4-22-1998', 240 | two: 'before:2-12-1997', 241 | } 242 | ``` 243 | 244 | > Fails Before (Date) Rule 245 | ```js 246 | let form = { 247 | one: '4-22-1997', 248 | two: '3-12-1997' 249 | } 250 | 251 | let rules = { 252 | one: 'before:4-22-1997', 253 | two: 'before:2-3-1996', 254 | } 255 | ``` 256 | 257 | ### Before Or Equal Rule 258 | (Date) 259 | The field under validation must be before or equal to the compared date. 260 | 261 | > Passes Before Or Equal (Date) Rule 262 | ```js 263 | let form = { 264 | one: '4-22-1997', 265 | two: '2-12-1997' 266 | } 267 | 268 | let rules = { 269 | one: 'before_or_equal:3-21-1998', 270 | two: 'before_or_equal:2-12-1997', 271 | } 272 | ``` 273 | 274 | > Fails Before Or Equal (Date) Rule 275 | ```js 276 | let form = { 277 | one: '4-22-1997', 278 | two: '2-3-1997' 279 | } 280 | 281 | let rules = { 282 | one: 'before_or_equal:4-23-1997', 283 | two: 'before_or_equal:2-3-1996', 284 | } 285 | ``` 286 | 287 | ### After Rule 288 | (Date) 289 | 290 | The Field under evaluation must be after the compared date 291 | 292 | > Passes After (Date) Rule 293 | ```js 294 | let form = { 295 | one: '4-22-1997', 296 | two: '2-2-1997' 297 | } 298 | 299 | let rules = { 300 | one: 'after:4-23-1997', 301 | two: 'after:2-3-1996', 302 | } 303 | ``` 304 | 305 | ### Date Rule 306 | (Date) 307 | The field under validation must be a valid, non-relative date according to the new Date js constructor. 308 | 309 | > Passes Date Rule 310 | - 4.22.1997 311 | - 4-22-1997 312 | - 4/22/1997 313 | - April 22 1997 314 | - Tuesday April 22 1997 315 | 316 | > Fails Date Rule 317 | - asdfweadf 318 | - 23423423 319 | - [] 320 | 321 | > Fails After (Date) Rule 322 | ```js 323 | let form = { 324 | one: '4-22-1997', 325 | two: '2-12-1997' 326 | } 327 | 328 | let rules = { 329 | one: 'after:4-22-1998', 330 | two: 'after:1-11-1996', 331 | } 332 | ``` 333 | 334 | 335 | ### After Or Equal Rule 336 | (Date) 337 | The field under validation must be after or equal to the compared date. 338 | 339 | > Passes After Or Equal (Date) Rule 340 | ```js 341 | let form = { 342 | one: '4-22-1997', 343 | two: '1-11-2013', 344 | } 345 | 346 | let rules = { 347 | one: 'after_or_equal:4-22-1997', 348 | two: 'after_or_equal:2-12-2014', 349 | } 350 | ``` 351 | 352 | > Fails After Or Equal (Date) Rule 353 | ```js 354 | let form = { 355 | one: '4-22-1997', 356 | two: '2-12-1997' 357 | } 358 | 359 | let rules = { 360 | one: 'after_or_equal:4-23-1997', 361 | two: 'after_or_equal:2-3-1996', 362 | } 363 | ``` 364 | 365 | 366 | ### Accepted Rule 367 | 368 | The field under form must be yes, on, 1, or true. This is useful for validating "Terms of Service" acceptance. 369 | 370 | > Passing Accepted Rule 371 | ```js 372 | let data = { terms_of_service: 'no' }; 373 | let rules = { terms_of_service: 'accepted' }; 374 | 375 | // false 376 | form(data).rules(rules).validate().errors().has('terms_of_service'); 377 | ``` 378 | 379 | > Failing Accepted Rule 380 | ```js 381 | let data = { terms_of_service: null } 382 | let rules = { terms_of_service: 'accepted' } 383 | 384 | // true 385 | form(data).rules(rules).validate().errors().has('terms_of_services'); 386 | ``` 387 | 388 | 389 | ### Alpha Rule 390 | The field under form must be entirely alphabetic characters. 391 | 392 | > Passing Alpha Rule 393 | ```js 394 | let data = { letters: 'asdeddadfjkkdjfasdf' }; 395 | let rules = { letters: ['alpha'] }; 396 | 397 | // false 398 | form(data).rules(rules).validate().errors().has('letters'); 399 | ``` 400 | 401 | > Failing Alpha Rule 402 | ```js 403 | let data = { letters: '5-@'} 404 | let rules = { letters: ['alpha'] } 405 | 406 | // true 407 | form(data).rules(rules).validate().errors().has('letters'); 408 | ``` 409 | 410 | ### Alpha Dash Rule 411 | The field under form may have alpha-numeric characters, as well as dashes and underscores. 412 | 413 | > Passing Alpha Dash Rule 414 | 415 | ```js 416 | let data = { slug: 'user_name' }; 417 | let rules = { slug: ['alpha_dash'] }; 418 | 419 | // false 420 | form(data).rules(rules).validate().errors().has('slug'); 421 | ``` 422 | 423 | > Failing Alpha Dash Rule 424 | ```js 425 | let data = { words: 'hello world'} 426 | let rules = { words: ['alpha_dash'] } 427 | 428 | // true 429 | form(data).rules(rules).validate().errors().has('words'); 430 | ``` 431 | 432 | ### Alpha Num Rule 433 | The field under form must be entirely alpha-numeric characters. 434 | 435 | > Passing Alpha Num Rule 436 | 437 | ```js 438 | let data = { key: '4asdasdfe4d23545w634adf' }; 439 | let rules = { key: ['alpha_num'] }; 440 | 441 | // false 442 | form(data).rules(rules).validate().errors().any(); 443 | ``` 444 | 445 | > Failing Alpha Num Rule 446 | ```js 447 | let data = { identifier: '1-asdf4adf_d_42'} 448 | let rules = { identifier: ['alpha_num'] } 449 | 450 | // true 451 | form(data).rules(rules).validate().errors().any(); 452 | ``` 453 | 454 | ### Array Rule 455 | The field under form must be a JS array. 456 | 457 | > Passing Array Rule 458 | ```js 459 | let data = { list: ['banana', 'broccoli', 'carrot'] }; 460 | let rules = { list: 'array' }; 461 | 462 | // false 463 | form(data).rules(rules).validate().errors().any(); 464 | ``` 465 | 466 | > Failing Array Rule 467 | ```js 468 | let data = { options: { name: 'hey world' } } 469 | let rules = { options: 'array' } 470 | 471 | // true 472 | form(data).rules(rules).validate().errors().any(); 473 | ``` 474 | 475 | 476 | ### Email Rule 477 | The given field value must be an email 478 | 479 | > Passing Email Rule 480 | ```js 481 | let data = { email: 'example@cleancode.studio' }; 482 | let rules = { email: ['email'] }; 483 | 484 | // false 485 | form(data).rules(rules).validate().errors().any(); 486 | ``` 487 | 488 | > Failing Email Rule 489 | ```js 490 | let data = { email: 'asdfsdaf@.net'} 491 | let rules = { email: ['email'] } 492 | 493 | // true 494 | form(data).rules(rules).validate().errors().any(); 495 | ``` 496 | 497 | 498 | ### Boolean Rule 499 | - Boolish form, not strict boolean check 500 | - Validates that field value is "truthy" or "falsy" 501 | 502 | |**Truthy**|**Falsy**| 503 | |---|---| 504 | |1|0| 505 | |"1"|"0"| 506 | |"on"|"off"| 507 | |"On"|"No"| 508 | |"ON"|"OFF"| 509 | |"yes"|"no"| 510 | |"Yes"|"Off"| 511 | |"YES"|"NO"| 512 | |true|false| 513 | |"true"|"false"| 514 | |"True"|"False"| 515 | |"TRUE"|"FALSE"| 516 | 517 | > Passing Boolean Rule 518 | ```js 519 | let data = { selected: 'Yes' }; 520 | let rules = { selected: ['boolean'] }; 521 | 522 | // false 523 | form(data).rules(rules).validate().errors().any(); 524 | ``` 525 | 526 | > Failing Boolean Rule 527 | ```js 528 | form = { selected: null }; 529 | rules = { selected: ['boolean'] }; 530 | 531 | // true 532 | form(data).rules(rules).validate().errors().any(); 533 | ``` 534 | 535 | 536 | ### Confirmed form Rule 537 | - `{field}` value must match `{field}_confirmation` value 538 | - Example `password` must match `password_confirmation` value to pass `confirmed` ruled 539 | > Passing Confirmed Rule 540 | ```js bash 541 | let data = { password: 'secret', password_confirmation: 'secret' } 542 | let rules = { password: 'confirmed' } 543 | 544 | // false 545 | form(data).rules(rules).validate().errors().any(); 546 | ``` 547 | 548 | > Failing Confirmed Rule 549 | ```js bash 550 | let data = { password: 'secret' }; 551 | let rules = { password: 'confirmed' }; 552 | 553 | // true 554 | form(data).rules(rules).validate().errors().any(); 555 | form.password_confirmation = 'something_something'; 556 | 557 | // true 558 | form.validate().errors().any(); 559 | ``` 560 | 561 | > Passing Confirmed Rule Again 562 | ```js bash 563 | form.password_confirmation = 'secret'; 564 | 565 | // false 566 | form.validate().errors().any(); 567 | ``` 568 | 569 | ### Greater Than Rule 570 | (Numeric) 571 | 572 | Number must be greater than compared value 573 | 574 | > Passing greater than rule 575 | ```js 576 | 577 | let form = { 578 | age: 24, 579 | members: 19, 580 | percentage: 0.4, 581 | }; 582 | 583 | let rules = { 584 | age: 'greater_than:13', 585 | members: 'greater_than:10', 586 | percentage: 'greater_than:0.35', 587 | }; 588 | ``` 589 | 590 | > Failing greater than rule 591 | ```js 592 | let form = { 593 | age: 24, 594 | members: 19, 595 | percentage: 0.4, 596 | }; 597 | 598 | let rules = { 599 | age: 'greater_than:24', 600 | members: 'greater_than:100', 601 | percentage: 'greater_than:0.9', 602 | }; 603 | ``` 604 | 605 | ### Gte Rule 606 | (Greater Than Or Equal - Numeric) 607 | Number must be greater than or equal to compared value 608 | 609 | > Passing greater than or equal rule (gte) 610 | ```js 611 | 612 | let form = { 613 | age: 24, 614 | members: 19, 615 | percentage: 0.4, 616 | }; 617 | 618 | let rules = { 619 | age: 'gte:24', 620 | members: 'gte:10', 621 | percentage: 'gte:0.35', 622 | }; 623 | ``` 624 | 625 | > Failing greater than or equal rule (gte) 626 | ```js 627 | 628 | let form = { 629 | age: 24, 630 | members: 19, 631 | percentage: 0.4, 632 | }; 633 | 634 | let rules = { 635 | age: 'greater_than:25', 636 | members: 'greater_than:100', 637 | percentage: 'greater_than:0.9', 638 | }; 639 | ``` 640 | 641 | ### Less Than Rule 642 | (Numeric) 643 | 644 | Number must be less than compared value 645 | 646 | > Passing less than rule 647 | ```js 648 | 649 | let form = { 650 | age: 24, 651 | members: 19, 652 | percentage: 0.4, 653 | } ; 654 | 655 | let rules = { 656 | age: 'less_than:25', 657 | members: 'less_than:20', 658 | percentage: 'less_than:0.8', 659 | } 660 | ``` 661 | 662 | > Failing less than rule 663 | ```js 664 | let form = { 665 | age: 24, 666 | members: 19, 667 | percentage: 0.4, 668 | }; 669 | 670 | let rules = { 671 | age: 'less_than:24', 672 | members: 'less_than:10', 673 | percentage: 'less_than:0.1', 674 | } 675 | ``` 676 | 677 | 678 | ### Lte Rule 679 | (Less than or equal - Numeric) 680 | 681 | Number must be less than or equal to compared value 682 | 683 | > Passing Less than or equal (lte) rule 684 | ```js 685 | 686 | let form = { 687 | age: 24, 688 | members: 19, 689 | percentage: 0.4, 690 | } ; 691 | 692 | let rules = { 693 | age: 'lte:24', 694 | members: 'lte:20', 695 | percentage: 'lte:0.8', 696 | } 697 | ``` 698 | 699 | > Failing less than or equal (lte) rule 700 | ```js 701 | let form = { 702 | age: 24, 703 | members: 19, 704 | percentage: 0.4, 705 | }; 706 | 707 | let rules = { 708 | age: 'less_than:24', 709 | members: 'less_than:10', 710 | percentage: 'less_than:0.5', 711 | } 712 | ``` 713 | 714 | ### Different form Rule 715 | The given field value is different than another field value 716 | 717 | > Passing Different Rule 718 | ```js bash 719 | let data = { password: 'asdfasdfasdf', confirm_password: 'secret' }; 720 | let rules = { password: 'different:confirm_password' }; 721 | 722 | form(data).rules(rules).validate().errors().any(); 723 | ``` 724 | 725 | > Failing Different Rule 726 | ```js bash 727 | let data = { password: 'secret', confirm_password: 'secret' } 728 | let rules = { password: 'different:confirm_password' } 729 | 730 | form(data).rules(rules).validate().errors().any(); 731 | ``` 732 | 733 | 734 | ### Digits Rule 735 | The field under form must be numeric and must have an exact length of value. 736 | 737 | > Passing Digits Rule 738 | ```js 739 | let data = { amount: '10000' } 740 | let rules = { amount: 'digits:6' } 741 | 742 | form(data).rules(rules).validate().errors().any(); 743 | ``` 744 | 745 | > Failing Digits Rule 746 | ```js 747 | let data = { amount: '10000' } 748 | let rules = { amount: 'digits:4' } 749 | 750 | form(data).rules(rules).validate().errors().any(); 751 | ``` 752 | 753 | ### Digits Between Rule 754 | The field under form must be numeric and have a length between the lower and upper limit defined. 755 | 756 | > Passing Digits Between Rule 757 | ```js 758 | let data = { amount: '10000' } 759 | let rules = { amount: 'digits_between:4,6' } 760 | 761 | form(data).rules(rules).validate().errors().any(); 762 | ``` 763 | 764 | > Failing Digits Between Rule 765 | ```js 766 | let data = { amount: '10000' } 767 | let rules = { amount: 'digits_between:3,5' } 768 | 769 | form(data).rules(rules).validate().errors().any(); 770 | ``` 771 | 772 | ### Distinct Rule 773 | The field under form must be an array with no duplicate values. 774 | 775 | > Passing Distinct Rule 776 | ```js 777 | let data = { shopping_list: ['ham', 'eggs', 'milk', 'turkey'] } 778 | let rules = { shopping_list: 'distinct' } 779 | 780 | form(data).rules(rules).validate().errors().any(); 781 | ``` 782 | 783 | > Failing Distinct Rule 784 | ```js 785 | 786 | let data = { shopping_list: ['ham', 'ham', 'eggs', 'milk', 'turkey'] } 787 | let rules = { shopping_list: 'distinct' } 788 | 789 | form(data).rules(rules).validate().errors().any(); 790 | ``` 791 | 792 | ### Email Rule 793 | The given field value must be an email 794 | 795 | > Passing Email Rule 796 | ```js 797 | let data = { email: 'example@cleancode.studio' }; 798 | let rules = { email: ['email'] }; 799 | 800 | form(data).rules(rules).validate().errors().any(); 801 | ``` 802 | 803 | > Failing Email Rule 804 | ```js 805 | let data = { email: 'asdfsdaf@.net'} 806 | let rules = { email: ['email'] } 807 | 808 | form(data).rules(rules).validate().errors().any(); 809 | ``` 810 | 811 | ### Ends With Rule 812 | The field under form must end with one of the given values. 813 | 814 | > Passing Ends With Rule 815 | ```js 816 | let data = { name: 'sammie' }; 817 | let rules = { name: 'ends_with:sl,ie,asx' }; 818 | 819 | form(data).rules(rules).validate().errors().any(); 820 | ``` 821 | 822 | > Failing Ends With Rule 823 | ```js 824 | let data = { name: 5 }; 825 | let rules = { name: 'ends_with:sl,ie,asx' }; 826 | 827 | form(data).rules(rules).validate().errors().any(); 828 | 829 | form.setData({ name: 'azure' }).setRules({ name: 'ends_with:sl,ie,asx' }) 830 | 831 | form.validate().errors().any(); 832 | ``` 833 | 834 | 835 | ### Integer Rule 836 | 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. 837 | 838 | > Passing Integer Rule 839 | ```js 840 | let data = { students: 25 } 841 | let rules = { students: ['integer'] } 842 | 843 | form(data).rules(rules).validate().errors().any(); 844 | ``` 845 | 846 | > Failing Integer Rule 847 | ```js 848 | let data = { students: 'yes' } 849 | let rules = { students: ['integer'] } 850 | 851 | form(data).rules(rules).validate().errors().any(); 852 | ``` 853 | 854 | ### IP Rule 855 | This form rule confirms that value is an IP address. 856 | 857 | > Passing IP Rule 858 | - "115.42.150.37" 859 | - "192.168.0.1" 860 | - "110.234.52.124" 861 | - "2001:0db8:85a3:0000:0000:8a2e:0370:7334" (Ipv6) 862 | 863 | > Failing IP Rule 864 | - "210.110" – must have 4 octets 865 | - "255" – must have 4 octets 866 | - "y.y.y.y" – the only digit has allowed 867 | - "255.0.0.y" – the only digit has allowed 868 | - "666.10.10.20" – digit must between [0-255] 869 | - "4444.11.11.11" – digit must between [0-255] 870 | - "33.3333.33.3" – digit must between [0-255] 871 | 872 | 873 | ### IPv4 Rule 874 | This form rule confirms that value is an IPv4 address. 875 | 876 | > Passing IPv4 Rule 877 | - "115.42.150.37" 878 | - "192.168.0.1" 879 | - "110.234.52.124" 880 | 881 | > Failing IPv4 Rule 882 | - "210.110" – must have 4 octets 883 | - "255" – must have 4 octets 884 | - "y.y.y.y" – the only digit has allowed 885 | - "255.0.0.y" – the only digit has allowed 886 | - "666.10.10.20" – digit must between [0-255] 887 | - "4444.11.11.11" – digit must between [0-255] 888 | - "33.3333.33.3" – digit must between [0-255] 889 | - "2001:0db8:85a3:0000:0000:8a2e:0370:7334" (Ipv6) 890 | 891 | 892 | ### IPv6 Rule 893 | This form rule confirms that value is an IPv6 address. 894 | 895 | > Passing IPv6 Rule 896 | - "2001:0db8:85a3:0000:0000:8a2e:0370:7334" (Ipv6) 897 | 898 | > Failing IPv6 Rule 899 | - "210.110" – must have 4 octets 900 | - "255" – must have 4 octets 901 | - "y.y.y.y" – the only digit has allowed 902 | - "255.0.0.y" – the only digit has allowed 903 | - "666.10.10.20" – digit must between [0-255] 904 | - "4444.11.11.11" – digit must between [0-255] 905 | - "33.3333.33.3" – digit must between [0-255] 906 | - "110.234.52.124" 907 | - "192.168.0.1" 908 | - "115.42.150.37" 909 | 910 | 911 | ### Json Rule 912 | The given field value must be a Json String 913 | 914 | > Passing Json Rule 915 | ```js 916 | let data = { content: JSON.stringify({ inspire: 'love' }) }; 917 | let rules = { content: 'json' }; 918 | 919 | form(data).rules(rules).validate().errors().any(); 920 | ``` 921 | 922 | > Failing Json Rule 923 | ```js 924 | let data = { content: 'fasdf' } 925 | let rules = { content: 'json' } 926 | 927 | form(data).rules(rules).validate().errors().any(); 928 | ``` 929 | 930 | ### Max Rule 931 | The given field must not be more than the defined maximum limit 932 | 933 | > Passing Max Limit Rule 934 | ```js 935 | let data = { password: 'secret' } 936 | let rules = { password: 'max:10' } 937 | 938 | form(data).rules(rules).validate().errors().any(); 939 | ``` 940 | 941 | > Failing Max Limit Rule 942 | ```js 943 | let data = { password: 'secret'} 944 | let rules = { password: 'max:4' } 945 | 946 | form(data).rules(rules).validate().errors().any(); 947 | ``` 948 | 949 | ### Min Rule 950 | The given field must not be less than the defined minimum limit 951 | 952 | > Passing Min Limit Rule 953 | ```js 954 | let data = { password: 'secret' } 955 | let rules = { password: 'min:6' } 956 | 957 | form(data).rules(rules).validate().errors().any(); 958 | ``` 959 | 960 | > Failing Min Limit Rule 961 | ```js 962 | let data = { password: 'secret'} 963 | let rules = { password: 'min:8' } 964 | 965 | form(data).rules(rules).validate().errors().any(); 966 | ``` 967 | 968 | 969 | ### Not Regex Rule 970 | The given field value must NOT match the regular expression pattern 971 | 972 | > Passing Not Regex Rule 973 | ```js 974 | let data = { email: 'ex.-fn' }; 975 | let rules = { email: ['not_regex:/^.+@.+$/i'] }; 976 | 977 | form(data).rules(rules).validate().errors().any(); 978 | ``` 979 | 980 | > Failing Not Regex Rule 981 | ```js 982 | 983 | 984 | let data = { email: 'example@gmail.com'} 985 | let rules = { email: ['not_regex:/^.+@.+$/i'] } 986 | 987 | form(data).rules(rules).validate().errors().any(); 988 | ``` 989 | 990 | 991 | ### Not Within Rule 992 | The given field must NOT be "within" the comma delimited list of items 993 | 994 | > Passing Not Within Rule 995 | ```js 996 | let data = { language: 'PigLatin' } 997 | let rules = { language: 'not_within:German,Spanish,English,Latin' } 998 | 999 | form(data).rules(rules).validate().errors().any(); 1000 | ``` 1001 | 1002 | > Failing Not Within Rule 1003 | ```js 1004 | let data = { pencil: '2a'}; 1005 | let rules = { pencil: 'not_within:notebook,pencil,2a,marker,sharpie,whiteboard' }; 1006 | 1007 | form(data).rules(rules).validate().errors().any(); 1008 | ``` 1009 | 1010 | ### Number Rule 1011 | The given field must be a Number (Strict Typed Check). See Numeric For Looser Type Checking 1012 | 1013 | > Passing Number Rule 1014 | ```js 1015 | let data = { id: 15 }; 1016 | let rules = { id: ['number'] }; 1017 | 1018 | form(data).rules(rules).validate().errors().any(); 1019 | ``` 1020 | 1021 | > Failing Number Rule 1022 | ```js 1023 | let data = { id: '15'} 1024 | let rules = { id: ['number'] } 1025 | 1026 | form(data).rules(rules).validate().errors().any(); 1027 | ``` 1028 | 1029 | ### Numeric Rule 1030 | Determine if a value is numeric, or is a string that can properly represent a numeric 1031 | 1032 | - Numerical value, not strict number check 1033 | - Automatically attempts to cast value to numerical value. 1034 | - Validates that field value an integer, decimal, or bigInt. 1035 | 1036 | > Passing Numeric Rule 1037 | ```js 1038 | let data = { members: '25' } 1039 | let rules = { member: ['numeric'] } 1040 | 1041 | form(data).rules(rules).validate().errors().any(); 1042 | ``` 1043 | 1044 | > Failing Numeric Rule 1045 | ```js 1046 | let data = { members: 'yes' } 1047 | let rules = { member: ['numeric'] } 1048 | 1049 | form(data).rules(rules).validate().errors().any(); 1050 | ``` 1051 | 1052 | 1053 | ### Phone Rule 1054 | The given field value must be a phone number 1055 | 1056 | > Passing Phone Rule 1057 | ```js 1058 | let data = { send_sms: ['555-555-5555'] } 1059 | let rules = { send_sms: ['phone'] } 1060 | 1061 | form(data).rules(rules).validate().errors().any(); 1062 | ``` 1063 | 1064 | > Failing Phone Rule 1065 | ```js 1066 | let data = { send_sms: '+(3) - 4 32'} 1067 | let rules = { send_sms: ['phone'] } 1068 | 1069 | form(data).rules(rules).validate().errors().any(); 1070 | ``` 1071 | 1072 | > Phone Number Formats Within Testing Coverage 1073 | - +61 1 2345 6789 1074 | - +61 01 2345 6789 1075 | - 01 2345 6789 1076 | - 01-2345-6789 1077 | - (01) 2345 6789 1078 | - (01) 2345-6789 1079 | - 5555555555 1080 | - (555) 555 5555 1081 | - 555 555 5555 1082 | - +15555555555 1083 | - 555-555-5555 1084 | 1085 | > _(Any contributions welcome (vuejs-validators.js repo) for improving regex form patterns for current rules as well as adding new rules)_ 1086 | 1087 | 1088 | ### Regex Rule 1089 | The given field value must match the regular expression pattern 1090 | 1091 | > Passing Regex Rule 1092 | ```js 1093 | let data = { email: 'example@gmail.com' }; 1094 | let rules = { email: ['regex:/^.+@.+$/i'] }; 1095 | 1096 | form(data).rules(rules).validate().errors().any(); 1097 | ``` 1098 | 1099 | > Failing Regex Rule 1100 | ```js 1101 | let data = { email: 'ex.-fn'} 1102 | let rules = { email: ['regex:/^.+@.+$/i'] } 1103 | 1104 | form(data).rules(rules).validate().errors().any(); 1105 | ``` 1106 | 1107 | 1108 | ### Required Rule 1109 | Validates that a given field exists and its value is set 1110 | 1111 | > Passing Required Rule 1112 | ```js 1113 | let data = { name: 'jules' }; 1114 | let rules = { name: ['required'] }; 1115 | 1116 | form(data).rules(rules).validate().errors().any(); 1117 | ``` 1118 | 1119 | > Failing Required Rule 1120 | ```js 1121 | let data = { name: '' }; 1122 | let rules = { name: ['required'] }; 1123 | 1124 | form(data).rules(rules).validate().errors().any(); 1125 | ``` 1126 | 1127 | 1128 | ### Same form Rule 1129 | The given field value is the same as another field value 1130 | 1131 | > Passing Same Rule 1132 | ```js 1133 | let data = { password: 'secret', confirm_password: 'secret' } 1134 | let rules = { password: 'same:confirm_password' } 1135 | 1136 | form(data).rules(rules).validate().errors().any(); 1137 | ``` 1138 | 1139 | > Failing Same Rule 1140 | ```js bash 1141 | let data = { password: 'asdfasdfasdf', confirm_password: 'secret' }; 1142 | let rules = { password: 'same:confirm_password' }; 1143 | 1144 | form(data).rules(rules).validate().errors().any(); 1145 | ``` 1146 | 1147 | ### Starts With Rule 1148 | The field under form must start with one of the given values. 1149 | 1150 | > Passing Starts With Rule 1151 | ```js 1152 | let data = { name: 'sammie' }; 1153 | let rules = { name: 'starts_with:joe,sam,tom' }; 1154 | 1155 | form(data).rules(rules).validate().errors().any(); 1156 | ``` 1157 | 1158 | > Failing Starts With Rule 1159 | ```js 1160 | let data = { name: 5 }; 1161 | let rules = { name: 'starts_with:sl,ie,asx' }; 1162 | 1163 | form(data).rules(rules).validate().errors().any(); 1164 | 1165 | form.setData({ name: 'azure' }) 1166 | .setRules({ name: 'starts_with:joe,sam,tom'}) 1167 | .validate() 1168 | .errors() 1169 | .any(); 1170 | ``` 1171 | 1172 | 1173 | ### String Rule 1174 | The given field value must be a String 1175 | 1176 | > Passing String Rule 1177 | ```js 1178 | let data = { name: 'sammie' }; 1179 | let rules = { name: 'string' }; 1180 | 1181 | form(data).rules(rules).validate().errors().any(); 1182 | ``` 1183 | 1184 | > Failing String Rule 1185 | ```js 1186 | let data = { name: 54345 } 1187 | let rules = { name: 'string' } 1188 | 1189 | form(data).rules(rules).validate().errors().any(); 1190 | ``` 1191 | 1192 | ### Url Rule 1193 | The given field value must be an http(s) url 1194 | 1195 | > Passing Url Rule 1196 | ```js 1197 | let data = { link: 'https://cleancode.studio' }; 1198 | let rules = { link: 'url' }; 1199 | 1200 | form(data).rules(rules).validate().errors().any(); 1201 | ``` 1202 | 1203 | > Failing Url Rule 1204 | ```js 1205 | let data = { link: 'httP/ope_type@.net'} 1206 | let rules = { link: 'url' } 1207 | 1208 | form(data).rules(rules).validate().errors().any(); 1209 | ``` 1210 | 1211 | 1212 | ### Within Rule 1213 | The given field must be "within" the comma delimited list of items 1214 | 1215 | > Passing Within Rule 1216 | ```js 1217 | let data = { name: 'Sam' } 1218 | let rules = { name: 'within:James,Boronica,Sam,Steve,Lenny' } 1219 | 1220 | form(data).rules(rules).validate().errors().any(); 1221 | ``` 1222 | 1223 | > Failing Within Rule 1224 | ```js 1225 | let data = { name: 'jake'}; 1226 | let rules = { name: 'within:patricia,veronica,samuel,jeviah' }; 1227 | 1228 | form(data).rules(rules).validate().errors().any(); 1229 | ``` 1230 | 1231 | 1232 | ## Form Error Messages Api 1233 | > form.errors() Methods 1234 | - [any()](#any-errors) 1235 | - [all()](#all-errors) 1236 | - [list()](#list-errors) 1237 | - [set(errors)](#set-errors) 1238 | - [forget()](#forget-errors) 1239 | - [has(field)](#has-error) 1240 | - [get(field)](#get-error) 1241 | - [list(field)](#list-error) 1242 | - [add(field, message)](#add-error) 1243 | - [set(field, messages)](#set-field-errors) 1244 | - [forget(field)](#forget-field) 1245 | - [getValidator()](#get-errors-validator) 1246 | 1247 | 1248 | ### Any Errors 1249 | > Determine if there are "any" errors (bool) 1250 | ```js 1251 | let data = { name: '' }; 1252 | let rules = { name: 'required'}; 1253 | form(data).rules(rules).errors().any(); 1254 | ``` 1255 | ``` 1256 | Output: true 1257 | ``` 1258 | 1259 | 1260 | ### All Errors 1261 | > Retrieve all errors within the errors object 1262 | 1263 | ```js 1264 | let data = { name: '', email: '' }; 1265 | let rules = { name: 'required', email: 'email|required' }; 1266 | 1267 | form(data).rules(rules).validate().errors().all(); 1268 | ``` 1269 | ``` 1270 | Output: 1271 | 1272 | { 1273 | name: [ 1274 | 'name field is required' 1275 | ], 1276 | email: [ 1277 | 'email field must be an email address', 1278 | 'email field is required' 1279 | ] 1280 | } 1281 | ``` 1282 | 1283 | 1284 | 1285 | ### List Errors 1286 | > Retrieve all errors within the errors object 1287 | ```js 1288 | let data = { name: '', email: '' }; 1289 | let rules = { name: 'required', email: 'email|required' }; 1290 | 1291 | form(data).rules(rules).validate().errors().list(); 1292 | ``` 1293 | ``` 1294 | Output: 1295 | 1296 | [ 1297 | 'name field is required', 1298 | 'email field must be an email address', 1299 | 'email field is required' 1300 | ] 1301 | ``` 1302 | 1303 | 1304 | 1305 | ### Set Errors 1306 | > Set all errors 1307 | 1308 | ```js 1309 | let data = { name: '' }; 1310 | let rules = { name: 'required' }; 1311 | 1312 | form(data).rules(rules).validate(); 1313 | 1314 | form.errors().list(); 1315 | // Output: ['name is a required field'] 1316 | 1317 | form.errors().set({ notice: ['set this random error message'] }); 1318 | form.errors().list() 1319 | ``` 1320 | ``` 1321 | Output: ['set this random error message'] 1322 | ``` 1323 | 1324 | 1325 | ### Forget Errors 1326 | > Forget errors and reset them to empty 1327 | ```js 1328 | let data = { name: '' }; 1329 | let rules = { name: 'required' }; 1330 | 1331 | form(data).rules(rules).validate().errors().list(); 1332 | // Output: ['Name is a required field'] 1333 | 1334 | form.errors().forget(); 1335 | form.errors().list(); 1336 | ``` 1337 | ``` 1338 | Output: [] 1339 | ``` 1340 | 1341 | ### Has Error 1342 | > Determine if a specific field has error messages 1343 | 1344 | ```js 1345 | let data = { name: '', email: 'example@gmail.com' }; 1346 | let rules = { name: 'required', email: 'email|required' }; 1347 | form(data).rules(rules).validate(); 1348 | 1349 | form.errors().has('name'); 1350 | form.errors().has('email'); 1351 | form.errors().has('something_else'); 1352 | ``` 1353 | ``` 1354 | Output: 1355 | has name: true 1356 | has email: false 1357 | has something_else: false 1358 | ``` 1359 | ### Get Error 1360 | > Get _first_ error message for a specific field 1361 | ```js 1362 | let data = { name: '' }; 1363 | let rules = { name: 'required|min:3'}; 1364 | 1365 | form(data).rules(rules).validate().errors().get('name'); 1366 | ``` 1367 | ``` 1368 | Output: "Name is a required field" 1369 | ``` 1370 | 1371 | ### List Error 1372 | > List errors for a specific field 1373 | ```js 1374 | let data = { name: '' }; 1375 | let rules = { name: 'required|min:3'}; 1376 | 1377 | form(data).rules(rules).validate().errors().list('name'); 1378 | ``` 1379 | ``` 1380 | Output: ['name is a required field', 'name must be longer than 3 characters'] 1381 | ``` 1382 | 1383 | ### Add Error 1384 | > Add error message for a specific field 1385 | ```js 1386 | let data = { name: '' }; 1387 | let rules = { name: 'required|min:3'}; 1388 | 1389 | form(data).rules(rules).validate().add( 1390 | 'name', 'four failures in a row. Two more failures before your locked out' 1391 | ); 1392 | 1393 | form.errors().list('name'); 1394 | ``` 1395 | ``` 1396 | 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'] 1397 | ``` 1398 | 1399 | 1400 | ### Set Error 1401 | > Set error messages for a specific field 1402 | ```js 1403 | let data = { name: '' }; 1404 | let rules = { name: 'required' }; 1405 | 1406 | form(data).rules(rules).validate().list('name'); 1407 | ``` 1408 | ``` 1409 | Output: ['name is a required field'] 1410 | ``` 1411 | ```js 1412 | form.errors().set('name', ['random messages', 'set on', 'the name field']); 1413 | form.errors().list('name'); 1414 | ``` 1415 | ``` 1416 | Output: ['random messages', 'set on', 'the name field'] 1417 | ``` 1418 | 1419 | ### Forget Error 1420 | > Forget error messages for a specific field 1421 | ```js 1422 | let data = { name: '' }; 1423 | let rules = { name: 'required' }; 1424 | 1425 | form(data).rules(rules).validate().list('name'); 1426 | ``` 1427 | ``` 1428 | Output: ['name is a required field'] 1429 | ``` 1430 | ```js 1431 | form.errors().forget('name'); 1432 | form.errors().list('name'); 1433 | ``` 1434 | ``` 1435 | Output: [] 1436 | ``` 1437 | 1438 | --- 1439 | 1440 | ## Extending the Error Bag 1441 | 1442 | --- 1443 | 1444 | --- 1445 | 1446 | ### Error Bag Macro 1447 | 1448 | --- 1449 | Adds custom method on form error bag instance 1450 | 1451 | ```js 1452 | let example = form(data).rules(rules); 1453 | 1454 | let example.errors().macro('count', function () { 1455 | return this.list().length; 1456 | }); 1457 | 1458 | 1459 | // example.errors().count() === example.errors().list().length 1460 | ``` 1461 | 1462 | 1463 | --- 1464 | 1465 | ### Error Bag ForceMacro 1466 | 1467 | --- 1468 | Allows you to overwrite pre-defined macro and over write core error message bag functions (Use With Caution) 1469 | 1470 | ```js 1471 | let example = form({ name: '' }).rules({ name: 'required|min:3' }).validate(); 1472 | 1473 | example.errors().get('name'); // Outputs: "Name is a required field" 1474 | 1475 | example.errors().forceMacro('get', function (field) { 1476 | if (this.has(field)) { 1477 | return this.list(field).join(', ') + '.'; 1478 | } 1479 | }); 1480 | 1481 | example.errors().get('name'); // Outputs: "Name is a required field, name must have at least 3 characters." 1482 | ``` 1483 | -------------------------------------------------------------------------------- /bundler/versioning.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Versioning 4 | 5 | --- 6 | 7 | > Vuejs-Form Will Implement Semantic Versioning 8 | > 9 | > Starting (Friday, May 15, 2020) 10 | 11 | |Code Status|Stage|Rule|Example Version| 12 | |---|---|---|---| 13 | |First release|New Product|Start with 1.0.0|1.0.0| 14 | |Backward compatible bug fixes|Patch Release|Increment the third digit|1.0.1| 15 | |Backward compatible new features|Minor Release|Increment the middle digit and reset last digit to zero|1.1.0| 16 | |Changes that break backward compatibility|Major Release|Increment the first digit and reset middle and last digits to zero|2.0.0| 17 | 18 | - [Learn More About Semantic Versioning](https://docs.npmjs.com/about-semantic-versioning) 19 | -------------------------------------------------------------------------------- /bundler/vue.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Quick Vue Example 4 | 5 | --- 6 | 7 | ```html 8 | 24 | ``` 25 | ```js 26 | import form from 'vuejs-form' 27 | 28 | export default { 29 | data: () => ({ 30 | form: form({ 31 | email: '', 32 | password: '', 33 | confirm_password: '' 34 | }) 35 | .rules({ 36 | email: 'email|min:5|required', 37 | password: 'same:confirm_password', 38 | confirm_password: 'min:6|required', 39 | }) 40 | .messages({ 41 | 'email.required': ':attribute is required', 42 | 'email.email': ':attribute must be a valid email', 43 | 'email.min': ':attribute may not have less than :min characters', 44 | 'password.same': 'Whoops, :attribute does not match the :same field', 45 | }), 46 | }), 47 | 48 | watch: { 49 | /*-------------------------------------------------------------- 50 | * When Should Your Form "Validate", Providing Error Messages? 51 | *-------------------------------------------------------------- 52 | * Form validates every time form data is updated. To 53 | * display errors on form submit, remove watcher & 54 | * move "this.form.validate()" over to submit() 55 | *-------------------------------------------------------------- 56 | */ 57 | ['form.data']: { 58 | deep: true, 59 | immediate: false, 60 | handler: (now, old) => { this.form.validate(); }, 61 | } 62 | }, 63 | 64 | methods: { 65 | failed() { 66 | console.log('errors: ', this.form.errors().all()); 67 | }, 68 | passed() { 69 | console.log('data: ', this.form.all()); 70 | console.log('wrapped data: ', this.form.wrap('data')); 71 | }, 72 | submit() { 73 | return this.form.errors().any() ? this.failed() : this.passed(); 74 | }, 75 | } 76 | } 77 | ``` 78 | -------------------------------------------------------------------------------- /bundler/vuejs-form-purpose-statement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhorton34/vuejs-form/76474941aa5eaad773bec16b66291ec99852cd5b/bundler/vuejs-form-purpose-statement.png -------------------------------------------------------------------------------- /code_of_conduct.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ## Code Of Conduct 4 | 5 | --- 6 | 7 | The Clean Code Studio code of conduct is derived from Laravel code of of conduct. Any violations 8 | of the code of conduct may be reported to Zachary Horton (zak@cleancode.studio) 9 | 10 | - Participants will be tolerant of opposing views. 11 | 12 | - Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks. 13 | 14 | - When interpreting the words and actions of others, participants should always assume good intentions. 15 | 16 | - Behavior that can be reasonably considered harassment will not be tolerated. 17 | -------------------------------------------------------------------------------- /dist/helpers/accessor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Get value of a nested property 4 | * 5 | * @param form 6 | * @returns {*} 7 | */ 8 | 9 | module.exports = function access(form) { 10 | return new Proxy(form, { 11 | get: function get(target, key) { 12 | if (Object.keys(target.data).includes(key)) { 13 | if (!Object.keys(target).includes(key)) { 14 | target[key] = null; // Initialize an empty key if the property does not exist. 15 | } 16 | 17 | return target.data[key]; 18 | } 19 | 20 | return target[key]; 21 | }, 22 | set: function set(target, key, value) { 23 | target.data[key] = value; 24 | return target.data[key] === value; 25 | } 26 | }); 27 | }; -------------------------------------------------------------------------------- /dist/helpers/dataGet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } 4 | 5 | function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 6 | 7 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 8 | 9 | function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } 10 | 11 | function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } 12 | 13 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 14 | 15 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 16 | 17 | var isObject = function isObject(check) { 18 | return _typeof(check) === 'object' && Array.isArray(check) === false; 19 | }; 20 | 21 | var isArray = function isArray(check) { 22 | return Array.isArray(check) === true; 23 | }; 24 | /** 25 | * Get Nested Data With An Optional "*" wildcard 26 | */ 27 | 28 | 29 | module.exports = function (target) { 30 | var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; 31 | 32 | if (path === '') { 33 | return target; 34 | } 35 | 36 | path = Array.isArray(path) ? path : path.split('.'); 37 | return path.reduce(function (value, segment) { 38 | if (segment === '*') { 39 | if (isArray(value)) { 40 | return value.reduce(function (list, item) { 41 | return [].concat(_toConsumableArray(list), [item]); 42 | }, []); 43 | } else if (isObject(value)) { 44 | return Object.values(value); 45 | } else { 46 | return value; 47 | } 48 | } 49 | 50 | if (isArray(value)) { 51 | return value.reduce(function (list, item) { 52 | if (isObject(item)) { 53 | return Object.keys(item).includes(segment) ? [].concat(_toConsumableArray(list), [item[segment]]) : _toConsumableArray(list); 54 | } else if (!isObject(item) && isArray(item)) { 55 | return [].concat(_toConsumableArray(list), [item[segment]]); 56 | } else { 57 | return [].concat(_toConsumableArray(list), [item]); 58 | } 59 | }, []); 60 | } 61 | 62 | if (isObject(value)) { 63 | return value[segment]; 64 | } 65 | 66 | return value[segment] || value || null; 67 | }, target); 68 | }; -------------------------------------------------------------------------------- /dist/helpers/dataSet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 4 | 5 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 6 | 7 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 8 | 9 | var example = { 10 | family: { 11 | mom: { 12 | name: '' 13 | } 14 | } 15 | }; 16 | 17 | var dataSet = function dataSet(target, value) { 18 | var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; 19 | if (path === '') return _objectSpread(_objectSpread({}, target), value); 20 | path = Array.isArray(path) ? path : path.split('.'); // 21 | // if (!path.includes('*')) { 22 | // target[path] = value; 23 | // 24 | // return target; 25 | // } 26 | 27 | var types = ['wildcard', 'object', 'array', 'value', 'empty']; 28 | 29 | var iterator = function iterator(path, loop) { 30 | return { 31 | loop: loop, 32 | last: loop.length === path.length, 33 | prev: loop === 0 ? false : loop - 1, 34 | next: loop === path.length ? false : loop + 1, 35 | left: path.length - loop 36 | }; 37 | }; 38 | 39 | var resolve = function resolve(target, path, iteration) { 40 | return { 41 | wildcard: path[iteration.loop] === '*', 42 | prevWildcard: iteration.prev ? path[iteration.prev] === '*' : false, 43 | nextWildcard: iteration.next ? path[iteration.next] === '*' : false, 44 | target: target, 45 | path: path 46 | }; 47 | }; 48 | 49 | var structure = function structure(target, path, value, segment, iterator, mapping) { 50 | return { 51 | mapping: mapping, 52 | path: target[path[iterator.loop]], 53 | prev: iterator.prev ? resolve(target, path, iterator) : false, 54 | next: iterator.next ? resolve(target, path, iterator) : false 55 | }; 56 | }; 57 | 58 | var loop = 0; 59 | var structures = []; 60 | path.forEach(function (segment) { 61 | structures[loop] = structure(target, path, value, segment, iterator(path, loop), structures); 62 | loop = loop + 1; 63 | }); 64 | return structures; 65 | }; 66 | 67 | dataSet(example, value, 'family.mom.name'); -------------------------------------------------------------------------------- /dist/helpers/exists.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Determine if a key value pair is missing 4 | * 5 | * @param value 6 | * @returns boolean 7 | */ 8 | 9 | module.exports = function (value) { 10 | return typeof value !== "undefined"; 11 | }; -------------------------------------------------------------------------------- /dist/helpers/fieldsOf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var variadic = require('./variadic'); 4 | 5 | module.exports = function (form, keys) { 6 | var properties = variadic(keys); 7 | return properties.length > 0 ? { 8 | has: function has() { 9 | return properties; 10 | }, 11 | toArray: function toArray() { 12 | return Array.isArray(properties) ? properties : Array.from(properties); 13 | }, 14 | isEmpty: function isEmpty() { 15 | return Array.isArray(properties) ? properties.length === 0 : Array.from(properties).length === 0; 16 | } 17 | } : { 18 | has: function has() { 19 | return form.keys(); 20 | }, 21 | toArray: function toArray() { 22 | return form.keys(); 23 | }, 24 | isEmpty: function isEmpty() { 25 | return form.keys().length === 0; 26 | } 27 | }; 28 | }; -------------------------------------------------------------------------------- /dist/helpers/isEmpty.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Determine if a value is empty 4 | * 5 | * @param value 6 | * @returns boolean 7 | */ 8 | 9 | module.exports = function (value) { 10 | if (value === null || value === '') return true; 11 | if (Array.isArray(value)) return value.length === 0; 12 | 13 | for (var key in value) { 14 | if (Object.keys(value, key)) return false; 15 | } 16 | 17 | return true; 18 | }; -------------------------------------------------------------------------------- /dist/helpers/nestedValue.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Get value of a nested property 4 | * 5 | * @param mainObject 6 | * @param key 7 | * @returns {*} 8 | */ 9 | 10 | module.exports = function nestedValue(mainObject, key) { 11 | try { 12 | return key.split('.').reduce(function (obj, property) { 13 | return obj[property]; 14 | }, mainObject); 15 | } catch (err) { 16 | return null; 17 | } 18 | }; -------------------------------------------------------------------------------- /dist/helpers/setKeys.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var variadic = require('./variadic'); 4 | 5 | module.exports = function (form, keys) { 6 | var properties = variadic(keys); 7 | return properties.length > 0 ? { 8 | has: function has() { 9 | return properties; 10 | } 11 | } : { 12 | has: function has() { 13 | return form.keys(); 14 | } 15 | }; 16 | }; -------------------------------------------------------------------------------- /dist/helpers/setProperties.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var variadic = require('./variadic.js'); 4 | 5 | module.exports = function (form, keys) { 6 | var properties = variadic(keys); 7 | return properties.length > 0 ? { 8 | orAll: function orAll() { 9 | return properties; 10 | } 11 | } : { 12 | orAll: function orAll() { 13 | return form.keys(); 14 | } 15 | }; 16 | }; -------------------------------------------------------------------------------- /dist/helpers/variadic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Variadic helper function 4 | * 5 | * @param args 6 | * @returns {*} 7 | */ 8 | 9 | module.exports = function variadic(args) { 10 | if (Array.isArray(args[0])) { 11 | return args[0]; 12 | } 13 | 14 | return args; 15 | }; -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 4 | 5 | function VueForm(input) { 6 | if (input !== undefined && !Array.isArray(input) && _typeof(input) !== 'object') { 7 | this.data = [input]; 8 | } else if (input instanceof this.constructor) { 9 | this.data = input.all(); 10 | } else { 11 | this.data = input || {}; 12 | } 13 | } 14 | 15 | VueForm.prototype.anyFilled = require('./methods/anyFilled'); 16 | VueForm.prototype.all = require('./methods/all'); 17 | VueForm.prototype["boolean"] = require('./methods/boolean'); 18 | VueForm.prototype.empty = require('./methods/empty'); 19 | VueForm.prototype.except = require('./methods/except'); 20 | VueForm.prototype.extend = require('./methods/extend'); 21 | VueForm.prototype.fill = require('./methods/fill'); 22 | VueForm.prototype.filled = require('./methods/filled'); 23 | VueForm.prototype.forget = require('./methods/forget'); 24 | VueForm.prototype.has = require('./methods/has'); 25 | VueForm.prototype.hasAny = require('./methods/hasAny'); 26 | VueForm.prototype.input = require('./methods/input'); 27 | VueForm.prototype.keys = require('./methods/keys'); 28 | VueForm.prototype.make = require('./methods/make'); 29 | VueForm.prototype.missing = require('./methods/missing'); 30 | VueForm.prototype.only = require('./methods/only'); 31 | VueForm.prototype.set = require('./methods/set'); 32 | VueForm.prototype.toArray = require('./methods/toArray'); 33 | VueForm.prototype.wrap = require('./methods/wrap'); 34 | VueForm.prototype.localMacro = require('./methods/localMacro'); 35 | VueForm.prototype.forceLocalMacro = require('./methods/forceLocalMacro'); 36 | VueForm.prototype.macro = require('./methods/macro'); 37 | VueForm.prototype.forceMacro = require('./methods/forceMacro'); 38 | VueForm.prototype.proxy = require('./methods/proxy'); 39 | VueForm.prototype.build = require('./methods/build'); 40 | VueForm.prototype.use = require('./methods/use'); 41 | 42 | var form = function form(dataOrValidatable, data) { 43 | if (typeof data === "undefined") { 44 | return new VueForm(dataOrValidatable).proxy(); 45 | } else if (typeof dataOrValidatable === 'function') { 46 | return new VueForm(data).use(dataOrValidatable, {}).proxy(); 47 | } else if (typeof dataOrValidatable !== 'function' && typeof data !== "undefined") { 48 | console.error("form(validatable, data): validatable must be an instance of vuejs-validators: See vuejs-form Docs"); 49 | console.log("vuejs-form has a powerful, optional, validation library. vuejs-validators"); 50 | console.log("vuejs-validators exports a validator function"); 51 | console.log('vuejs-validators docs: https://github.com/zhorton34/vuejs-validators'); 52 | console.log('vuejs-forms docs: https://github.com/zhorton34/vuejs-form'); 53 | console.log('If you are trying make your vuejs-form data "validatable": ', '1: Install vuejs-validators', '2: Pass in vuejs-validators "validation" instance as the 1st parameter and the form data as the 2nd parameter (Ex: "form(validator, data)")'); 54 | console.log("-----------------"); 55 | console.log('To create a vuejs-form that is NOT "validatable" simply:', '1: Omit the second parameter', '2: Pass in data as the first parameter', '2: Non Validatable Form Example: form({ name: "sarah", email: "sarah.smith@gmail.com" })'); 56 | } 57 | 58 | return new VueForm(data).proxy(); 59 | }; 60 | 61 | var validatable = require('vuejs-validators'); 62 | 63 | var _require = require('vuejs-validators'), 64 | MessageBag = _require.MessageBag, 65 | MessageBagFactory = _require.MessageBagFactory; 66 | 67 | var ValidatableForm = function ValidatableForm() { 68 | var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 69 | return form(validatable, data); 70 | }; 71 | 72 | module.exports = ValidatableForm; 73 | module.exports.VueForm = VueForm; 74 | module.exports.SimpleForm = form; 75 | module.exports.validator = validatable; 76 | module.exports["default"] = ValidatableForm; 77 | module.exports.ValidatableForm = ValidatableForm; 78 | module.exports.MessageBag = MessageBag; 79 | module.exports.MessageBagFactory = MessageBagFactory; -------------------------------------------------------------------------------- /dist/methods/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function all() { 4 | return this.data; 5 | }; -------------------------------------------------------------------------------- /dist/methods/anyFilled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var dataGet = require('../helpers/dataGet'); 4 | 5 | var isEmpty = require('../helpers/isEmpty'); 6 | 7 | var fieldsOf = require('../helpers/fieldsOf'); 8 | 9 | module.exports = function anyFilled() { 10 | var _this = this; 11 | 12 | var valueFilled = function valueFilled(key) { 13 | return isEmpty(dataGet(_this.data, key)) === false; 14 | }; 15 | 16 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 17 | args[_key] = arguments[_key]; 18 | } 19 | 20 | return fieldsOf(this, args).has().some(valueFilled); 21 | }; -------------------------------------------------------------------------------- /dist/methods/boolean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var dataGet = require('../helpers/dataGet.js'); 4 | 5 | module.exports = function _boolean(property) { 6 | var truthy = [1, "1", true, "true", "on", "yes"]; 7 | return truthy.includes(dataGet(this.data, property)); 8 | }; -------------------------------------------------------------------------------- /dist/methods/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var accessor = require('../helpers/accessor.js'); 4 | 5 | module.exports = function build() { 6 | return accessor(this); 7 | }; -------------------------------------------------------------------------------- /dist/methods/empty.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isEmpty = require('../helpers/isEmpty'); 4 | 5 | var dataGet = require('../helpers/dataGet'); 6 | 7 | var fieldsOf = require('../helpers/fieldsOf.js'); 8 | 9 | module.exports = function empty() { 10 | var _this = this; 11 | 12 | var emptyValue = function emptyValue(key) { 13 | return isEmpty(dataGet(_this.data, key)); 14 | }; 15 | 16 | for (var _len = arguments.length, properties = new Array(_len), _key = 0; _key < _len; _key++) { 17 | properties[_key] = arguments[_key]; 18 | } 19 | 20 | return fieldsOf(this, properties).has().some(emptyValue); 21 | }; -------------------------------------------------------------------------------- /dist/methods/except.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 4 | 5 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 6 | 7 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 8 | 9 | var variadic = require('../helpers/variadic.js'); 10 | 11 | module.exports = function except() { 12 | var _this = this; 13 | 14 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 15 | args[_key] = arguments[_key]; 16 | } 17 | 18 | var properties = variadic(args); 19 | return Object.keys(this.data).filter(function (property) { 20 | return !properties.includes(property); 21 | }).reduce(function (only, field) { 22 | return _objectSpread(_defineProperty({}, field, _this.data[field]), only); 23 | }, {}); 24 | }; -------------------------------------------------------------------------------- /dist/methods/extend.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function extend(name, callback) { 4 | if (Object.keys(this.constructor.prototype).includes(name)) { 5 | return console.error("Cant extend form with method ".concat(name, ", it already exists")); 6 | } 7 | 8 | this.constructor.prototype[name] = callback; 9 | }; -------------------------------------------------------------------------------- /dist/methods/fill.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } 4 | 5 | function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 6 | 7 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 8 | 9 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 10 | 11 | function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } 12 | 13 | function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } 14 | 15 | module.exports = function fill() { 16 | var _this = this; 17 | 18 | var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 19 | var value = arguments.length > 1 ? arguments[1] : undefined; 20 | 21 | if (typeof value === "undefined") { 22 | Object.entries(input).forEach(function (_ref) { 23 | var _ref2 = _slicedToArray(_ref, 2), 24 | key = _ref2[0], 25 | value = _ref2[1]; 26 | 27 | if (_this.empty(key)) { 28 | _this.data[key] = value; 29 | } 30 | }); 31 | } else if (this.empty(input)) { 32 | this.data[input] = value; 33 | } 34 | }; -------------------------------------------------------------------------------- /dist/methods/filled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isEmpty = require('../helpers/isEmpty'); 4 | 5 | var dataGet = require('../helpers/dataGet'); 6 | 7 | var fieldsOf = require('../helpers/fieldsOf'); 8 | 9 | module.exports = function filled() { 10 | var _this = this; 11 | 12 | var valueFilled = function valueFilled(key) { 13 | return isEmpty(dataGet(_this.data, key)) === false; 14 | }; 15 | 16 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 17 | args[_key] = arguments[_key]; 18 | } 19 | 20 | return fieldsOf(this, args).has().every(valueFilled); 21 | }; -------------------------------------------------------------------------------- /dist/methods/forceLocalMacro.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Similar to "localMacro" function, with ability to forcibly overwrite base prototype methods and existing macro methods (On Local Instance instead of globally) 4 | * (See Laravel Macros For In Depth Explanation) 5 | * 6 | * @param name 7 | * @param fn 8 | * @return void 9 | */ 10 | 11 | module.exports = function forceMacro(name, fn) { 12 | console.warn("\n\t\tForcing macro to implement ".concat(name, " method -- Move forward with caution.\n\t\tWe recommend using \"macro\" in place of \"forceMacro\" method if you do not \n\t\tneed to override pre-existing or base behavior.\n\t ")); 13 | this[name] = fn; 14 | }; -------------------------------------------------------------------------------- /dist/methods/forceMacro.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Similar to "macro" function, with ability to forcibly overwrite base prototype methods and existing macro methods 4 | * (See Laravel Macros For In Depth Explanation) 5 | * 6 | * @param name 7 | * @param fn 8 | * @return void 9 | */ 10 | 11 | module.exports = function forceMacro(name, fn) { 12 | console.warn("\n\t\tForcing macro to implement ".concat(name, " method -- Move forward with caution.\n\t\tWe recommend using \"macro\" in place of \"forceMacro\" method if you do not \n\t\tneed to override pre-existing or base behavior.\n\t ")); 13 | this[name] = fn; 14 | this.constructor.prototype[name] = fn; 15 | }; -------------------------------------------------------------------------------- /dist/methods/forget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } 4 | 5 | function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 6 | 7 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 8 | 9 | function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } 10 | 11 | function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } 12 | 13 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 14 | 15 | var fieldsFrom = require('../helpers/fieldsOf.js'); 16 | 17 | module.exports = function forget() { 18 | for (var _len = arguments.length, list = new Array(_len), _key = 0; _key < _len; _key++) { 19 | list[_key] = arguments[_key]; 20 | } 21 | 22 | var fields = fieldsFrom(this, list); 23 | this.data = fields.isEmpty() ? {} : this.except.apply(this, _toConsumableArray(fields.toArray())); 24 | }; -------------------------------------------------------------------------------- /dist/methods/has.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var dataGet = require('../helpers/dataGet'); 4 | 5 | var fieldsOf = require('../helpers/fieldsOf'); 6 | 7 | module.exports = function has() { 8 | var _this = this; 9 | 10 | var value = function value(key) { 11 | return dataGet(_this.data, key); 12 | }; 13 | 14 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 15 | args[_key] = arguments[_key]; 16 | } 17 | 18 | return fieldsOf(this, args).has().every(value); 19 | }; -------------------------------------------------------------------------------- /dist/methods/hasAny.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var dataGet = require('../helpers/dataGet'); 4 | 5 | var fieldsOf = require('../helpers/fieldsOf'); 6 | 7 | module.exports = function hasAny() { 8 | var _this = this; 9 | 10 | var value = function value(key) { 11 | return dataGet(_this.data, key); 12 | }; 13 | 14 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 15 | args[_key] = arguments[_key]; 16 | } 17 | 18 | return fieldsOf(this, args).has().some(value); 19 | }; -------------------------------------------------------------------------------- /dist/methods/input.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isEmpty = require('../helpers/isEmpty.js'); 4 | 5 | var dataGet = require('../helpers/dataGet.js'); 6 | 7 | module.exports = function input(key) { 8 | var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 9 | var value = dataGet(this.data, key); 10 | return isEmpty(value) ? defaultValue : value; 11 | }; -------------------------------------------------------------------------------- /dist/methods/keys.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function keys() { 4 | return Object.keys(this.data); 5 | }; -------------------------------------------------------------------------------- /dist/methods/localMacro.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Add/Extend Form Functionality On Specific Instance With Custom Methods Using LocalMacro 4 | * 5 | * @param name 6 | * @param fn 7 | */ 8 | 9 | module.exports = function localMacro(name, fn) { 10 | if (typeof this.constructor.prototype[name] !== 'undefined' || typeof this[name] !== 'undefined') { 11 | console.error("Cant extend form with ".concat(name, " localMacro, it already exists (use forceLocalMacro if you want to forcibly overwrite base behavior or previously set macro/localMacro")); 12 | } else { 13 | this[name] = fn; 14 | } 15 | }; -------------------------------------------------------------------------------- /dist/methods/macro.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Add/Extend Form Functionality With Custom Methods Using Macros (globally) 4 | * 5 | * @param name 6 | * @param fn 7 | */ 8 | 9 | module.exports = function macro(name, fn) { 10 | if (Object.keys(this.constructor.prototype).includes(name)) { 11 | return console.error("Cant extend form with ".concat(name, " macro, it already exists (use forceMacro if you want to forcibly overwrite base behavior or previously set macro")); 12 | } else { 13 | this[name] = fn; 14 | this.constructor.prototype[name] = fn; 15 | } 16 | }; -------------------------------------------------------------------------------- /dist/methods/make.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var accessor = require('../helpers/accessor'); 4 | 5 | module.exports = function make(input) { 6 | return accessor(new this.constructor(input)); 7 | }; -------------------------------------------------------------------------------- /dist/methods/missing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var exists = require('../helpers/exists'); 4 | 5 | var dataGet = require('../helpers/dataGet'); 6 | 7 | var fieldsOf = require('../helpers/fieldsOf'); 8 | 9 | module.exports = function missing() { 10 | var _this = this; 11 | 12 | var missing = function missing(key) { 13 | return !exists(dataGet(_this.data, key)); 14 | }; 15 | 16 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 17 | args[_key] = arguments[_key]; 18 | } 19 | 20 | return fieldsOf(this, args).has().some(missing); 21 | }; -------------------------------------------------------------------------------- /dist/methods/only.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 4 | 5 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 6 | 7 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 8 | 9 | var variadic = require('../helpers/variadic'); 10 | 11 | module.exports = function only() { 12 | var _this = this; 13 | 14 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 15 | args[_key] = arguments[_key]; 16 | } 17 | 18 | var properties = variadic(args); 19 | return properties.reduce(function (only, field) { 20 | return _objectSpread(_defineProperty({}, field, _this.data[field]), only); 21 | }, {}); 22 | }; -------------------------------------------------------------------------------- /dist/methods/override.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Add/Extend Form Functionality With Custom Methods Using Macros 4 | * 5 | * @param name 6 | * @param fn 7 | */ 8 | 9 | module.exports = function override(name, fn) { 10 | if (Object.keys(this.constructor.prototype).includes(name)) { 11 | console.warn("\n\t\t\tOverriding default form method ".concat(name, ". With customized functionality. \n\t\t\tProceed with caution, you could break EVERYTHING...(Hopefully wont though :)\n\t\t")); 12 | } 13 | 14 | this.constructor.prototype[name] = fn; 15 | }; -------------------------------------------------------------------------------- /dist/methods/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var accessor = require('../helpers/accessor.js'); 4 | 5 | module.exports = function proxy() { 6 | return accessor(this); 7 | }; -------------------------------------------------------------------------------- /dist/methods/set.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } 4 | 5 | function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 6 | 7 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 8 | 9 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 10 | 11 | function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } 12 | 13 | function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } 14 | 15 | module.exports = function set() { 16 | var _this = this; 17 | 18 | var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 19 | var value = arguments.length > 1 ? arguments[1] : undefined; 20 | 21 | if (typeof value === "undefined") { 22 | Object.entries(input).forEach(function (_ref) { 23 | var _ref2 = _slicedToArray(_ref, 2), 24 | key = _ref2[0], 25 | value = _ref2[1]; 26 | 27 | _this.data[key] = value; 28 | }); 29 | } else { 30 | this.data[input] = value; 31 | } 32 | }; -------------------------------------------------------------------------------- /dist/methods/toArray.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } 4 | 5 | function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 6 | 7 | function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 8 | 9 | function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 10 | 11 | function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } 12 | 13 | function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } 14 | 15 | module.exports = function toArray() { 16 | return Object.entries(this.data).map(function (_ref) { 17 | var _ref2 = _slicedToArray(_ref, 2), 18 | key = _ref2[0], 19 | value = _ref2[1]; 20 | 21 | return { 22 | key: key, 23 | value: value 24 | }; 25 | }); 26 | }; -------------------------------------------------------------------------------- /dist/methods/use.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Use Validator (vuejs-validators) 4 | * 5 | * @param validatable 6 | * @param options 7 | * @returns {*} 8 | */ 9 | 10 | module.exports = function use(validatable) { 11 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 12 | 13 | this.setValidator = function () { 14 | var rules = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 15 | var messages = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 16 | var translator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 17 | this.validatorInstance = validatable(this.data, rules, messages, translator); 18 | return this; 19 | }; 20 | 21 | this.hasValidator = function () { 22 | return typeof this.validatorInstance !== 'undefined'; 23 | }; 24 | 25 | this.rules = function () { 26 | var rules = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 27 | 28 | if (this.validator()) { 29 | this.validator().setRules(rules); 30 | } else { 31 | this.validatorInstance = validatable(this.data, rules); 32 | } 33 | 34 | return this; 35 | }; 36 | 37 | this.messages = function () { 38 | var messages = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 39 | var rules = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 40 | 41 | if (this.hasValidator()) { 42 | this.validator().setMessages(messages); 43 | } else { 44 | this.validatorInstance = validatable(this.data, rules, messages); 45 | } 46 | 47 | return this; 48 | }; 49 | 50 | this.validate = function () { 51 | this.validator().setData(this.data); 52 | this.validator().validate(); 53 | return this; 54 | }; 55 | 56 | this.validator = function () { 57 | return this.validatorInstance; 58 | }; 59 | 60 | this.errors = function () { 61 | return this.validator().errors(); 62 | }; 63 | 64 | this.setValidator(options); 65 | return this; 66 | }; -------------------------------------------------------------------------------- /dist/methods/wrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 4 | 5 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 6 | 7 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 8 | 9 | module.exports = function wrap(key) { 10 | return key.split('.').reverse().reduce(function (payload, property) { 11 | return _defineProperty({}, property, _objectSpread({}, payload)); 12 | }, this.data); 13 | }; -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const { readdirSync } = require('fs'); 2 | 3 | module.exports = { 4 | title: 'vuejs-form', 5 | description: 'Simplified, dependency free wrapper for easily interacting with form input data.', 6 | themeConfig: { 7 | nav: [ 8 | { text: 'Home', link: '/' }, 9 | { text: 'Installation', link: '/installation.md' }, 10 | { text: 'Usage', link: '/usage.md' }, 11 | { text: 'API', link: '/api.md' }, 12 | { text: 'GitHub', link: 'https://github.com/zhorton34/vuejs-form.js' }, 13 | ], 14 | sidebar: [{ 15 | title: 'Get started', 16 | collapsable:false, 17 | children: [ 18 | 'installation', 19 | 'usage', 20 | ], 21 | }, { 22 | title: 'API', 23 | collapsable: false, 24 | children: readdirSync('docs/api', 'utf-8').map(file => `/api/${file}`), 25 | }], 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | actionText: Get Started → 4 | actionLink: /installation/ 5 | features: 6 | - title: 📄 Simplified API 7 | details: Enjoy Building Forms In Vue 8 | - title: 🏗 Extendable 9 | details: Want to customize? vuejs-form.js can be extended using extends. 10 | - title: Vuejs + Forms 11 | details: Simplify Vue Forms With This Package Inspired By Laravel $request->input() and set up based on Collection.js 12 | footer: MIT Licensed | Copyright © Zachary Horton 13 | --- -------------------------------------------------------------------------------- /docs/api/all.md: -------------------------------------------------------------------------------- 1 | # `all()` 2 | 3 | The all method returns the underlying input object represented by the form: 4 | 5 | ```js 6 | form({ name: 'sarah', email: 'sarah@gmail.com' }).all(); 7 | 8 | // { name: 'sarah', email: 'sarah@gmail.com' } 9 | ``` 10 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/all.js) 11 | -------------------------------------------------------------------------------- /docs/api/boolean.md: -------------------------------------------------------------------------------- 1 | # `boolean(property)` 2 | 3 | The boolean method determines if the given field has a truthy or falsy values: 4 | #### Truthy values: true, "true", "yes", "on", "1", 1 5 | #### Falsy values: Everything else 6 | 7 | ```js 8 | 9 | const LoginForm = form({ 10 | name: '', 11 | email: '', 12 | terms: '' 13 | }) 14 | 15 | LoginForm.terms = true 16 | LoginForm.boolean('terms') // true 17 | 18 | LoginForm.terms = 'true' 19 | LoginForm.boolean('terms') // true 20 | 21 | LoginForm.terms = 'yes' 22 | LoginForm.boolean('terms') // true 23 | 24 | LoginForm.terms = 'on' 25 | LoginForm.boolean('terms') // true 26 | 27 | LoginForm.terms = "1" 28 | LoginForm.boolean('terms') // true 29 | 30 | LoginForm.terms = 1 31 | LoginForm.boolean('terms') // true 32 | ``` 33 | 34 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/boolean.js) 35 | -------------------------------------------------------------------------------- /docs/api/empty.md: -------------------------------------------------------------------------------- 1 | # `empty(one, two, three, ...)` 2 | 3 | The empty method determines if the input property exists but the value is empty: 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.empty('name') // false 9 | ExampleForm.empty('name', 'email') // false 10 | 11 | ExampleForm.empty('id') // true 12 | ``` 13 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/empty.js) 14 | -------------------------------------------------------------------------------- /docs/api/except.md: -------------------------------------------------------------------------------- 1 | # `except(one, two, three, ...)` 2 | 3 | The except method grabs all of the inputs except the properties passed in: 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.except('id') 9 | /** 10 | * { name: 'sarah', email: 'sarah@gmail.com' } 11 | */ 12 | 13 | ExampleForm.except('id', 'name') 14 | /** 15 | * { email: 'sarah@gmail.com' } 16 | */ 17 | ``` 18 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/except.js) 19 | 20 | -------------------------------------------------------------------------------- /docs/api/fill.md: -------------------------------------------------------------------------------- 1 | # `fill({ key: value, keyTwo: valueTwo, etc... })` 2 | 3 | The fill method allows you to fill in new or empty values without overriding existing values: 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.fill({ 9 | id: 2, 10 | name: 'tim', 11 | email: 'tim@gmail.com' 12 | }) 13 | 14 | ExampleForm.all() 15 | // { id: 2, name: 'sarah', email: 'sarah@gmail.com' } 16 | ``` 17 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/fill.js) 18 | -------------------------------------------------------------------------------- /docs/api/filled.md: -------------------------------------------------------------------------------- 1 | # `filled(propertyOne, propertyTwo, etc...)` 2 | 3 | The filled method determine if a value is filled (AKA not empty): 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.filled('id', 'name') // false 9 | ExampleForm.filled('name', 'email') // true 10 | ``` 11 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/filled.js) 12 | -------------------------------------------------------------------------------- /docs/api/forceMacro.md: -------------------------------------------------------------------------------- 1 | # `forceMacro(key, fn)` 2 | forceMacro can be used to extend form object and FORCIBLY OVERWRITE base form behavior (Use VERY cautiously and prefer macro over forceMacro) 3 | 4 | _NOTE: Use forceLocalMacro if you only want to extend a specific form instance instead of all form instances._ 5 | 6 | ```js 7 | import form from 'vuejs-form'; 8 | 9 | form().forceMacro('all', function () { 10 | return this.keys().reduce((list, field) => ({ 11 | ...list, 12 | [field]: { 13 | name: field, 14 | value: this.data[field], 15 | errors: this.errors().list(field), 16 | } 17 | }), 18 | {}); 19 | }) 20 | 21 | form({ name: 'sam' }).rules({ name: 'required' }).validate(); 22 | ``` 23 | 24 | ``` 25 | # forceMacro implementation of form.all() Outputs 26 | { 27 | name: { 28 | value: 'sam', 29 | name: 'name', 30 | errors: ['Name field is required'] 31 | } 32 | } 33 | ``` 34 | 35 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/forceMacro.js) 36 | 37 | -------------------------------------------------------------------------------- /docs/api/forget.md: -------------------------------------------------------------------------------- 1 | # `forget(propertyOne, propertyTwo, etc...)` 2 | 3 | The forget method will remove or "forget" a key value pair from the form input data 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.forget('id', 'name') 9 | ExampleForm.all() // { email: 'sarah@gmail.com' } 10 | ``` 11 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/forget.js) 12 | -------------------------------------------------------------------------------- /docs/api/has.md: -------------------------------------------------------------------------------- 1 | # `has(propertyOne, propertyTwo, etc...)` 2 | 3 | The has method will determine if a key exists within the form input data 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.has('id', 'name') // true 9 | ExampleForm.has('something', 'id', 'name') // false 10 | ``` 11 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/has.js) 12 | -------------------------------------------------------------------------------- /docs/api/hasAny.md: -------------------------------------------------------------------------------- 1 | # `hasAny(propertyOne, propertyTwo, etc...)` 2 | 3 | The hasAny method will determine if a key has any of the given properties within the form input data 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.hasAny('id', 'name') // true 9 | ExampleForm.hasAny('something', 'id', 'name') // true 10 | ``` 11 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/hasAny.js) 12 | -------------------------------------------------------------------------------- /docs/api/input.md: -------------------------------------------------------------------------------- 1 | # `input(property, default = false)` 2 | 3 | The input method will resolve a given input value or default to false. You can define a default as the second parameter 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.input('id') // false 9 | ExampleForm.input('id', 1) // 1 10 | ExampleForm.input('name', 'tim') // sarah 11 | ``` 12 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/input.js) 13 | -------------------------------------------------------------------------------- /docs/api/keys.md: -------------------------------------------------------------------------------- 1 | # `keys()` 2 | 3 | The keys method will resolve an array of the input keys 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.keys() // ['id', 'name', 'email'] 9 | ``` 10 | 11 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/keys.js) 12 | -------------------------------------------------------------------------------- /docs/api/macro.md: -------------------------------------------------------------------------------- 1 | # `macro(key, fn)` 2 | 3 | The macro method can be used to extend the forms base behavior with custom methods/functions 4 | 5 | _NOTE: Use localMacro if you only want to extend a specific form instance instead of all form instances._ 6 | 7 | ```js 8 | import form from 'vuejs-form'; 9 | 10 | form(data).macro('count', () => { 11 | return this.keys().length; 12 | }); 13 | 14 | // form.count() === form.keys().length 15 | ``` 16 | 17 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/macro.js) 18 | 19 | -------------------------------------------------------------------------------- /docs/api/make.md: -------------------------------------------------------------------------------- 1 | # `make({ ... })` 2 | 3 | The make method will "make" a new form when used on the underlying class (With the proxy used on all forms) 4 | 5 | ```js 6 | import { VueForm } from 'vuejs-form' 7 | 8 | const ExampleForm = VueForm.make({ id: '', name: 'sarah', email: 'sarah@gmail.com' }) 9 | ExampleForm.all() // { id: '', name: 'sarah', email: 'sarah@gmail.com' } 10 | ``` 11 | 12 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/make.js) 13 | -------------------------------------------------------------------------------- /docs/api/missing.md: -------------------------------------------------------------------------------- 1 | # `missing(propertyOne, propertyTwo, ...)` 2 | 3 | The missing method will determine if the form is missing the following properties 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }) 7 | 8 | ExampleForm.missing('id') // false 9 | ExampleForm.missing('something') // true 10 | ExampleForm.missing('name', 'email') // false 11 | ExampleForm.missing('name', 'email', 'something') // true 12 | ``` 13 | 14 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/missing.js) 15 | -------------------------------------------------------------------------------- /docs/api/only.md: -------------------------------------------------------------------------------- 1 | # `only(propertyOne, propertyTwo, ...)` 2 | 3 | The only method will return an object of "only" the input properties you defined 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }) 7 | 8 | ExampleForm.only('name', 'email') // { name: 'sarah', email: 'sarah@gmail.com' } 9 | ExampleForm.only('id', 'name') // { id: '', name: 'sarah' } 10 | ExampleForm.only('id') // { id: '' } 11 | ``` 12 | 13 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/only.js) 14 | -------------------------------------------------------------------------------- /docs/api/set.md: -------------------------------------------------------------------------------- 1 | # `set({ key: value, keyTwo: valueTwo, etc... })` 2 | 3 | The set method allows you to set new and override previous values: 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.set({ 9 | id: 2, 10 | name: 'tim', 11 | email: 'tim@gmail.com', 12 | password: 'secret', 13 | }) 14 | 15 | ExampleForm.all() 16 | // { id: 2, name: 'tim', email: 'tim@gmail.com', password: 'secret' } 17 | ``` 18 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/set.js) 19 | -------------------------------------------------------------------------------- /docs/api/toArray.md: -------------------------------------------------------------------------------- 1 | # `toArray()` 2 | 3 | The toArray method transforms the input into an array of key value pair objects: 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.toArray() 9 | /** 10 | [ 11 | { key: 'id', value: '' }, 12 | { key: 'name', value: 'sarah' }, 13 | { key: 'email', value: 'sarah@gmail.com' } 14 | ] 15 | */ 16 | 17 | ``` 18 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/toArray.js) 19 | -------------------------------------------------------------------------------- /docs/api/wrap.md: -------------------------------------------------------------------------------- 1 | # `wrap(key)` 2 | 3 | The wrap method allows you to wrap the input within a given object key: 4 | 5 | ```js 6 | const ExampleForm = form({ id: '', name: 'sarah', email: 'sarah@gmail.com' }); 7 | 8 | ExampleForm.wrap('data') 9 | /** 10 | { 11 | data: { 12 | id: '', 13 | name: 'sarah', 14 | email: 'sarah@gmail.com' 15 | } 16 | } 17 | */ 18 | 19 | ``` 20 | [View source on GitHub](https://github.com/zhorton34/vuejs-form.js/blob/master/src/methods/wrap.js) 21 | -------------------------------------------------------------------------------- /mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/dist/index.js": "/dist/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuejs-form", 3 | "version": "1.3.0", 4 | "description": "Form wrapper simplifying developer ability to access, check, fill, and pass input data", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "all": "npm run build && npm test", 8 | "pretest": "npm run transpile", 9 | "test": "mocha test/tests.js", 10 | "transpile": "babel src --quiet --out-dir dist", 11 | "readme": "node bundler/bundle.js", 12 | "bundle": "webpack-cli dist/index.js --output build/vuejs-form.js --mode development --output-library form", 13 | "uglify": "uglifyjs build/vuejs-form.js --compress --mangle --output build/vuejs-form.min.js", 14 | "build": "npm run transpile && npm run bundle && npm run uglify && npm run readme", 15 | "coverage": "npm run transpile && nyc mocha test/tests.js", 16 | "reporter": "nyc report --reporter=html", 17 | "docs:dev": "vuepress dev docs", 18 | "docs:build": "vuepress build docs", 19 | "prepublishOnly": "npm run all" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/zhorton34/vuejs-form.git" 24 | }, 25 | "keywords": [ 26 | "vue form", 27 | "form", 28 | "vue", 29 | "vuejs form", 30 | "vuejs forms", 31 | "form vuejs", 32 | "vue forms package", 33 | "vue form input", 34 | "vue validation", 35 | "vuejs validators", 36 | "validation with vuejs", 37 | "validators", 38 | "data validation", 39 | "javascript validation", 40 | "vue validators", 41 | "reactive vue validation", 42 | "vue rules", 43 | "validator javascript", 44 | "that vue validator", 45 | "javascript validation rules", 46 | "vuejs validation messages", 47 | "vue js validation rules", 48 | "simple vuejs validators" 49 | ], 50 | "author": "Zachary Horton", 51 | "license": "MIT", 52 | "babel": { 53 | "presets": [ 54 | "@babel/preset-env" 55 | ] 56 | }, 57 | "bugs": { 58 | "url": "https://github.com/zhorton34/vuejs-form/issues" 59 | }, 60 | "homepage": "https://github.com/zhorton34/vuejs-form#readme", 61 | "devDependencies": { 62 | "@babel/cli": "^7.2.3", 63 | "@babel/core": "^7.2.2", 64 | "@babel/polyfill": "^7.8.3", 65 | "@babel/preset-env": "^7.9.6", 66 | "benchmark": "^2.1.0", 67 | "chai": "^4.1.2", 68 | "hoax.js": "^1.0.0", 69 | "mocha": "^3.5.2", 70 | "nyc": "^11.0.2", 71 | "uglify-js": "^3.2.2", 72 | "vuejs-validators": "^1.1.7", 73 | "webpack": "^4.43.0", 74 | "webpack-cli": "^3.3.11" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/helpers/accessor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Get value of a nested property 5 | * 6 | * @param form 7 | * @returns {*} 8 | */ 9 | module.exports = function access(form) { 10 | return new Proxy(form, { 11 | get (target, key){ 12 | if(Object.keys(target.data).includes(key)) { 13 | 14 | if(!Object.keys(target).includes(key)) { 15 | target[key] = null; // Initialize an empty key if the property does not exist. 16 | } 17 | 18 | return target.data[key]; 19 | } 20 | 21 | return target[key] 22 | }, 23 | 24 | set (target, key, value) { 25 | target.data[key] = value; 26 | 27 | return target.data[key] === value; 28 | } 29 | }) 30 | }; 31 | -------------------------------------------------------------------------------- /src/helpers/dataGet.js: -------------------------------------------------------------------------------- 1 | const isObject = check => typeof check === 'object' && Array.isArray(check) === false; 2 | const isArray = check => Array.isArray(check) === true; 3 | 4 | /** 5 | * Get Nested Data With An Optional "*" wildcard 6 | */ 7 | module.exports = function (target, path = '') { 8 | if (path === '') { 9 | return target; 10 | } 11 | 12 | path = Array.isArray(path) ? path : path.split('.'); 13 | 14 | return path.reduce((value, segment) => { 15 | if (segment === '*') { 16 | if (isArray(value)) { 17 | return value.reduce((list, item) => [...list, item], []) 18 | } else if (isObject(value)) { 19 | return Object.values(value); 20 | } else { 21 | return value 22 | } 23 | } 24 | 25 | if (isArray(value)) { 26 | return value.reduce((list, item) => { 27 | if (isObject(item)) { 28 | return Object.keys(item).includes(segment) ? [...list, item[segment]] : [...list]; 29 | } else if (!isObject(item) && isArray(item)) { 30 | return [...list, item[segment]] 31 | } else { 32 | return [...list, item] 33 | } 34 | }, []); 35 | } 36 | 37 | if (isObject(value)) { 38 | return value[segment] 39 | } 40 | 41 | return value[segment] || value || null; 42 | 43 | }, target) 44 | }; 45 | -------------------------------------------------------------------------------- /src/helpers/dataSet.js: -------------------------------------------------------------------------------- 1 | const example = { 2 | family: { 3 | mom: { name: '' } 4 | } 5 | }; 6 | const dataSet = function(target, value, path = '') { 7 | if (path === '') return { ...target, ...value }; 8 | 9 | path = Array.isArray(path) ? path : path.split('.'); 10 | // 11 | // if (!path.includes('*')) { 12 | // target[path] = value; 13 | // 14 | // return target; 15 | // } 16 | 17 | 18 | const types = ['wildcard', 'object', 'array', 'value', 'empty']; 19 | 20 | const iterator = (path, loop) => ({ 21 | loop, 22 | last: loop.length === path.length, 23 | prev: loop === 0 ? false : loop - 1, 24 | next: loop === path.length ? false : loop + 1, 25 | left: path.length - loop 26 | }); 27 | 28 | const resolve = (target, path, iteration) => ({ 29 | wildcard: path[iteration.loop] === '*', 30 | prevWildcard: iteration.prev ? path[iteration.prev] === '*' : false, 31 | nextWildcard: iteration.next ? path[iteration.next] === '*' : false, 32 | target, 33 | path, 34 | }); 35 | const structure = (target, path, value, segment, iterator, mapping) => ({ 36 | mapping, 37 | path: target[path[iterator.loop]], 38 | prev: iterator.prev ? resolve(target, path, iterator) : false, 39 | next: iterator.next ? resolve(target, path, iterator) : false, 40 | }); 41 | 42 | let loop = 0; 43 | let structures = []; 44 | path.forEach(segment => { 45 | structures[loop] = structure(target, path, value, segment, iterator(path, loop), structures); 46 | 47 | loop = loop+1; 48 | }); 49 | 50 | return structures; 51 | 52 | }; 53 | 54 | dataSet(example, value, 'family.mom.name'); 55 | -------------------------------------------------------------------------------- /src/helpers/exists.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Determine if a key value pair is missing 5 | * 6 | * @param value 7 | * @returns boolean 8 | */ 9 | module.exports = function (value) { 10 | return typeof value !== "undefined"; 11 | }; 12 | -------------------------------------------------------------------------------- /src/helpers/fieldsOf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const variadic = require('./variadic'); 3 | 4 | module.exports = function (form, keys) { 5 | const properties = variadic(keys); 6 | 7 | return properties.length > 0 8 | ? { 9 | has: () => properties, 10 | toArray: () => Array.isArray(properties) ? properties : Array.from(properties), 11 | isEmpty: () => Array.isArray(properties) ? properties.length === 0 : Array.from(properties).length === 0 12 | } 13 | : { 14 | has: () => form.keys(), 15 | toArray: () => form.keys(), 16 | isEmpty: () => form.keys().length === 0 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/helpers/isEmpty.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Determine if a value is empty 5 | * 6 | * @param value 7 | * @returns boolean 8 | */ 9 | module.exports = function (value) { 10 | if (value === null || value === '') return true; 11 | if (Array.isArray(value)) return value.length === 0; 12 | for (const key in value) if (Object.keys(value, key)) return false; 13 | return true; 14 | }; 15 | -------------------------------------------------------------------------------- /src/helpers/nestedValue.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Get value of a nested property 5 | * 6 | * @param mainObject 7 | * @param key 8 | * @returns {*} 9 | */ 10 | module.exports = function nestedValue(mainObject, key) { 11 | try { 12 | return key.split('.').reduce((obj, property) => obj[property], mainObject); 13 | } catch (err) { 14 | return null; 15 | } 16 | }; -------------------------------------------------------------------------------- /src/helpers/variadic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Variadic helper function 5 | * 6 | * @param args 7 | * @returns {*} 8 | */ 9 | module.exports = function variadic(args) { 10 | if (Array.isArray(args[0])) { 11 | return args[0]; 12 | } 13 | 14 | return args; 15 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function VueForm(input) { 4 | if (input !== undefined && !Array.isArray(input) && typeof input !== 'object') { 5 | this.data = [input]; 6 | } else if (input instanceof this.constructor) { 7 | this.data = input.all(); 8 | } else { 9 | this.data = input || {}; 10 | } 11 | } 12 | 13 | VueForm.prototype.anyFilled = require('./methods/anyFilled'); 14 | VueForm.prototype.all = require('./methods/all'); 15 | VueForm.prototype.boolean = require('./methods/boolean'); 16 | VueForm.prototype.empty = require('./methods/empty'); 17 | VueForm.prototype.except = require('./methods/except'); 18 | VueForm.prototype.extend = require('./methods/extend'); 19 | VueForm.prototype.fill = require('./methods/fill'); 20 | VueForm.prototype.filled = require('./methods/filled'); 21 | VueForm.prototype.forget = require('./methods/forget'); 22 | VueForm.prototype.has = require('./methods/has'); 23 | VueForm.prototype.hasAny = require('./methods/hasAny'); 24 | VueForm.prototype.input = require('./methods/input'); 25 | VueForm.prototype.keys = require('./methods/keys'); 26 | VueForm.prototype.make = require('./methods/make'); 27 | VueForm.prototype.missing = require('./methods/missing'); 28 | VueForm.prototype.only = require('./methods/only'); 29 | VueForm.prototype.set = require('./methods/set'); 30 | VueForm.prototype.toArray = require('./methods/toArray'); 31 | VueForm.prototype.wrap = require('./methods/wrap'); 32 | VueForm.prototype.localMacro = require('./methods/localMacro'); 33 | VueForm.prototype.forceLocalMacro = require('./methods/forceLocalMacro'); 34 | VueForm.prototype.macro = require('./methods/macro'); 35 | VueForm.prototype.forceMacro = require('./methods/forceMacro'); 36 | VueForm.prototype.proxy = require('./methods/proxy'); 37 | VueForm.prototype.build = require('./methods/build'); 38 | VueForm.prototype.use = require('./methods/use'); 39 | 40 | const form = function (dataOrValidatable, data) { 41 | if (typeof data === "undefined") { 42 | return (new VueForm(dataOrValidatable)).proxy(); 43 | } 44 | else if (typeof dataOrValidatable === 'function') { 45 | return (new VueForm(data)).use(dataOrValidatable, {}).proxy(); 46 | } 47 | else if (typeof dataOrValidatable !== 'function' && typeof data !== "undefined") { 48 | console.error(`form(validatable, data): validatable must be an instance of vuejs-validators: See vuejs-form Docs`); 49 | console.log(`vuejs-form has a powerful, optional, validation library. vuejs-validators`); 50 | console.log(`vuejs-validators exports a validator function`); 51 | console.log('vuejs-validators docs: https://github.com/zhorton34/vuejs-validators'); 52 | console.log('vuejs-forms docs: https://github.com/zhorton34/vuejs-form'); 53 | console.log( 54 | 'If you are trying make your vuejs-form data "validatable": ', 55 | '1: Install vuejs-validators', 56 | '2: Pass in vuejs-validators "validation" instance as the 1st parameter and the form data as the 2nd parameter (Ex: "form(validator, data)")' 57 | ); 58 | console.log("-----------------"); 59 | console.log( 60 | 'To create a vuejs-form that is NOT "validatable" simply:', 61 | '1: Omit the second parameter', 62 | '2: Pass in data as the first parameter', 63 | '2: Non Validatable Form Example: form({ name: "sarah", email: "sarah.smith@gmail.com" })', 64 | ); 65 | } 66 | 67 | return (new VueForm(data)).proxy(); 68 | }; 69 | 70 | const validatable = require('vuejs-validators'); 71 | const { MessageBag, MessageBagFactory } = require('vuejs-validators'); 72 | 73 | 74 | const ValidatableForm = function ValidatableForm(data = {}) { 75 | return form(validatable, data); 76 | }; 77 | 78 | module.exports = ValidatableForm; 79 | module.exports.VueForm = VueForm; 80 | module.exports.SimpleForm = form; 81 | module.exports.validator = validatable; 82 | module.exports.default = ValidatableForm; 83 | module.exports.ValidatableForm = ValidatableForm; 84 | 85 | module.exports.MessageBag = MessageBag; 86 | module.exports.MessageBagFactory = MessageBagFactory; 87 | 88 | -------------------------------------------------------------------------------- /src/methods/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function all() { 4 | return this.data; 5 | }; -------------------------------------------------------------------------------- /src/methods/anyFilled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dataGet = require('../helpers/dataGet'); 4 | const isEmpty = require('../helpers/isEmpty'); 5 | const fieldsOf = require('../helpers/fieldsOf'); 6 | 7 | module.exports = function anyFilled(...args) { 8 | const valueFilled = key => isEmpty(dataGet(this.data, key)) === false; 9 | 10 | return fieldsOf(this, args).has().some(valueFilled); 11 | }; 12 | -------------------------------------------------------------------------------- /src/methods/boolean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dataGet = require('../helpers/dataGet.js'); 4 | 5 | module.exports = function boolean(property) { 6 | const truthy = [1, "1", true, "true", "on", "yes"]; 7 | 8 | return truthy.includes(dataGet(this.data, property)); 9 | }; 10 | -------------------------------------------------------------------------------- /src/methods/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const accessor = require('../helpers/accessor.js'); 4 | 5 | module.exports = function build() { 6 | return accessor(this); 7 | }; 8 | -------------------------------------------------------------------------------- /src/methods/empty.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isEmpty = require('../helpers/isEmpty'); 4 | const dataGet = require('../helpers/dataGet'); 5 | const fieldsOf = require('../helpers/fieldsOf.js'); 6 | 7 | module.exports = function empty(...properties) { 8 | const emptyValue = key => isEmpty(dataGet(this.data, key)); 9 | 10 | return fieldsOf(this, properties).has().some(emptyValue); 11 | }; 12 | -------------------------------------------------------------------------------- /src/methods/except.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const variadic = require('../helpers/variadic.js'); 4 | 5 | module.exports = function except(...args) { 6 | const properties = variadic(args); 7 | 8 | return Object.keys(this.data) 9 | .filter(property => !properties.includes(property)) 10 | .reduce((only, field) => ({ [field]: this.data[field], ...only }), {}); 11 | }; 12 | -------------------------------------------------------------------------------- /src/methods/extend.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function extend(name, callback) { 4 | if (Object.keys(this.constructor.prototype).includes(name)) { 5 | return console.error(`Cant extend form with method ${name}, it already exists`); 6 | } 7 | 8 | this.constructor.prototype[name] = callback; 9 | }; 10 | -------------------------------------------------------------------------------- /src/methods/fill.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function fill(input = {}, value) { 4 | 5 | if (typeof value === "undefined") { 6 | Object.entries(input).forEach(([key, value]) => { 7 | if (this.empty(key)) { 8 | this.data[key] = value; 9 | } 10 | }); 11 | } else if (this.empty(input)) { 12 | this.data[input] = value; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/methods/filled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isEmpty = require('../helpers/isEmpty'); 4 | const dataGet = require('../helpers/dataGet'); 5 | const fieldsOf = require('../helpers/fieldsOf'); 6 | 7 | module.exports = function filled(...args) { 8 | const valueFilled = key => isEmpty(dataGet(this.data, key)) === false; 9 | 10 | return fieldsOf(this, args).has().every(valueFilled); 11 | }; 12 | -------------------------------------------------------------------------------- /src/methods/forceLocalMacro.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | /** 5 | * Similar to "localMacro" function, with ability to forcibly overwrite base prototype methods and existing macro methods (On Local Instance instead of globally) 6 | * (See Laravel Macros For In Depth Explanation) 7 | * 8 | * @param name 9 | * @param fn 10 | * @return void 11 | */ 12 | module.exports = function forceMacro(name, fn) { 13 | console.warn(` 14 | Forcing macro to implement ${name} method -- Move forward with caution. 15 | We recommend using "macro" in place of "forceMacro" method if you do not 16 | need to override pre-existing or base behavior. 17 | `); 18 | 19 | this[name] = fn; 20 | }; 21 | -------------------------------------------------------------------------------- /src/methods/forceMacro.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | /** 5 | * Similar to "macro" function, with ability to forcibly overwrite base prototype methods and existing macro methods 6 | * (See Laravel Macros For In Depth Explanation) 7 | * 8 | * @param name 9 | * @param fn 10 | * @return void 11 | */ 12 | module.exports = function forceMacro(name, fn) { 13 | console.warn(` 14 | Forcing macro to implement ${name} method -- Move forward with caution. 15 | We recommend using "macro" in place of "forceMacro" method if you do not 16 | need to override pre-existing or base behavior. 17 | `); 18 | 19 | this[name] = fn; 20 | this.constructor.prototype[name] = fn; 21 | }; 22 | -------------------------------------------------------------------------------- /src/methods/forget.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | const fieldsFrom = require('../helpers/fieldsOf.js'); 5 | 6 | module.exports = function forget(...list) { 7 | const fields = fieldsFrom(this, list); 8 | 9 | this.data = fields.isEmpty() ? {} : this.except(...fields.toArray()); 10 | }; 11 | -------------------------------------------------------------------------------- /src/methods/has.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dataGet = require('../helpers/dataGet'); 4 | const fieldsOf = require('../helpers/fieldsOf'); 5 | 6 | module.exports = function has(...args) { 7 | const value = key => dataGet(this.data, key); 8 | 9 | return fieldsOf(this, args).has().every(value); 10 | }; 11 | -------------------------------------------------------------------------------- /src/methods/hasAny.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dataGet = require('../helpers/dataGet'); 4 | const fieldsOf = require('../helpers/fieldsOf'); 5 | 6 | module.exports = function hasAny(...args) { 7 | const value = key => dataGet(this.data, key); 8 | 9 | return fieldsOf(this, args).has().some(value); 10 | }; 11 | -------------------------------------------------------------------------------- /src/methods/input.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const isEmpty = require('../helpers/isEmpty.js'); 4 | const dataGet = require('../helpers/dataGet.js'); 5 | 6 | module.exports = function input(key, defaultValue = false) { 7 | const value = dataGet(this.data, key); 8 | 9 | return isEmpty(value) ? defaultValue : value; 10 | }; 11 | -------------------------------------------------------------------------------- /src/methods/keys.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function keys() { 4 | return Object.keys(this.data) 5 | }; 6 | -------------------------------------------------------------------------------- /src/methods/localMacro.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | /** 5 | * Add/Extend Form Functionality On Specific Instance With Custom Methods Using LocalMacro 6 | * 7 | * @param name 8 | * @param fn 9 | */ 10 | module.exports = function localMacro(name, fn) { 11 | if (typeof this.constructor.prototype[name] !== 'undefined' || typeof this[name] !== 'undefined') { 12 | console.error(`Cant extend form with ${name} localMacro, it already exists (use forceLocalMacro if you want to forcibly overwrite base behavior or previously set macro/localMacro`); 13 | } else { 14 | this[name] = fn; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/methods/macro.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | /** 5 | * Add/Extend Form Functionality With Custom Methods Using Macros (globally) 6 | * 7 | * @param name 8 | * @param fn 9 | */ 10 | module.exports = function macro(name, fn) { 11 | if (Object.keys(this.constructor.prototype).includes(name)) { 12 | return console.error(`Cant extend form with ${name} macro, it already exists (use forceMacro if you want to forcibly overwrite base behavior or previously set macro`); 13 | } 14 | else { 15 | this[name] = fn; 16 | this.constructor.prototype[name] = fn; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/methods/make.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const accessor = require('../helpers/accessor'); 4 | 5 | module.exports = function make(input) { 6 | return accessor(new this.constructor(input)); 7 | }; 8 | -------------------------------------------------------------------------------- /src/methods/missing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const exists = require('../helpers/exists'); 4 | const dataGet = require('../helpers/dataGet'); 5 | const fieldsOf = require('../helpers/fieldsOf'); 6 | 7 | module.exports = function missing(...args) { 8 | const missing = key => !exists(dataGet(this.data, key)); 9 | 10 | return fieldsOf(this, args).has().some(missing); 11 | }; 12 | -------------------------------------------------------------------------------- /src/methods/only.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const variadic = require('../helpers/variadic'); 4 | 5 | module.exports = function only(...args) { 6 | const properties = variadic(args); 7 | 8 | return properties.reduce((only, field) => ({ 9 | [field]: this.data[field], 10 | ...only 11 | }), {}); 12 | }; 13 | -------------------------------------------------------------------------------- /src/methods/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const accessor = require('../helpers/accessor.js'); 4 | 5 | module.exports = function proxy() { 6 | return accessor(this); 7 | }; 8 | -------------------------------------------------------------------------------- /src/methods/set.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = function set(input = {}, value) { 5 | if (typeof value === "undefined") { 6 | Object.entries(input).forEach(([key, value]) => { 7 | this.data[key] = value; 8 | }); 9 | } else { 10 | this.data[input] = value; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/methods/toArray.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function toArray() { 4 | return Object.entries(this.data).map(([key, value]) => ({ key, value })) 5 | }; 6 | -------------------------------------------------------------------------------- /src/methods/use.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Use Validator (vuejs-validators) 5 | * 6 | * @param validatable 7 | * @param options 8 | * @returns {*} 9 | */ 10 | module.exports = function use(validatable, options = {}) { 11 | this.setValidator = function (rules = {}, messages = {}, translator = {}) { 12 | this.validatorInstance = validatable(this.data, rules, messages, translator); 13 | 14 | return this; 15 | }; 16 | 17 | this.hasValidator = function () { 18 | return typeof this.validatorInstance !== 'undefined'; 19 | }; 20 | 21 | this.rules = function (rules = {}) { 22 | if (this.validator()) { 23 | this.validator().setRules(rules); 24 | } else { 25 | this.validatorInstance = validatable(this.data, rules); 26 | } 27 | 28 | return this; 29 | }; 30 | 31 | this.messages = function (messages = {}, rules = {}) { 32 | if (this.hasValidator()) { 33 | this.validator().setMessages(messages); 34 | } else { 35 | this.validatorInstance = validatable(this.data, rules, messages); 36 | } 37 | 38 | return this; 39 | }; 40 | 41 | this.validate = function () { 42 | this.validator().setData(this.data); 43 | this.validator().validate(); 44 | 45 | return this; 46 | }; 47 | 48 | this.validator = function () { 49 | return this.validatorInstance; 50 | }; 51 | 52 | this.errors = function () { 53 | return this.validator().errors(); 54 | }; 55 | 56 | this.setValidator(options); 57 | 58 | return this; 59 | }; 60 | -------------------------------------------------------------------------------- /src/methods/wrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function wrap(key) { 4 | return key.split('.').reverse().reduce((payload, property) => ({ 5 | [property]: { ...payload } 6 | }), this.data); 7 | }; 8 | -------------------------------------------------------------------------------- /test/methods/accessor_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const accessor = require('../../src/helpers/accessor.js'); 4 | 5 | module.exports = (it, expect, form) => { 6 | it('should return true given the accessor proxy properly accesses form.data[name] value via form[name]', () => { 7 | let example = accessor({ data: { name: 'sam' } }); 8 | 9 | expect(example.name).to.eql('sam'); 10 | }); 11 | 12 | it('should return false given the accessor proxy is not used and form.data[name] value does not equal form[name]', () => { 13 | let example = { data: { name: 'sam' } }; 14 | 15 | expect(example.name === 'sam').to.eql(false); 16 | }); 17 | 18 | it('should return true for proxy successfully working when adding a form without allowing form.data[name] value access via form[name]', () => { 19 | let example = form({ name: 'sam' }); 20 | 21 | expect(example.name).to.eql('sam'); 22 | }); 23 | 24 | it('should return true for proxy successfully working when adding a form validatable. Allowing form.data[name] value access via form[name]', () => { 25 | const Validator = function (data = {}, rules = {}, messages = {}, translator = {}) { 26 | this.data = data; 27 | this.rules = rules; 28 | this.messages = messages; 29 | this.translator = translator; 30 | }; 31 | 32 | const validatable = (data = {}, rules = {}, messages = {}, translator = {}) => (new Validator(data, rules, messages, translator)); 33 | 34 | let example = form(validatable, { name: 'sam' }); 35 | 36 | expect(example.name).to.eql('sam'); 37 | }); 38 | }; 39 | 40 | -------------------------------------------------------------------------------- /test/methods/all_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return all form input as an object', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).all()).to.eql({ 6 | name: 'example', 7 | email: 'example@gmail.com' 8 | }) 9 | }); 10 | }; 11 | -------------------------------------------------------------------------------- /test/methods/anyFilled_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return true when any key has a anyFilled value', () => { 5 | expect(form({ check: 'hey', two: '' }).anyFilled('check', 'two')).to.eql(true); 6 | expect(form({ check: ['hey'], two: [] }).anyFilled('check', 'two')).to.eql(true); 7 | expect(form({ check: { wow: 'hey' }, two: '' }).anyFilled('check', 'two')).to.eql(true); 8 | }); 9 | 10 | it('should return false when no key has a anyFilled value', () => { 11 | expect(form({ check: '', checkTwo: '' }).anyFilled('check', 'checkTwo')).to.eql(false); 12 | }); 13 | 14 | it('should return true when all keys have a anyFilled value', () => { 15 | expect(form({ check: 'fill', checkTwo: 'value' }).anyFilled('check', 'checkTwo')).to.eql(true); 16 | }); 17 | 18 | it('should return false when single key does not have a anyFilled value', () => { 19 | expect(form({ check: '' }).anyFilled('check')).to.eql(false); 20 | }); 21 | 22 | it('should return true when wild card is used to find key', () => { 23 | expect(form({ tags: [ { name: 'one' }, { name: 'two' } ] }).anyFilled('tags.*.name')).to.eql(true); 24 | }); 25 | 26 | it('should return false when wild card is used to find key', () => { 27 | expect(form({ tags: { list: [], category: 'example' } }).anyFilled('tags.list.*')).to.eql(false); 28 | }); 29 | 30 | it('should check all values when no arguments are provided', () => { 31 | expect(form({ one: '', two: '', three: '' }).anyFilled()).to.eql(false); 32 | expect(form({ one: 'first', two: '', three: '' }).anyFilled()).to.eql(true); 33 | expect(form({ one: '', two: ['second'], three: '' }).anyFilled()).to.eql(true); 34 | expect(form({ one: '', two: '', three: { third: 'test' } }).anyFilled()).to.eql(true); 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /test/methods/boolean_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return true for truthy values(true, "true", "yes", "on", 1, and "1")', () => { 5 | expect(form({ check: true }).boolean('check')).to.eql(true); 6 | expect(form({ check: 'true' }).boolean('check')).to.eql(true); 7 | expect(form({ check: 'yes' }).boolean('check')).to.eql(true); 8 | expect(form({ check: 'on' }).boolean('check')).to.eql(true); 9 | expect(form({ check: 1 }).boolean('check')).to.eql(true); 10 | expect(form({ check: '1' }).boolean('check')).to.eql(true); 11 | }); 12 | 13 | it('should return false for not truthy values(true, "true", "yes", "on", 1, and "1")', () => { 14 | expect(form({ check: false }).boolean('check')).to.eql(false); 15 | expect(form({ check: 'random' }).boolean('check')).to.eql(false); 16 | expect(form({ check: 0 }).boolean('check')).to.eql(false); 17 | expect(form({ check: [] }).boolean('check.0')).to.eql(false); 18 | expect(form({}).boolean('check')).to.eql(false); 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /test/methods/empty_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return true when string, array, or object is empty', () => { 5 | expect(form({ one: '', two: [], three: {} }).empty('one', 'two', 'three')).to.eql(true); 6 | }); 7 | 8 | it('should return true when some are filled but an empty exists', () => { 9 | expect(form({ one: '', two: 'hey', three: 'world' }).empty('one', 'two', 'three')).to.eql(true); 10 | }); 11 | 12 | it('should return true when string, array, or object are empty', () => { 13 | expect(form({ one: 'hey', two: [], three: {} }).empty('one', 'two', 'three')).to.eql(true); 14 | expect(form({ one: '', two: ['yo'], three: {} }).empty('one', 'two', 'three')).to.eql(true); 15 | expect(form({ one: '', two: [], three: { hi: 'world' } }).empty('one', 'two', 'three')).to.eql(true); 16 | }); 17 | 18 | it('should return false when string, array, or object are filled', () => { 19 | expect(form({ one: 'hey' }).empty('one')).to.eql(false); 20 | expect(form({ two: ['yo'] }).empty('two')).to.eql(false); 21 | expect(form({ three: { hi: 'world' } }).empty('three')).to.eql(false); 22 | }); 23 | 24 | it('should check all values when no arguments are provided', () => { 25 | expect(form({ one: '', two: '', three: '' }).empty()).to.eql(true); 26 | expect(form({ one: 'first', two: '', three: '' }).empty()).to.eql(true); 27 | expect(form({ one: '', two: ['second'], three: '' }).empty()).to.eql(true); 28 | expect(form({ one: '', two: '', three: { third: 'test' } }).empty()).to.eql(true); 29 | expect(form({ one: 'first', two: ['second'], three: { third: 'test' } }).empty()).to.eql(false); 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /test/methods/except_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return all except the key/values where the pairs key lines up with the passed in keys', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).except('name')).to.eql({ 6 | email: 'example@gmail.com' 7 | }); 8 | 9 | expect(form({ id: 1, name: 'example', email: 'example@gmail.com' }) 10 | .except('name', 'email')).to.eql({ id: 1 }); 11 | }) 12 | }; 13 | -------------------------------------------------------------------------------- /test/methods/fill_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should fill in input data but not override previous non empty data', () => { 5 | const example = form({ id: null, email: 'example@gmail.com' }); 6 | 7 | expect(example.all()).to.eql({ id: null, email: 'example@gmail.com' }); 8 | 9 | example.fill({ id: 1, email: 'hey@g.com' }); 10 | 11 | expect(example.all()).to.eql({ id: 1, email: 'example@gmail.com' }); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /test/methods/filled_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return true when key has a filled value', () => { 5 | expect(form({ check: 'hey' }).filled('check')).to.eql(true); 6 | expect(form({ check: ['hey'] }).filled('check')).to.eql(true); 7 | expect(form({ check: { wow: 'hey' } }).filled('check')).to.eql(true); 8 | }); 9 | 10 | it('should return false when any key does not have a filled value', () => { 11 | expect(form({ check: '', checkTwo: 'value' }).filled('check', 'checkTwo')).to.eql(false); 12 | }); 13 | 14 | it('should return true when all keys have a filled value', () => { 15 | expect(form({ check: 'fill', checkTwo: 'value' }).filled('check', 'checkTwo')).to.eql(true); 16 | }); 17 | it('should return false when single key does not have a filled value', () => { 18 | expect(form({ check: '' }).filled('check')).to.eql(false); 19 | }); 20 | 21 | it('should check all are filled when no arguments are provided', () => { 22 | expect(form({ one: '', two: '', three: '' }).filled()).to.eql(false); 23 | expect(form({ one: 'first', two: '', three: '' }).filled()).to.eql(false); 24 | expect(form({ one: '', two: ['second'], three: '' }).filled()).to.eql(false); 25 | expect(form({ one: '', two: '', three: { third: 'test' } }).filled()).to.eql(false); 26 | expect(form({ one: 'first', two: ['second'], three: { third: 'test' } }).filled()).to.eql(true); 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /test/methods/forceLocal_macro.test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = (it, expect, form) => { 5 | it('should be able to extend the instance of form prototype using "forceLocalMacro"', () => { 6 | let example = form(); 7 | expect(Object.keys(example).includes('example_force_macro_method')).to.eql(false); 8 | 9 | example.forceLocalMacro('example_force_local_macro', function () { 10 | return 'hello world'; 11 | }); 12 | 13 | expect(example.example_force_local_macro()).to.eql('hello world'); 14 | expect(typeof form({}).example_force_local_macro === 'undefined').to.eql(true); 15 | }); 16 | 17 | it('should be able to forcibly overwrite a base form prototype function using "forceMacro"', () => { 18 | 19 | let example = form({ name: 'sam' }); 20 | expect(example.all()).to.eql({ name: 'sam' }); 21 | 22 | example.forceLocalMacro('all', function () { 23 | return 'hello world'; 24 | }); 25 | 26 | expect(example.all()).to.eql('hello world'); 27 | expect(form({ name: 'sam' }).all()).to.eql({ name: 'sam' }); 28 | }); 29 | 30 | it('should be able to forcibly overwrite a previously defined "macro"', () => { 31 | let example = form({ name: 'sam' }); 32 | 33 | example.localMacro('inspire', function () { 34 | return `We believe in you ${this.data.name}`; 35 | }); 36 | 37 | expect(example.inspire()).to.eql("We believe in you sam"); 38 | 39 | example.forceLocalMacro('inspire', function () { 40 | return `We have faith in you sarah` 41 | }); 42 | 43 | expect(example.inspire()).to.eql("We have faith in you sarah"); 44 | }); 45 | 46 | }; 47 | -------------------------------------------------------------------------------- /test/methods/forceMacro_test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = (it, expect, form) => { 5 | it('should be able to extend the base form prototype using "forceMacro"', () => { 6 | expect(Object.keys(form).includes('example_force_macro_method')).to.eql(false); 7 | 8 | form().forceMacro('example_force_macro_method', function () { 9 | return 'hello world'; 10 | }); 11 | 12 | expect(form().example_force_macro_method()).to.eql('hello world'); 13 | }); 14 | 15 | it('should be able to forcibly overwrite a base form prototype function using "forceMacro"', () => { 16 | 17 | expect(form({ name: 'sam' }).all()).to.eql({ name: 'sam' }); 18 | 19 | form().forceMacro('all', function () { 20 | return 'hello world'; 21 | }); 22 | 23 | expect(form({ name: 'sam' }).all()).to.eql('hello world'); 24 | 25 | form().forceMacro('all', function () { 26 | return this.data; 27 | }); 28 | 29 | expect(form({ name: 'sam' }).all()).to.eql({ name: 'sam' }); 30 | }); 31 | 32 | it('should be able to forcibly overwrite a previously defined "macro"', () => { 33 | let example = form({ name: 'sam' }); 34 | 35 | form().macro('inspire', function () { 36 | return `We believe in you ${this.data.name}`; 37 | }); 38 | 39 | expect(example.inspire()).to.eql("We believe in you sam"); 40 | 41 | form().forceMacro('inspire', function () { 42 | return `We have faith in you sarah` 43 | }); 44 | 45 | expect(example.inspire()).to.eql("We have faith in you sarah"); 46 | 47 | }); 48 | 49 | form().forceMacro('all', function () { 50 | return this.data; 51 | }); 52 | }; 53 | -------------------------------------------------------------------------------- /test/methods/forget_test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = (it, expect, form) => { 5 | it('should forget an input key value pair properly', () => { 6 | const example = form({ name: 'example', email: 'example@gmail.com' }); 7 | 8 | expect(example.all()).to.eql({ name: 'example', email: 'example@gmail.com' }); 9 | 10 | example.forget('name'); 11 | 12 | expect(example.all()).to.eql({ email: 'example@gmail.com' }); 13 | }); 14 | 15 | it('should forget multiple input key value pair properly', () => { 16 | const example = form({ id: 1, name: 'example', email: 'example@gmail.com' }); 17 | 18 | expect(example.all()).to.eql({ id: 1, name: 'example', email: 'example@gmail.com' }); 19 | 20 | example.forget('name', 'id'); 21 | 22 | expect(example.all()).to.eql({ email: 'example@gmail.com' }); 23 | }); 24 | 25 | it('should forget all input when no parameters are passed', () => { 26 | const example = form({ id: 1, name: 'example', email: 'example@gmail.com' }); 27 | 28 | expect(example.all()).to.eql({ id: 1, name: 'example', email: 'example@gmail.com' }); 29 | 30 | example.forget(); 31 | 32 | expect(example.all()).to.eql({}); 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /test/methods/hasAny_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return true if it has any of the given keys', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).hasAny('name', 'missing')).to.eql(true) 6 | }); 7 | 8 | it('should return false if its missing all of the given keys', () => { 9 | expect(form({ name: 'example', email: 'example@gmail.com' }).hasAny('something', 'missing')).to.eql(false) 10 | }); 11 | 12 | it('should return true or false based on values existing within wild card paths', () => { 13 | expect(form({ post: { 14 | meta: { tags: ['one', 'two', 'three'] }, 15 | }}).hasAny('post.meta.tags', 'something')).to.eql(true); 16 | 17 | expect(form({ post: { meta: { }, }}).hasAny('post.meta.tags')).to.eql(false) 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /test/methods/has_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return true if it has the given keys', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).has('name', 'email')).to.eql(true) 6 | }); 7 | 8 | it('should return false if it does not have any of the given keys', () => { 9 | expect(form({ name: 'example', email: 'example@gmail.com' }).has('name', 'email', 'missing')).to.eql(false) 10 | }); 11 | 12 | it('should return true or false based on values existing within wild card paths', () => { 13 | expect(form({ post: { 14 | meta: { tags: ['one', 'two', 'three'] }, 15 | }}).has('post.meta.tags')).to.eql(true); 16 | 17 | expect(form({ post: { meta: { }, }}).has('post.meta.tags')).to.eql(false) 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /test/methods/input_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should except a key and then return the value, if its missing then return the default, if there is no default then return false', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).input('name')).to.eql('example'); 6 | expect(form({ name: 'example', email: 'example@gmail.com' }).input('something', 'default')).to.eql('default'); 7 | expect(form({ name: 'example', email: 'example@gmail.com' }).input('something')).to.eql(false); 8 | }); 9 | 10 | it('accept a key with a wildcard and be able to use the default value if the given value is empty', () => { 11 | expect(form({ name: 'example', email: 'example@gmail.com' }).input('something', 'default')).to.eql('default'); 12 | expect(form({ name: 'example', email: 'example@gmail.com' }).input('something')).to.eql(false); 13 | 14 | expect(form({ family: { 15 | mom: { name: 'julie' }, dad: { name: 'rob' } 16 | } 17 | }).input('family.*.name')).to.eql(['julie', 'rob']); 18 | 19 | expect(form({ family: { 20 | mom: { name: 'julie' }, dad: { name: 'rob' } 21 | } 22 | }).input('family.*.email', ['julie@gmail.com', 'rob@gmail.com'])).to.eql(['julie@gmail.com', 'rob@gmail.com']); 23 | }); 24 | 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /test/methods/keys_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return an array of the input keys', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).keys()).to.eql(['name', 'email']) 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/methods/localMacro_test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = (it, expect, form) => { 5 | it('should be able to extend the base form prototype using a "macro"', () => { 6 | expect(typeof form({ email: 'e@g.com' }).example_local_macro_method === 'undefined').to.eql(true); 7 | 8 | form().macro('example_local_macro_method', function () { 9 | return 'hello world'; 10 | }); 11 | 12 | expect(typeof form({ email: 'e@g.com'}).example_local_macro_method === 'undefined').to.eql(false); 13 | expect(form().example_local_macro_method()).to.eql('hello world'); 14 | }); 15 | 16 | it('should not be able to forcibly overwrite a base form prototype function using a "macro"', () => { 17 | expect(form({ name: 'sam' }).all()).to.eql({ name: 'sam' }); 18 | 19 | form().macro('all', function () { 20 | return 'hello world'; 21 | }); 22 | 23 | expect(form({ name: 'sam' }).all()).to.eql({ name: 'sam' }); 24 | }); 25 | 26 | it('should not apply localMacro everywhere', () => { 27 | form().localMacro('everywhere', () => 'world'); 28 | 29 | let one = form({ first: 'test' }); 30 | let two = form({ second: 'test' }); 31 | 32 | expect(typeof one.everywhere === 'undefined').to.eql(true); 33 | expect(typeof two.everywhere === 'undefined').to.eql(true); 34 | 35 | one.localMacro('everywhere', () => 'world'); 36 | 37 | expect(one.everywhere()).to.eql('world'); 38 | expect(typeof two.everywhere === 'undefined').to.eql(true); 39 | }); 40 | 41 | it('should allow macros to be applied on a specific instance instead of everywhere', () => { 42 | let does_have = form({ first: 'first' }); 43 | let does_not_have = form({ second: 'test' }); 44 | 45 | does_have.localMacro('localized', () => 'success'); 46 | 47 | expect(typeof does_have.localized === 'undefined').to.eql(false); 48 | expect(typeof does_not_have.localized === 'undefined').to.eql(true); 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /test/methods/macro_test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = (it, expect, form) => { 5 | it('should be able to extend base prototype using a "macro"', () => { 6 | expect(typeof form().example_macro_method === 'undefined').to.eql(true); 7 | 8 | form({}).macro('example_macro_method', function () { 9 | return 'hello world'; 10 | }); 11 | 12 | expect(typeof form({}).example_macro_method === 'undefined').to.eql(false); 13 | 14 | expect(form({}).example_macro_method()).to.eql('hello world'); 15 | }); 16 | 17 | it('should not be able to force overwrite on form prototype function using a "macro"', () => { 18 | expect(form({ name: 'sam' }).all()).to.eql({ name: 'sam' }); 19 | 20 | form().macro('all', function () { 21 | return 'hello world'; 22 | }); 23 | 24 | expect(form({ name: 'sam' }).all()).to.eql({ name: 'sam' }); 25 | }); 26 | 27 | it('should apply macros everywhere or on every prototype instance created by default', () => { 28 | form().macro('macro_everywhere', () => 'world'); 29 | 30 | let one = form({ first: 'test' }); 31 | let two = form({ second: 'test' }); 32 | 33 | expect(one.macro_everywhere()).to.eql('world'); 34 | expect(two.macro_everywhere()).to.eql('world'); 35 | 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /test/methods/missing_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should be true if it is missing from the input', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).missing('something')).to.eql(true) 6 | }); 7 | it('should be false if it is not missing from the input (even if empty)', () => { 8 | expect(form({ name: 'example', email: '' }).missing('email')).to.eql(false) 9 | }); 10 | it('should be true if any are missing from the input', () => { 11 | expect(form({ name: 'example', email: '' }).missing('email', 'something')).to.eql(true) 12 | }); 13 | it('should be false if all are in the input', () => { 14 | expect(form({ name: 'example', email: '' }).missing('name', 'email')).to.eql(false) 15 | }) 16 | }; 17 | -------------------------------------------------------------------------------- /test/methods/only_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return only the key/values where the pairs line up with the passed in keys', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).only('email')).to.eql({ 6 | email: 'example@gmail.com' 7 | }); 8 | 9 | expect(form({ id: 1, name: 'example', email: 'example@gmail.com' }).only('id', 'email')).to.eql({ 10 | id: 1, email: 'example@gmail.com' 11 | }); 12 | }) 13 | }; 14 | -------------------------------------------------------------------------------- /test/methods/set_test.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = (it, expect, form) => { 5 | it('should set input data and override previous non empty data', () => { 6 | const example = form({ id: null, email: 'example@gmail.com' }); 7 | 8 | expect(example.all()).to.eql({ id: null, email: 'example@gmail.com' }); 9 | 10 | example.set({ id: 1, email: 'hey@g.com' }); 11 | 12 | expect(example.all()).to.eql({ id: 1, email: 'hey@g.com' }); 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /test/methods/toArray_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return an array of key value pair objects', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).toArray()).to.eql( 6 | [ 7 | { key: 'name', value: 'example' }, 8 | { key: 'email', value: 'example@gmail.com' } 9 | ] 10 | ) 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /test/methods/use_test.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (it, expect, form) => { 3 | const Validator = function (data, rules, messages, translator) { 4 | this.data = data; 5 | this.rules = rules; 6 | this.confirm = 'success'; 7 | this.messages = messages; 8 | this.translator = translator; 9 | this.errorBag = { 10 | data: { 11 | a: ['one', 'two'], 12 | b: ['three'] 13 | }, 14 | }; 15 | 16 | this.errorBag.all = () => this.errorBag.data; 17 | this.errorBag.list = () => ['one', 'two', 'three']; 18 | }; 19 | 20 | Validator.prototype.validate = function () { 21 | this.errorBag.data = { 22 | one: ['a', 'b', 'c', 'd'], 23 | two: ['e', 'f', 'g', 'h'] 24 | }; 25 | 26 | this.errorBag.all = () => this.errorBag.data; 27 | this.errorBag.list = () => ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; 28 | 29 | return this; 30 | }; 31 | 32 | Validator.prototype.errors = function () { 33 | return this.errorBag; 34 | }; 35 | 36 | Validator.prototype.setRules = function (rules) { 37 | this.rules = rules; 38 | 39 | return this; 40 | }; 41 | 42 | Validator.prototype.setMessages = function (messages) { 43 | this.messages = messages; 44 | 45 | return this; 46 | }; 47 | 48 | Validator.prototype.setData = function (data) { 49 | this.data = data; 50 | 51 | return this; 52 | }; 53 | 54 | const validatable = (data = {}, rules = {}, messages = {}, translator = {}) => (new Validator(data, rules, messages, translator)); 55 | 56 | it('should "use" validatable instance and be able to get "validator"', () => { 57 | let example = form(validatable, { name: 'sam' }); 58 | 59 | expect(example.validator().data).to.eql({ name: 'sam' }); 60 | }); 61 | 62 | it('should "use" validatable instance and confirm it "hasValidator"', () => { 63 | let example = form(validatable, { name: 'sam' }); 64 | 65 | expect(example.hasValidator()).to.eql(true); 66 | }); 67 | 68 | it('should "use" validatable and set "rules"', () => { 69 | let example = form(validatable, { name: 'sam' }).rules({ 70 | 'name': 'required|min:4' 71 | }); 72 | 73 | expect(example.validator().rules).to.eql({ 'name': 'required|min:4' }); 74 | }); 75 | 76 | it('should "use" validatable and set "messages"', () => { 77 | let example = form(validatable, { name: 'sam' }).rules({ 78 | 'name': 'required|min:4' 79 | }).messages({ 80 | 'name.required': 'name is required', 81 | 'name.min': 'name must not be less than :min characters' 82 | }); 83 | 84 | expect(example.validator().messages).to.eql({ 85 | 'name.required': 'name is required', 86 | 'name.min': 'name must not be less than :min characters' 87 | }); 88 | }); 89 | 90 | it('should "use" validatable, with ability to "validate" and get "errors"', () => { 91 | let example = form(validatable, { name: 'sam' }).rules({ 92 | 'name': 'required|min:4' 93 | }).messages({ 94 | 'name.required': 'name is required', 95 | 'name.min': 'name must not be less than :min characters' 96 | }); 97 | 98 | expect(example.validate().errors().list()).to.eql([ 99 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' 100 | ]); 101 | }); 102 | 103 | it('should "use" validatable, with ability to "setValidator" a new', () => { 104 | let example = form(validatable, { name: 'sam' }).rules({ 105 | 'name': 'required|min:4' 106 | }).messages({ 107 | 'name.required': 'name is required', 108 | 'name.min': 'name must not be less than :min characters' 109 | }); 110 | 111 | example.setValidator({ 112 | 'name': 'required', 113 | }, { 114 | 'name.required': 'Updated Messages' 115 | }, { 116 | 'phrases': 'cool' 117 | }); 118 | 119 | expect(example.validator().rules).to.eql({ 120 | 'name': 'required', 121 | }); 122 | 123 | expect(example.validator().messages).to.eql({ 124 | 'name.required': 'Updated Messages' 125 | }); 126 | 127 | expect(example.validator().translator).to.eql({ 128 | 'phrases': 'cool' 129 | }); 130 | }); 131 | }; 132 | -------------------------------------------------------------------------------- /test/methods/wrap_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = (it, expect, form) => { 4 | it('should return data object wrapped or nested under the wrapped property key', () => { 5 | expect(form({ name: 'example', email: 'example@gmail.com' }).wrap('data')).to.eql({ 6 | data: { name: 'example', email: 'example@gmail.com' } 7 | }) 8 | }); 9 | 10 | it('should return data object wrapped or nested under nested property keys', () => { 11 | expect(form({ name: 'example', email: 'example@gmail.com' }).wrap('data.user.personal')).to.eql({ 12 | data: { 13 | user: { 14 | personal: { 15 | name: 'example', 16 | email: 'example@gmail.com' 17 | } 18 | } 19 | } 20 | }) 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const { it } = require('mocha'); 5 | const { describe } = require('mocha'); 6 | const path = require('path'); 7 | const { expect } = require('chai'); 8 | const { SimpleForm: form } = require('../dist'); 9 | 10 | let test = process.argv[process.argv.length - 1]; 11 | const runSingleTest = test.indexOf('--') !== -1; 12 | test = test.replace('--', ''); 13 | test += '_test.js'; 14 | 15 | const tests = fs.readdirSync(path.join(__dirname, 'methods')); 16 | const methods = fs.readdirSync(path.join(__dirname, '../src/methods')); 17 | 18 | tests.forEach((file) => { 19 | describe(file.replace('_test.js', '()'), () => { 20 | if (!runSingleTest) { 21 | // eslint-disable-next-line 22 | require(path.join(__dirname, 'methods', file))(it, expect, form); 23 | } else if (runSingleTest && file === test) { 24 | // eslint-disable-next-line 25 | require(path.join(__dirname, 'methods', file))(it, expect, form); 26 | } 27 | }); 28 | }); 29 | 30 | // if (!runSingleTest) { 31 | // describe('general tests', () => { 32 | // it('should test every method', () => { 33 | // const missingTests = collect(methods).diff(collect(tests).transform(t => t.replace(/_test/, ''))).all(); 34 | // expect(missingTests).to.eql([]); 35 | // }); 36 | 37 | // it('should document all methods in README.md', () => { 38 | // const content = fs.readFileSync(path.join(__dirname, '../README.md'), 'utf-8'); 39 | 40 | // const re = /#### `(.*)\(\)`/g; 41 | // let matches = re.exec(content); 42 | 43 | // const documentedMethods = []; 44 | 45 | // while (matches !== null) { 46 | // documentedMethods.push(matches[1]); 47 | // matches = re.exec(content); 48 | // } 49 | 50 | // const missingDocumentation = collect(methods).transform(t => t.replace(/.js/, '')).diff(documentedMethods).all(); 51 | // expect(missingDocumentation).to.eql(['symbol.iterator']); 52 | // }); 53 | 54 | // it('should document all methods in docs/api', () => { 55 | // const docFiles = fs.readdirSync(path.join(__dirname, '../docs/api'), 'utf-8'); 56 | // const methodFiles = fs.readdirSync(path.join(__dirname, '../src/methods'), 'utf-8'); 57 | 58 | // const docsCollection = collect(docFiles).map(t => t.replace(/.md/, '')); 59 | // const methodsCollection = collect(methodFiles).map(t => t.replace(/.js/, '')); 60 | 61 | // expect(methodsCollection.diff(docsCollection).all()).to.eql(['symbol.iterator']); 62 | // }); 63 | 64 | // it('should not have any dependencies', () => { 65 | // const content = fs.readFileSync('package.json'); 66 | // const pckg = JSON.parse(content); 67 | 68 | // expect(pckg.dependencies).to.eql(undefined); 69 | // }); 70 | // }); 71 | --------------------------------------------------------------------------------