├── .gitignore ├── LICENSE ├── README.md ├── builds ├── cdn.js └── module.js ├── dist ├── validation.esm.js └── validation.min.js ├── package-lock.json ├── package.json ├── scripts └── build.js └── src ├── checkInput.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # misc 9 | .DS_Store 10 | *.pem 11 | 12 | # debug 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # local env files 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | # test 24 | index.html 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Mark Mead 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alpine JS Form Validation 2 | 3 | Add client side form validation with Alpine JS 🎉 4 | 5 | ## Install 6 | 7 | ### With a CDN 8 | 9 | ```html 10 | 14 | 15 | 16 | ``` 17 | 18 | ### With a Package Manager 19 | 20 | ```shell 21 | yarn add -D alpinejs-form-validation 22 | 23 | npm install -D alpinejs-form-validation 24 | ``` 25 | 26 | ```js 27 | import Alpine from 'alpinejs' 28 | import validation from 'alpinejs-form-validation' 29 | 30 | Alpine.plugin(validation) 31 | 32 | Alpine.start() 33 | ``` 34 | 35 | ## Example 36 | 37 | _This example uses Tailwind CSS for styling but that is not mandatory._ 38 | 39 | ```html 40 |
45 |
49 | 50 | 51 | 58 | 59 | 60 | Need a name 61 | 62 |
63 | 64 |
71 | 72 | 73 | 80 | 81 | 82 | Must be at least 18 years old 83 | 84 | 85 | 86 | Can't be older than 24 years old 87 | 88 |
89 | 90 |
97 | 98 | 99 | 105 | 106 | 107 | Must be at least 10 characters 108 | 109 | 110 | 111 | Can't be more than 50 characters 112 | 113 |
114 | 115 |
119 | 131 | 132 | 133 | Must accept 134 | 135 |
136 | 137 | 140 |
141 | ``` 142 | 143 | ### Functionality 144 | 145 | Breaking down the example we have the following. 146 | 147 | #### `$dispatch('validate')` 148 | 149 | Emitting the event `validate` will trigger each input within the element that 150 | the event was emitted from to run through the validation checks. 151 | 152 | #### `x-validation.required="contactName"` 153 | 154 | Here we are setting up the directive `x-validation` and passing the modifier 155 | `required` which says, the value of `contactName` must exist to pass validation. 156 | 157 | **Validation Options** 158 | 159 | - `required` 160 | - `min.X` (Where "X" is an integer) 161 | - `max.X` (Where "X" is an integer) 162 | - `min:length.X` (Where "X" is an integer) 163 | - `max:length.X` (Where "X" is an integer) 164 | 165 | #### `@error="required = $valid($event.detail, 'required')"` 166 | 167 | Here we have a few things going on. 168 | 169 | We're listing for the `@error` event which is emitted once the input has run 170 | through the validation checks. We're then setting `required` to either 171 | true/false based on the response from the magic helper `$valid`. 172 | 173 | **`$valid`** 174 | 175 | This is a magic helper which returns true/false based on the validation status 176 | of the input. You pass in the `$event.detail` which comes from the `@error` 177 | event and the validation option, in the example that's `required`. 178 | 179 | ### Styling 180 | 181 | You could style the inputs like this: 182 | 183 | ```css 184 | [data-validation-valid='false'] { 185 | border-color: red; 186 | } 187 | 188 | [data-validation-valid='true'] { 189 | border-color: green; 190 | } 191 | ``` 192 | 193 | ### Stats 194 | 195 | ![](https://img.shields.io/bundlephobia/min/alpinejs-form-validation) 196 | ![](https://img.shields.io/npm/v/alpinejs-form-validation) 197 | ![](https://img.shields.io/npm/dt/alpinejs-form-validation) 198 | ![](https://img.shields.io/github/license/markmead/alpinejs-form-validation) 199 | -------------------------------------------------------------------------------- /builds/cdn.js: -------------------------------------------------------------------------------- 1 | import validation from '../src/index.js' 2 | 3 | document.addEventListener('alpine:init', () => window.Alpine.plugin(validation)) 4 | -------------------------------------------------------------------------------- /builds/module.js: -------------------------------------------------------------------------------- 1 | import validation from '../src/index.js' 2 | 3 | export default validation 4 | -------------------------------------------------------------------------------- /dist/validation.esm.js: -------------------------------------------------------------------------------- 1 | function f(r,n,d,o,c=!1){let i=t=>n.find(a=>a===t)??!1,m=o(d),e={};if(i("required")&&(e.required=!0),i("min")){let t=n.indexOf(i("min"));e.min=Number(n[t+1])}if(i("max")){let t=n.indexOf(i("max"));e.max=Number(n[t+1])}if(i("min:length")){let t=n.indexOf(i("min:length"));e.minLength=Number(n[t+1])}if(i("max:length")){let t=n.indexOf(i("max:length"));e.maxLength=Number(n[t+1])}i("checked")&&(e.checked=!0),m(t=>{if(!c&&!t&&!r.getAttribute("data-validation-dirty"))return;let a={};i("required")&&(a.required=!!t),i("min")&&(a.min=t>=e.min),i("max")&&(a.max=t<=e.max),i("min:length")&&(a.minLength=t.length>=e.minLength),i("max:length")&&(a.maxLength=t.length<=e.maxLength),i("checked")&&(a.checked=!!t);let g=Object.values(a).every(x=>x);r.setAttribute("data-validation-dirty",!0),r.setAttribute("data-validation-valid",g),r.setAttribute("data-validation-status",JSON.stringify(a)),r.setAttribute("data-validation-options",JSON.stringify(e)),r.dispatchEvent(new CustomEvent("error",{bubbles:!0,cancelable:!0,detail:JSON.stringify(a)}))})}function u(r){r.directive("validation",(n,{modifiers:d,expression:o},{evaluateLater:c,effect:i})=>{i(()=>f(n,d,o,c)),document.addEventListener("validate",m=>{let{target:t}=m;t.contains(n)&&f(n,d,o,c,!0)})}),r.magic("valid",()=>(n,d)=>!JSON.parse(n)[d])}var v=u;export{v as default}; 2 | -------------------------------------------------------------------------------- /dist/validation.min.js: -------------------------------------------------------------------------------- 1 | (()=>{function u(r,n,d,o,c=!1){let i=t=>n.find(a=>a===t)??!1,m=o(d),e={};if(i("required")&&(e.required=!0),i("min")){let t=n.indexOf(i("min"));e.min=Number(n[t+1])}if(i("max")){let t=n.indexOf(i("max"));e.max=Number(n[t+1])}if(i("min:length")){let t=n.indexOf(i("min:length"));e.minLength=Number(n[t+1])}if(i("max:length")){let t=n.indexOf(i("max:length"));e.maxLength=Number(n[t+1])}i("checked")&&(e.checked=!0),m(t=>{if(!c&&!t&&!r.getAttribute("data-validation-dirty"))return;let a={};i("required")&&(a.required=!!t),i("min")&&(a.min=t>=e.min),i("max")&&(a.max=t<=e.max),i("min:length")&&(a.minLength=t.length>=e.minLength),i("max:length")&&(a.maxLength=t.length<=e.maxLength),i("checked")&&(a.checked=!!t);let g=Object.values(a).every(s=>s);r.setAttribute("data-validation-dirty",!0),r.setAttribute("data-validation-valid",g),r.setAttribute("data-validation-status",JSON.stringify(a)),r.setAttribute("data-validation-options",JSON.stringify(e)),r.dispatchEvent(new CustomEvent("error",{bubbles:!0,cancelable:!0,detail:JSON.stringify(a)}))})}function f(r){r.directive("validation",(n,{modifiers:d,expression:o},{evaluateLater:c,effect:i})=>{i(()=>u(n,d,o,c)),document.addEventListener("validate",m=>{let{target:t}=m;t.contains(n)&&u(n,d,o,c,!0)})}),r.magic("valid",()=>(n,d)=>!JSON.parse(n)[d])}document.addEventListener("alpine:init",()=>window.Alpine.plugin(f));})(); 2 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alpinejs-form-validation", 3 | "version": "1.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "alpinejs-form-validation", 9 | "version": "1.1.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "esbuild": "^0.25.0" 13 | } 14 | }, 15 | "node_modules/@esbuild/aix-ppc64": { 16 | "version": "0.25.0", 17 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", 18 | "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", 19 | "cpu": [ 20 | "ppc64" 21 | ], 22 | "dev": true, 23 | "license": "MIT", 24 | "optional": true, 25 | "os": [ 26 | "aix" 27 | ], 28 | "engines": { 29 | "node": ">=18" 30 | } 31 | }, 32 | "node_modules/@esbuild/android-arm": { 33 | "version": "0.25.0", 34 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", 35 | "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", 36 | "cpu": [ 37 | "arm" 38 | ], 39 | "dev": true, 40 | "license": "MIT", 41 | "optional": true, 42 | "os": [ 43 | "android" 44 | ], 45 | "engines": { 46 | "node": ">=18" 47 | } 48 | }, 49 | "node_modules/@esbuild/android-arm64": { 50 | "version": "0.25.0", 51 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", 52 | "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", 53 | "cpu": [ 54 | "arm64" 55 | ], 56 | "dev": true, 57 | "license": "MIT", 58 | "optional": true, 59 | "os": [ 60 | "android" 61 | ], 62 | "engines": { 63 | "node": ">=18" 64 | } 65 | }, 66 | "node_modules/@esbuild/android-x64": { 67 | "version": "0.25.0", 68 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", 69 | "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", 70 | "cpu": [ 71 | "x64" 72 | ], 73 | "dev": true, 74 | "license": "MIT", 75 | "optional": true, 76 | "os": [ 77 | "android" 78 | ], 79 | "engines": { 80 | "node": ">=18" 81 | } 82 | }, 83 | "node_modules/@esbuild/darwin-arm64": { 84 | "version": "0.25.0", 85 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", 86 | "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", 87 | "cpu": [ 88 | "arm64" 89 | ], 90 | "dev": true, 91 | "license": "MIT", 92 | "optional": true, 93 | "os": [ 94 | "darwin" 95 | ], 96 | "engines": { 97 | "node": ">=18" 98 | } 99 | }, 100 | "node_modules/@esbuild/darwin-x64": { 101 | "version": "0.25.0", 102 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", 103 | "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", 104 | "cpu": [ 105 | "x64" 106 | ], 107 | "dev": true, 108 | "license": "MIT", 109 | "optional": true, 110 | "os": [ 111 | "darwin" 112 | ], 113 | "engines": { 114 | "node": ">=18" 115 | } 116 | }, 117 | "node_modules/@esbuild/freebsd-arm64": { 118 | "version": "0.25.0", 119 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", 120 | "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", 121 | "cpu": [ 122 | "arm64" 123 | ], 124 | "dev": true, 125 | "license": "MIT", 126 | "optional": true, 127 | "os": [ 128 | "freebsd" 129 | ], 130 | "engines": { 131 | "node": ">=18" 132 | } 133 | }, 134 | "node_modules/@esbuild/freebsd-x64": { 135 | "version": "0.25.0", 136 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", 137 | "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", 138 | "cpu": [ 139 | "x64" 140 | ], 141 | "dev": true, 142 | "license": "MIT", 143 | "optional": true, 144 | "os": [ 145 | "freebsd" 146 | ], 147 | "engines": { 148 | "node": ">=18" 149 | } 150 | }, 151 | "node_modules/@esbuild/linux-arm": { 152 | "version": "0.25.0", 153 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", 154 | "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", 155 | "cpu": [ 156 | "arm" 157 | ], 158 | "dev": true, 159 | "license": "MIT", 160 | "optional": true, 161 | "os": [ 162 | "linux" 163 | ], 164 | "engines": { 165 | "node": ">=18" 166 | } 167 | }, 168 | "node_modules/@esbuild/linux-arm64": { 169 | "version": "0.25.0", 170 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", 171 | "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", 172 | "cpu": [ 173 | "arm64" 174 | ], 175 | "dev": true, 176 | "license": "MIT", 177 | "optional": true, 178 | "os": [ 179 | "linux" 180 | ], 181 | "engines": { 182 | "node": ">=18" 183 | } 184 | }, 185 | "node_modules/@esbuild/linux-ia32": { 186 | "version": "0.25.0", 187 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", 188 | "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", 189 | "cpu": [ 190 | "ia32" 191 | ], 192 | "dev": true, 193 | "license": "MIT", 194 | "optional": true, 195 | "os": [ 196 | "linux" 197 | ], 198 | "engines": { 199 | "node": ">=18" 200 | } 201 | }, 202 | "node_modules/@esbuild/linux-loong64": { 203 | "version": "0.25.0", 204 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", 205 | "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", 206 | "cpu": [ 207 | "loong64" 208 | ], 209 | "dev": true, 210 | "license": "MIT", 211 | "optional": true, 212 | "os": [ 213 | "linux" 214 | ], 215 | "engines": { 216 | "node": ">=18" 217 | } 218 | }, 219 | "node_modules/@esbuild/linux-mips64el": { 220 | "version": "0.25.0", 221 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", 222 | "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", 223 | "cpu": [ 224 | "mips64el" 225 | ], 226 | "dev": true, 227 | "license": "MIT", 228 | "optional": true, 229 | "os": [ 230 | "linux" 231 | ], 232 | "engines": { 233 | "node": ">=18" 234 | } 235 | }, 236 | "node_modules/@esbuild/linux-ppc64": { 237 | "version": "0.25.0", 238 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", 239 | "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", 240 | "cpu": [ 241 | "ppc64" 242 | ], 243 | "dev": true, 244 | "license": "MIT", 245 | "optional": true, 246 | "os": [ 247 | "linux" 248 | ], 249 | "engines": { 250 | "node": ">=18" 251 | } 252 | }, 253 | "node_modules/@esbuild/linux-riscv64": { 254 | "version": "0.25.0", 255 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", 256 | "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", 257 | "cpu": [ 258 | "riscv64" 259 | ], 260 | "dev": true, 261 | "license": "MIT", 262 | "optional": true, 263 | "os": [ 264 | "linux" 265 | ], 266 | "engines": { 267 | "node": ">=18" 268 | } 269 | }, 270 | "node_modules/@esbuild/linux-s390x": { 271 | "version": "0.25.0", 272 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", 273 | "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", 274 | "cpu": [ 275 | "s390x" 276 | ], 277 | "dev": true, 278 | "license": "MIT", 279 | "optional": true, 280 | "os": [ 281 | "linux" 282 | ], 283 | "engines": { 284 | "node": ">=18" 285 | } 286 | }, 287 | "node_modules/@esbuild/linux-x64": { 288 | "version": "0.25.0", 289 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", 290 | "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", 291 | "cpu": [ 292 | "x64" 293 | ], 294 | "dev": true, 295 | "license": "MIT", 296 | "optional": true, 297 | "os": [ 298 | "linux" 299 | ], 300 | "engines": { 301 | "node": ">=18" 302 | } 303 | }, 304 | "node_modules/@esbuild/netbsd-arm64": { 305 | "version": "0.25.0", 306 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", 307 | "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", 308 | "cpu": [ 309 | "arm64" 310 | ], 311 | "dev": true, 312 | "license": "MIT", 313 | "optional": true, 314 | "os": [ 315 | "netbsd" 316 | ], 317 | "engines": { 318 | "node": ">=18" 319 | } 320 | }, 321 | "node_modules/@esbuild/netbsd-x64": { 322 | "version": "0.25.0", 323 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", 324 | "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", 325 | "cpu": [ 326 | "x64" 327 | ], 328 | "dev": true, 329 | "license": "MIT", 330 | "optional": true, 331 | "os": [ 332 | "netbsd" 333 | ], 334 | "engines": { 335 | "node": ">=18" 336 | } 337 | }, 338 | "node_modules/@esbuild/openbsd-arm64": { 339 | "version": "0.25.0", 340 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", 341 | "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", 342 | "cpu": [ 343 | "arm64" 344 | ], 345 | "dev": true, 346 | "license": "MIT", 347 | "optional": true, 348 | "os": [ 349 | "openbsd" 350 | ], 351 | "engines": { 352 | "node": ">=18" 353 | } 354 | }, 355 | "node_modules/@esbuild/openbsd-x64": { 356 | "version": "0.25.0", 357 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", 358 | "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", 359 | "cpu": [ 360 | "x64" 361 | ], 362 | "dev": true, 363 | "license": "MIT", 364 | "optional": true, 365 | "os": [ 366 | "openbsd" 367 | ], 368 | "engines": { 369 | "node": ">=18" 370 | } 371 | }, 372 | "node_modules/@esbuild/sunos-x64": { 373 | "version": "0.25.0", 374 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", 375 | "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", 376 | "cpu": [ 377 | "x64" 378 | ], 379 | "dev": true, 380 | "license": "MIT", 381 | "optional": true, 382 | "os": [ 383 | "sunos" 384 | ], 385 | "engines": { 386 | "node": ">=18" 387 | } 388 | }, 389 | "node_modules/@esbuild/win32-arm64": { 390 | "version": "0.25.0", 391 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", 392 | "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", 393 | "cpu": [ 394 | "arm64" 395 | ], 396 | "dev": true, 397 | "license": "MIT", 398 | "optional": true, 399 | "os": [ 400 | "win32" 401 | ], 402 | "engines": { 403 | "node": ">=18" 404 | } 405 | }, 406 | "node_modules/@esbuild/win32-ia32": { 407 | "version": "0.25.0", 408 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", 409 | "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", 410 | "cpu": [ 411 | "ia32" 412 | ], 413 | "dev": true, 414 | "license": "MIT", 415 | "optional": true, 416 | "os": [ 417 | "win32" 418 | ], 419 | "engines": { 420 | "node": ">=18" 421 | } 422 | }, 423 | "node_modules/@esbuild/win32-x64": { 424 | "version": "0.25.0", 425 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", 426 | "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", 427 | "cpu": [ 428 | "x64" 429 | ], 430 | "dev": true, 431 | "license": "MIT", 432 | "optional": true, 433 | "os": [ 434 | "win32" 435 | ], 436 | "engines": { 437 | "node": ">=18" 438 | } 439 | }, 440 | "node_modules/esbuild": { 441 | "version": "0.25.0", 442 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", 443 | "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", 444 | "dev": true, 445 | "hasInstallScript": true, 446 | "license": "MIT", 447 | "bin": { 448 | "esbuild": "bin/esbuild" 449 | }, 450 | "engines": { 451 | "node": ">=18" 452 | }, 453 | "optionalDependencies": { 454 | "@esbuild/aix-ppc64": "0.25.0", 455 | "@esbuild/android-arm": "0.25.0", 456 | "@esbuild/android-arm64": "0.25.0", 457 | "@esbuild/android-x64": "0.25.0", 458 | "@esbuild/darwin-arm64": "0.25.0", 459 | "@esbuild/darwin-x64": "0.25.0", 460 | "@esbuild/freebsd-arm64": "0.25.0", 461 | "@esbuild/freebsd-x64": "0.25.0", 462 | "@esbuild/linux-arm": "0.25.0", 463 | "@esbuild/linux-arm64": "0.25.0", 464 | "@esbuild/linux-ia32": "0.25.0", 465 | "@esbuild/linux-loong64": "0.25.0", 466 | "@esbuild/linux-mips64el": "0.25.0", 467 | "@esbuild/linux-ppc64": "0.25.0", 468 | "@esbuild/linux-riscv64": "0.25.0", 469 | "@esbuild/linux-s390x": "0.25.0", 470 | "@esbuild/linux-x64": "0.25.0", 471 | "@esbuild/netbsd-arm64": "0.25.0", 472 | "@esbuild/netbsd-x64": "0.25.0", 473 | "@esbuild/openbsd-arm64": "0.25.0", 474 | "@esbuild/openbsd-x64": "0.25.0", 475 | "@esbuild/sunos-x64": "0.25.0", 476 | "@esbuild/win32-arm64": "0.25.0", 477 | "@esbuild/win32-ia32": "0.25.0", 478 | "@esbuild/win32-x64": "0.25.0" 479 | } 480 | } 481 | }, 482 | "dependencies": { 483 | "@esbuild/aix-ppc64": { 484 | "version": "0.25.0", 485 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", 486 | "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", 487 | "dev": true, 488 | "optional": true 489 | }, 490 | "@esbuild/android-arm": { 491 | "version": "0.25.0", 492 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", 493 | "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", 494 | "dev": true, 495 | "optional": true 496 | }, 497 | "@esbuild/android-arm64": { 498 | "version": "0.25.0", 499 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", 500 | "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", 501 | "dev": true, 502 | "optional": true 503 | }, 504 | "@esbuild/android-x64": { 505 | "version": "0.25.0", 506 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", 507 | "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", 508 | "dev": true, 509 | "optional": true 510 | }, 511 | "@esbuild/darwin-arm64": { 512 | "version": "0.25.0", 513 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", 514 | "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", 515 | "dev": true, 516 | "optional": true 517 | }, 518 | "@esbuild/darwin-x64": { 519 | "version": "0.25.0", 520 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", 521 | "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", 522 | "dev": true, 523 | "optional": true 524 | }, 525 | "@esbuild/freebsd-arm64": { 526 | "version": "0.25.0", 527 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", 528 | "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", 529 | "dev": true, 530 | "optional": true 531 | }, 532 | "@esbuild/freebsd-x64": { 533 | "version": "0.25.0", 534 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", 535 | "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", 536 | "dev": true, 537 | "optional": true 538 | }, 539 | "@esbuild/linux-arm": { 540 | "version": "0.25.0", 541 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", 542 | "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", 543 | "dev": true, 544 | "optional": true 545 | }, 546 | "@esbuild/linux-arm64": { 547 | "version": "0.25.0", 548 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", 549 | "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", 550 | "dev": true, 551 | "optional": true 552 | }, 553 | "@esbuild/linux-ia32": { 554 | "version": "0.25.0", 555 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", 556 | "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", 557 | "dev": true, 558 | "optional": true 559 | }, 560 | "@esbuild/linux-loong64": { 561 | "version": "0.25.0", 562 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", 563 | "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", 564 | "dev": true, 565 | "optional": true 566 | }, 567 | "@esbuild/linux-mips64el": { 568 | "version": "0.25.0", 569 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", 570 | "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", 571 | "dev": true, 572 | "optional": true 573 | }, 574 | "@esbuild/linux-ppc64": { 575 | "version": "0.25.0", 576 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", 577 | "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", 578 | "dev": true, 579 | "optional": true 580 | }, 581 | "@esbuild/linux-riscv64": { 582 | "version": "0.25.0", 583 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", 584 | "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", 585 | "dev": true, 586 | "optional": true 587 | }, 588 | "@esbuild/linux-s390x": { 589 | "version": "0.25.0", 590 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", 591 | "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", 592 | "dev": true, 593 | "optional": true 594 | }, 595 | "@esbuild/linux-x64": { 596 | "version": "0.25.0", 597 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", 598 | "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", 599 | "dev": true, 600 | "optional": true 601 | }, 602 | "@esbuild/netbsd-arm64": { 603 | "version": "0.25.0", 604 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", 605 | "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", 606 | "dev": true, 607 | "optional": true 608 | }, 609 | "@esbuild/netbsd-x64": { 610 | "version": "0.25.0", 611 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", 612 | "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", 613 | "dev": true, 614 | "optional": true 615 | }, 616 | "@esbuild/openbsd-arm64": { 617 | "version": "0.25.0", 618 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", 619 | "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", 620 | "dev": true, 621 | "optional": true 622 | }, 623 | "@esbuild/openbsd-x64": { 624 | "version": "0.25.0", 625 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", 626 | "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", 627 | "dev": true, 628 | "optional": true 629 | }, 630 | "@esbuild/sunos-x64": { 631 | "version": "0.25.0", 632 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", 633 | "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", 634 | "dev": true, 635 | "optional": true 636 | }, 637 | "@esbuild/win32-arm64": { 638 | "version": "0.25.0", 639 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", 640 | "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", 641 | "dev": true, 642 | "optional": true 643 | }, 644 | "@esbuild/win32-ia32": { 645 | "version": "0.25.0", 646 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", 647 | "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", 648 | "dev": true, 649 | "optional": true 650 | }, 651 | "@esbuild/win32-x64": { 652 | "version": "0.25.0", 653 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", 654 | "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", 655 | "dev": true, 656 | "optional": true 657 | }, 658 | "esbuild": { 659 | "version": "0.25.0", 660 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", 661 | "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", 662 | "dev": true, 663 | "requires": { 664 | "@esbuild/aix-ppc64": "0.25.0", 665 | "@esbuild/android-arm": "0.25.0", 666 | "@esbuild/android-arm64": "0.25.0", 667 | "@esbuild/android-x64": "0.25.0", 668 | "@esbuild/darwin-arm64": "0.25.0", 669 | "@esbuild/darwin-x64": "0.25.0", 670 | "@esbuild/freebsd-arm64": "0.25.0", 671 | "@esbuild/freebsd-x64": "0.25.0", 672 | "@esbuild/linux-arm": "0.25.0", 673 | "@esbuild/linux-arm64": "0.25.0", 674 | "@esbuild/linux-ia32": "0.25.0", 675 | "@esbuild/linux-loong64": "0.25.0", 676 | "@esbuild/linux-mips64el": "0.25.0", 677 | "@esbuild/linux-ppc64": "0.25.0", 678 | "@esbuild/linux-riscv64": "0.25.0", 679 | "@esbuild/linux-s390x": "0.25.0", 680 | "@esbuild/linux-x64": "0.25.0", 681 | "@esbuild/netbsd-arm64": "0.25.0", 682 | "@esbuild/netbsd-x64": "0.25.0", 683 | "@esbuild/openbsd-arm64": "0.25.0", 684 | "@esbuild/openbsd-x64": "0.25.0", 685 | "@esbuild/sunos-x64": "0.25.0", 686 | "@esbuild/win32-arm64": "0.25.0", 687 | "@esbuild/win32-ia32": "0.25.0", 688 | "@esbuild/win32-x64": "0.25.0" 689 | } 690 | } 691 | } 692 | } 693 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alpinejs-form-validation", 3 | "version": "1.1.0", 4 | "description": "Adds client side form/input validation powered by Alpine JS ✅", 5 | "keywords": [ 6 | "Alpine", 7 | "Alpine JS", 8 | "Alpine JS Plugins", 9 | "Alpine JS Form Validation", 10 | "JavaScript Validation", 11 | "Form Validation" 12 | ], 13 | "author": "Mark Mead", 14 | "license": "MIT", 15 | "module": "dist/validation.esm.js", 16 | "unpkg": "dist/validation.min.js", 17 | "scripts": { 18 | "build": "node scripts/build.js" 19 | }, 20 | "devDependencies": { 21 | "esbuild": "^0.25.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | buildPlugin({ 2 | entryPoints: ['builds/cdn.js'], 3 | outfile: 'dist/validation.min.js', 4 | }) 5 | 6 | buildPlugin({ 7 | entryPoints: ['builds/module.js'], 8 | outfile: 'dist/validation.esm.js', 9 | platform: 'neutral', 10 | mainFields: ['main', 'module'], 11 | }) 12 | 13 | function buildPlugin(buildOptions) { 14 | return require('esbuild').buildSync({ 15 | ...buildOptions, 16 | minify: true, 17 | bundle: true, 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /src/checkInput.js: -------------------------------------------------------------------------------- 1 | export function checkInput( 2 | el, 3 | modifiers, 4 | expression, 5 | evaluateLater, 6 | ignoreDirty = false 7 | ) { 8 | const getModifier = (key) => modifiers.find((mod) => mod === key) ?? false 9 | const getErrors = evaluateLater(expression) 10 | 11 | let validationOptions = {} 12 | 13 | if (getModifier('required')) { 14 | validationOptions.required = true 15 | } 16 | 17 | if (getModifier('min')) { 18 | const modifierIndex = modifiers.indexOf(getModifier('min')) 19 | 20 | validationOptions.min = Number(modifiers[modifierIndex + 1]) 21 | } 22 | 23 | if (getModifier('max')) { 24 | const modifierIndex = modifiers.indexOf(getModifier('max')) 25 | 26 | validationOptions.max = Number(modifiers[modifierIndex + 1]) 27 | } 28 | 29 | if (getModifier('min:length')) { 30 | const modifierIndex = modifiers.indexOf(getModifier('min:length')) 31 | 32 | validationOptions.minLength = Number(modifiers[modifierIndex + 1]) 33 | } 34 | 35 | if (getModifier('max:length')) { 36 | const modifierIndex = modifiers.indexOf(getModifier('max:length')) 37 | 38 | validationOptions.maxLength = Number(modifiers[modifierIndex + 1]) 39 | } 40 | 41 | if (getModifier('checked')) { 42 | validationOptions.checked = true 43 | } 44 | 45 | getErrors((value) => { 46 | // We don't check validation if there is no input value, 47 | // or if the input has not been interacted with 48 | if (!ignoreDirty && !value && !el.getAttribute('data-validation-dirty')) { 49 | return 50 | } 51 | 52 | let validationStatus = {} 53 | 54 | if (getModifier('required')) { 55 | validationStatus.required = !!value 56 | } 57 | 58 | if (getModifier('min')) { 59 | validationStatus.min = value >= validationOptions.min 60 | } 61 | 62 | if (getModifier('max')) { 63 | validationStatus.max = value <= validationOptions.max 64 | } 65 | 66 | if (getModifier('min:length')) { 67 | validationStatus.minLength = value.length >= validationOptions.minLength 68 | } 69 | 70 | if (getModifier('max:length')) { 71 | validationStatus.maxLength = value.length <= validationOptions.maxLength 72 | } 73 | 74 | if (getModifier('checked')) { 75 | validationStatus.checked = !!value 76 | } 77 | 78 | const isValid = Object.values(validationStatus).every((valid) => valid) 79 | 80 | el.setAttribute('data-validation-dirty', true) 81 | el.setAttribute('data-validation-valid', isValid) 82 | el.setAttribute('data-validation-status', JSON.stringify(validationStatus)) 83 | el.setAttribute( 84 | 'data-validation-options', 85 | JSON.stringify(validationOptions) 86 | ) 87 | 88 | el.dispatchEvent( 89 | new CustomEvent('error', { 90 | bubbles: true, 91 | cancelable: true, 92 | detail: JSON.stringify(validationStatus), 93 | }) 94 | ) 95 | }) 96 | } 97 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { checkInput } from './checkInput' 2 | 3 | export default function (Alpine) { 4 | Alpine.directive( 5 | 'validation', 6 | (el, { modifiers, expression }, { evaluateLater, effect }) => { 7 | effect(() => checkInput(el, modifiers, expression, evaluateLater)) 8 | 9 | document.addEventListener('validate', (event) => { 10 | const ignoreDirty = true 11 | 12 | const { target } = event 13 | 14 | if (!target.contains(el)) { 15 | return 16 | } 17 | 18 | checkInput(el, modifiers, expression, evaluateLater, ignoreDirty) 19 | }) 20 | } 21 | ) 22 | 23 | Alpine.magic('valid', () => (json, key) => !JSON.parse(json)[key]) 24 | } 25 | --------------------------------------------------------------------------------