dynamic recusive form

This commit is contained in:
Sebastian Frank
2017-12-13 14:24:59 +01:00
parent f49153603a
commit bf6dc2c944
6 changed files with 189 additions and 33 deletions
+3 -3
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+9 -9
View File
@@ -6902,15 +6902,6 @@
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true "dev": true
}, },
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
}
},
"string-width": { "string-width": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -6944,6 +6935,15 @@
} }
} }
}, },
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
}
},
"stringstream": { "stringstream": {
"version": "0.0.5", "version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+115 -9
View File
@@ -1,3 +1,4 @@
<!--
<template> <template>
<form @submit.prevent="submit()"> <form @submit.prevent="submit()">
<div v-for="(el, idx) in elements" :key="idx"> <div v-for="(el, idx) in elements" :key="idx">
@@ -19,10 +20,77 @@
</div> </div>
</form> </form>
</template> </template>
-->
<script> <script>
import MyInput from 'components/my-input.vue';
export default { export default {
name: "MyForm", name: "MyForm",
render(createElement) {
let self = this;
let _cElCall;
_cElCall = function(elements, formData) {
let formEl = [ ];
elements.forEach(el => {
let n = el.name ? el.name : el.key;
switch (el.element) {
case "my-input":
formEl.push(createElement(MyInput, {
props: {
name: n,
label: el.label,
description: el.description,
props: el.props,
value: formData[n],
validate: el.validate
},
on: {
input(val) {
formData[n] = val;
},
validate(valid) {
el.valid = valid;
}
}
}));
break;
case "section":
formEl.push(createElement("h3", el.label));
if (!formData[n]) {
formData[n] = { };
}
formEl.push(createElement("div", _cElCall(el.subElements, formData[n])));
break;
}
});
return formEl;
}
let formElements = _cElCall(this.elements, self.formData);
this.buttons.forEach(b => {
formElements.push(createElement("button", {
class: {
button: true
},
on: {
click(event) {
event.preventDefault();
self.buttonClick(b.type);
}
}
}, b.label ? b.label : "OK"))
})
return createElement('form', {
on: {
submit(e) {
e.preventDefault();
}
}
}, formElements)
},
props: { props: {
initData: { initData: {
type: Object, type: Object,
@@ -52,29 +120,29 @@ export default {
data() { data() {
return { return {
formData: this.initData, formData: this.initData,
formErrors: {}, // formErrors: {},
elementsMap: {} // elementsMap: {}
} }
}, },
created() { /*created() {
this.setOrder(); this.setOrder();
}, },*/
watch: { watch: {
elements() { /*elements() {
this.setOrder(); this.setOrder();
}, },*/
initData() { initData() {
this.formData = this.initData; this.formData = this.initData;
} }
}, },
methods: { methods: {
setOrder() { /*setOrder() {
this.elementsMap = {}; this.elementsMap = {};
for (let a in this.elements) { for (let a in this.elements) {
this.elementsMap[this.elements[a].key] = this.elements[a]; this.elementsMap[this.elements[a].key] = this.elements[a];
} }
}, },*/
validateData(name) { /*validateData(name) {
if (this.elementsMap[name].required) { if (this.elementsMap[name].required) {
if (!this.formData[name]) { if (!this.formData[name]) {
this.$set(this.formErrors, name, { this.$set(this.formErrors, name, {
@@ -88,6 +156,17 @@ export default {
} }
return true; return true;
}, },
validateElement(el, val) {
if (el.validate) {
let v = el.validate;
if (v.required && !val) {
el.validatorMessage = v.requiredMessage ? v.requiredMessage : "input is required";
return false;
}
}
return true;
},*/
buttonClick(type) { buttonClick(type) {
switch(type) { switch(type) {
case 'submit': case 'submit':
@@ -96,6 +175,32 @@ export default {
} }
}, },
submit() { submit() {
// bad hacky solution, but works
this.$children.forEach(c => {
c.validateValue();
});
let _validateE;
_validateE = function(eArr) {
for (let i = 0; i< eArr.length; ++i) {
if (eArr[i].valid === false) {
return false;
} else if (eArr[i].subElements) {
// validate subElements
let subValid = _validateE(eArr[i].subElements);
if (!subValid) {
return false;
}
}
}
return true;
}
if (_validateE(this.elements)) {
this.submitHandler(this.formData);
}
/*
let valid = true; let valid = true;
Object.keys(this.elementsMap).forEach(key => { Object.keys(this.elementsMap).forEach(key => {
valid = (this.validateData(key) && valid); valid = (this.validateData(key) && valid);
@@ -103,6 +208,7 @@ export default {
if (valid) { if (valid) {
this.submitHandler(this.formData); this.submitHandler(this.formData);
} }
*/
} }
} }
} }
+53 -7
View File
@@ -6,13 +6,22 @@
</div> </div>
<!-- --> <!-- -->
<input type="number" v-if="props && props.type == 'number'"
v-model="currentValue"
:class="{invalid}"
:id="name"
:name="name"
:placeholder="props.placeholder"
@blur="validateValue"
@change="handleChange"
>
<input type="text" v-if="props && props.type == 'text'" <input type="text" v-if="props && props.type == 'text'"
v-model="currentValue" v-model="currentValue"
:class="{invalid}" :class="{invalid}"
:id="name" :id="name"
:name="name" :name="name"
:placeholder="props.placeholder" :placeholder="props.placeholder"
@blur="validate" @blur="validateValue"
@change="handleChange" @change="handleChange"
> >
<input type="password" v-else-if="props && props.type == 'password'" <input type="password" v-else-if="props && props.type == 'password'"
@@ -21,7 +30,7 @@
:id="name" :id="name"
:name="name" :name="name"
:placeholder="props.placeholder" :placeholder="props.placeholder"
@blur="validate" @blur="validateValue"
@change="handleChange" @change="handleChange"
> >
<div class="checkbox_holder" v-else-if="props && props.type == 'checkbox'"> <div class="checkbox_holder" v-else-if="props && props.type == 'checkbox'">
@@ -30,6 +39,7 @@
v-model="currentValue" v-model="currentValue"
:id="name" :id="name"
:name="name" :name="name"
@blur="validateValue"
@change="handleChange" @change="handleChange"
> >
<div class="check_checkbox"></div> <div class="check_checkbox"></div>
@@ -54,17 +64,29 @@ export default {
'name', 'name',
'props', 'props',
'value', 'value',
'invalid', // 'invalid',
'validatorMessage' // 'validatorMessage',
'validate'
], ],
data() { data() {
return { return {
currentValue: this.value currentValue: this.value,
invalid: false,
validatorMessage: ""
} }
}, },
methods: { methods: {
validate() { validateValue() {
this.$emit('validate', this.currentValue); var valid = true;
let v = this.validate;
if (v) {
if (v.required && !this.currentValue) {
valid = false;
this.validatorMessage = (typeof v.requiredMessage == "string") ? v.requiredMessage : "input required";
}
}
this.invalid = !valid;
this.$emit('validate', valid);
}, },
handleChange() { handleChange() {
this.$emit('change', this.currentValue); this.$emit('change', this.currentValue);
@@ -72,6 +94,30 @@ export default {
}, },
watch: { watch: {
currentValue(val) { currentValue(val) {
if (this.props && this.props.datatype) {
// convert
let t = this.props.datatype;
let newVal;
switch (t) {
case "int":
newVal = parseInt(this.currentValue);
if (isNaN(newVal)) {
newVal = 0;
}
break;
case "float":
newVal = parseFloat(this.currentValue);
if (isNaN(newVal)) {
newVal = 0;
}
break;
}
if (newVal !== this.currentValue) {
this.currentValue = newVal;
return; // prevent double input events
}
}
this.$emit('input', val); this.$emit('input', val);
} }
} }
+6 -2
View File
@@ -28,8 +28,10 @@ export default {
key: "username", key: "username",
element: "my-input", element: "my-input",
icon: "icon-user", icon: "icon-user",
validate: {
required: true, required: true,
requiredMessage: "Der Benutzername wird benötigt!", requiredMessage: "Der Benutzername wird benötigt!"
},
props: { props: {
type: "text", type: "text",
placeholder: "Benutzername" placeholder: "Benutzername"
@@ -39,8 +41,10 @@ export default {
key: "password", key: "password",
element: "my-input", element: "my-input",
icon: "icon-key", icon: "icon-key",
validate: {
required: true, required: true,
requiredMessage: "Das Passwort wird benötigt!", requiredMessage: "Das Passwort wird benötigt!"
},
props: { props: {
type: "password", type: "password",
placeholder: "Passwort" placeholder: "Passwort"