feature/field-error
Björn 3 years ago
parent eec3ec627f
commit 055206d83b

@ -1 +1,160 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
html {
line-height: 1.15;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
main {
display: block;
}
h1 {
font-size: 2em;
margin: 0.67em 0;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
pre {
font-family: monospace, monospace;
font-size: 1em;
}
a {
background-color: transparent;
}
abbr[title] {
border-bottom: none;
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
b, strong {
font-weight: bolder;
}
code, kbd, samp {
font-family: monospace, monospace;
font-size: 1em;
}
small {
font-size: 80%;
}
sub, sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
img {
border-style: none;
}
button, input, optgroup, select, textarea {
font-family: inherit;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
button, input {
overflow: visible;
}
button, select {
text-transform: none;
}
[type=button], [type=reset], [type=submit], button {
-webkit-appearance: button;
}
[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner {
border-style: none;
padding: 0;
}
[type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring, button:-moz-focusring {
outline: 1px dotted ButtonText;
}
fieldset {
padding: 0.35em 0.75em 0.625em;
}
legend {
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
progress {
vertical-align: baseline;
}
textarea {
overflow: auto;
}
[type=checkbox], [type=radio] {
box-sizing: border-box;
padding: 0;
}
[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type=search]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
details {
display: block;
}
summary {
display: list-item;
}
template {
display: none;
}
[hidden] {
display: none;
}

File diff suppressed because one or more lines are too long

@ -7,7 +7,23 @@
<link href="styles.css" rel="stylesheet" type="text/css">
</head>
<body>
<demo></demo>
<form class="form" novalidate>
<div class="field">
<label>
email
<input type="email" name="email" />
</label>
<field-error name="email"></field-error>
</div>
<div class="field">
<label>
password
<input type="password" name="password" />
</label>
<field-error name="password"></field-error>
</div>
<button type="submit">Send</button>
</form>
<script type="text/javascript" src="demo.js"></script>
</body>
</html>

34647
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -8,9 +8,6 @@
},
"author": "Björn Hase",
"license": "MIT",
"dependencies": {
"riot": "^5.1.2"
},
"dependencies": {
"form-serialize": "^0.7.2",
"riot": "^5.1.2",

@ -0,0 +1,119 @@
import validate from 'validate.js'
import serialize from 'form-serialize'
/**
*
*
*
*/
class FormValidator
{
/**
*
* @param {[type]} formSelector [description]
* @param {[type]} constraits [description]
*/
constructor(formSelector, constraits)
{
this.errors = []
// getting selector to find form-element
this.formSelector = formSelector
// constraits for validate.js
this.constraits = constraits
// get form and elements
this.form = document.querySelector(this.formSelector)
this.elements = this.form.querySelectorAll('field-error')
// adding submit event
this.form.addEventListener('submit', (event) => {
this.onSubmit(event)
})
// adding event if a element is updated
this.form.addEventListener('field-update', (event) => {
this.onFieldUpdate(event)
})
}
/**
*
* @param {[type]} name [description]
* @return {[type]} [description]
*/
findElementByName(name) {
let result
this.elements.forEach((element) => {
if (element.attributes.name.nodeValue == name) {
result = element
return false
}
})
return result
}
/**
*
* @param {[type]} event [description]
* @return {[type]} [description]
*/
onSubmit(event) {
let errors = validate(serialize(event.target, {
hash: true
}), this.constraits)
if (errors) {
event.preventDefault()
this.elements.forEach((element) => {
let elementErrors = false
if (errors[element.attributes.name.nodeValue]) {
elementErrors = errors[element.attributes.name.nodeValue]
}
this.dispatchCustomEvent(elementErrors, element)
})
}
}
/**
*
* @param {[type]} event [description]
* @return {[type]} [description]
*/
onFieldUpdate(event) {
let errors = validate.single(event.detail.value, this.constraits[event.detail.name])
let element = this.findElementByName(event.detail.name)
this.dispatchCustomEvent(errors, element)
}
/**
*
* @param {[type]} errors [description]
* @param {[type]} element [description]
* @return {[type]} [description]
*/
dispatchCustomEvent(errors, element)
{
let detail = false
if (errors) {
detail = errors
}
const formValidationEvent = new CustomEvent('form-validation', {
'detail': detail
})
element.dispatchEvent(formValidationEvent)
}
}
export default FormValidator

@ -1,11 +1,17 @@
import * as riot from 'riot'
import Demo from './demo.riot'
import FormValidator from './../FormValidator'
import FieldError from './../field-error.riot'
riot.register('field-error', FieldError)
riot.mount('field-error')
// let it rain
riot.register('demo', Demo)
riot.mount('demo')
const formValidation = new FormValidator('form', {
'email': {
'presence': true,
'email': true
},
'password': {
'presence': true
}
})

@ -1,7 +1,9 @@
<field-error>
<div class="field__error" if={ state.errors.length > 0 }>
<ul>
<li each={ error in state.errors }>{ error }</li>
<li each={ error in state.errors }>
{ error }
</li>
</ul>
</div>
<script>
@ -9,7 +11,7 @@
/**
* Shows errors of Validation
*
* <field-error key="name" errors={ errors }></field-error>
* <field-error name="name" errors={ errors }></field-error>
*
*/
@ -18,24 +20,47 @@
state: {
errors: [
]
],
},
/**
* check if errors from props has an error, if not reset errors in state
*
* @param {object} props
* @param {object} state
*
*/
onBeforeUpdate(props, state) {
if (props.errors && props.errors.length > 0) {
state.errors = props.errors
} else {
state.errors = []
onMounted(props, state) {
const parent = this.root.closest('.field')
const element = document.querySelector('[name="' + props.name + '"]')
const form = element.closest('form')
// element and form set
if (element && form) {
element.addEventListener('keyup', function(event) {
const fieldUpdateEvent = new CustomEvent('field-update', {
'detail': {
'name': props.name,
'value': this.value
}
})
form.dispatchEvent(fieldUpdateEvent)
})
}
}
// add custom event to listen to form-validation
this.root.addEventListener('form-validation', (event) => {
// if detail is set with element name, get errors
if (event.detail) {
this.state.errors = event.detail
parent.classList.add('field--error')
parent.classList.remove('field--valid')
} else {
this.state.errors = []
parent.classList.remove('field--error')
parent.classList.add('field--valid')
}
this.update()
})
}
}
</script>

Loading…
Cancel
Save