Add support for select

This commit is contained in:
Crow Crowcrow 2018-05-02 18:04:48 +02:00
parent 61a7dbbe03
commit 1771eeb338
Signed by: Crow
GPG Key ID: 45A8E203AF859FD8
3 changed files with 131 additions and 89 deletions

View File

@ -1,4 +1,4 @@
HTMLElement.prototype.isChildOf = function(parent){ HTMLElement.prototype.isChildOf = function(parent) {
var node = this.parentNode; var node = this.parentNode;
while (node != null) { while (node != null) {
if (node == parent) { if (node == parent) {
@ -7,7 +7,7 @@ HTMLElement.prototype.isChildOf = function(parent){
node = node.parentNode; node = node.parentNode;
} }
return false; return false;
} };
class SerializerField { class SerializerField {
/** /**
@ -16,11 +16,11 @@ class SerializerField {
* @param {HTMLElement} f Input field (input|select|textarea) * @param {HTMLElement} f Input field (input|select|textarea)
* @returns {SerializerField} * @returns {SerializerField}
*/ */
constructor(f){ constructor(f) {
this.name; this.name;
this.parent; this.parent;
this.field = f; this.field = f;
this.required = f.required this.required = f.required;
this.type = this.field.getAttribute("type"); this.type = this.field.getAttribute("type");
} }
@ -29,23 +29,36 @@ class SerializerField {
* *
* @returns {(String|Number|Boolean|Date)} * @returns {(String|Number|Boolean|Date)}
*/ */
serialize(){ serialize() {
if(this.type == "number" || this.type == "range"){ if (this.field.tagName == "SELECT" && this.field.multiple) {
return Array.apply(null, this.field.options).filter(o => o.selected).map(o => o.value)
}
if (this.type == "number" || this.type == "range") {
return Number(this.field.value); return Number(this.field.value);
} else if(this.type == "date" || this.type == "datetime-local" || this.type == "month" || this.type == "week") { }
if (
this.type == "date" ||
this.type == "datetime-local" ||
this.type == "month" ||
this.type == "week"
) {
return new Date(this.field.value); return new Date(this.field.value);
} else if(this.type == "checkbox") { }
if (this.type == "checkbox") {
let checked = this.field.checked; let checked = this.field.checked;
if(this.field.hasAttribute("string")){ if (this.field.hasAttribute("string")) {
if(!checked) { if (!checked) {
return null; return null;
} }
return this.field.value return this.field.value;
} }
return checked; return checked;
} else { }
return this.field.value;
} return this.field.value;
} }
} }
@ -56,8 +69,10 @@ class Serializer {
* @param {HTMLElement} element * @param {HTMLElement} element
* @returns {Serializer} * @returns {Serializer}
*/ */
constructor(element){ constructor(element) {
let fields = element.querySelectorAll(`:scope input:not([type="submit"]), :scope select, :scope textarea, :scope group`); let fields = element.querySelectorAll(
`:scope input:not([type="submit"]), :scope select, :scope textarea, :scope group`
);
let groups = [].slice.call(element.querySelectorAll(`:scope group`)); let groups = [].slice.call(element.querySelectorAll(`:scope group`));
this.name = ":root"; this.name = ":root";
@ -67,8 +82,8 @@ class Serializer {
this.required = element.getAttribute("required") !== null || this.min != -1; this.required = element.getAttribute("required") !== null || this.min != -1;
for(let f of fields){ for (let f of fields) {
if(f.isChildOf(element) && groups.every(g => !f.isChildOf(g))){ if (f.isChildOf(element) && groups.every(g => !f.isChildOf(g))) {
this.addField(f); this.addField(f);
} }
} }
@ -79,39 +94,41 @@ class Serializer {
* *
* @param {HTMLElement} f * @param {HTMLElement} f
*/ */
addField(f){ addField(f) {
let fieldName = f.getAttribute("name"); let fieldName = f.getAttribute("name");
let isArray = fieldName.match(/^\[(\d*)\]/) let isArray = fieldName.match(/^\[(\d*)\]/);
fieldName = fieldName.replace(/^\[\d*\]/, ''); fieldName = fieldName.replace(/^\[\d*\]/, "");
console.log(f)
let field; let field;
if(f.tagName == "GROUP"){ if (f.tagName == "GROUP") {
field = new Serializer(f) field = new Serializer(f);
} else { } else {
field = new SerializerField(f); field = new SerializerField(f);
} }
if(field.type == "file"){ if (field.type == "file") {
return; return;
} }
field.name = fieldName; field.name = fieldName;
field.parent = this; field.parent = this;
f.serializer = field f.serializer = field;
if(isArray){ if (isArray) {
if(!this._fields.has(fieldName)){ if (!this._fields.has(fieldName)) {
this._fields.set(fieldName, []) this._fields.set(fieldName, []);
} }
if(isArray[1] != ""){ if (isArray[1] != "") {
this._fields.get(fieldName)[isArray[1]] = field this._fields.get(fieldName)[isArray[1]] = field;
} else { } else {
this._fields.get(fieldName).push(field) this._fields.get(fieldName).push(field);
} }
} else { } else {
this._fields.set(fieldName, field) this._fields.set(fieldName, field);
} }
} }
@ -120,12 +137,12 @@ class Serializer {
* *
* @param {(HTMLElement|Serializer|SerializerField)} f Serializer child * @param {(HTMLElement|Serializer|SerializerField)} f Serializer child
*/ */
removeField(f){ removeField(f) {
if(this._fields.has(f.name)){ if (this._fields.has(f.name)) {
let fields = this._fields.get(f.name) let fields = this._fields.get(f.name);
if(Array.isArray(fields)){ if (Array.isArray(fields)) {
let index = fields.indexOf(f); let index = fields.indexOf(f);
this._fields.set(f.name, fields.splice(index, 1)) this._fields.set(f.name, fields.splice(index, 1));
} else { } else {
this._fields.delete(); this._fields.delete();
} }
@ -137,20 +154,20 @@ class Serializer {
* *
* @returns {Object} * @returns {Object}
*/ */
serialize(){ serialize() {
let json = {}; let json = {};
for(let [k, f] of this._fields){ for (let [k, f] of this._fields) {
if(Array.isArray(f)){ if (Array.isArray(f)) {
if(k != ""){ if (k != "") {
json[k] = []; json[k] = [];
} else { } else {
json = []; json = [];
} }
for(let key in f){ for (let key in f) {
if(f[key]){ if (f[key]) {
let d = f[key].serialize(); let d = f[key].serialize();
if(d !== null){ if (d !== null) {
if(k == ""){ if (k == "") {
json[key] = d; json[key] = d;
continue; continue;
} }
@ -160,14 +177,14 @@ class Serializer {
} }
} else { } else {
let d = f.serialize(); let d = f.serialize();
if(f !== null){ if (f !== null) {
if(k == ""){ if (k == "") {
json = d json = d;
continue; continue;
} }
json[k] = d json[k] = d;
} }
} }
} }
return json; return json;
} }
@ -181,18 +198,18 @@ class Validator {
* @param {(erializer|SerializerField)} field * @param {(erializer|SerializerField)} field
* @returns {Object} * @returns {Object}
*/ */
static validateRequired(field){ static validateRequired(field) {
let e = { let e = {
valid: true, valid: true,
errors: {}, errors: {}
} };
let v; let v;
if(field instanceof Serializer){ if (field instanceof Serializer) {
v = Validator.validateRequiredSerializer(field); v = Validator.validateRequiredSerializer(field);
} else { } else {
v = Validator.validateRequiredSerialzerField(field) v = Validator.validateRequiredSerialzerField(field);
} }
if(!v.valid){ if (!v.valid) {
e.valid = false; e.valid = false;
e.errors = v.errors; e.errors = v.errors;
} }
@ -206,43 +223,43 @@ class Validator {
* @param {Serializer} field * @param {Serializer} field
* @returns {Object} * @returns {Object}
*/ */
static validateRequiredSerializer(field){ static validateRequiredSerializer(field) {
let e = { let e = {
valid: true, valid: true,
errors: {}, errors: {}
} };
let min = field.min; let min = field.min;
let validFieldsCount = 0; let validFieldsCount = 0;
for(let [k, f] of field._fields){ for (let [k, f] of field._fields) {
if(Array.isArray(f)){ if (Array.isArray(f)) {
e.errors = { e.errors = {
message: null, message: null
}; };
let validCount = 0; let validCount = 0;
for(let sf of f){ for (let sf of f) {
if(!(sf.name in e.errors)){ if (!(sf.name in e.errors)) {
e.errors[sf.name] = []; e.errors[sf.name] = [];
} }
let v = Validator.validateRequired(sf) let v = Validator.validateRequired(sf);
if(!v.valid){ if (!v.valid) {
console.log(e.errors) console.log(e.errors);
e.errors[sf.name].push(v.errors); e.errors[sf.name].push(v.errors);
continue; continue;
} }
validCount++ validCount++;
} }
e.valid = min <= validCount e.valid = min <= validCount;
if(e.valid){ if (e.valid) {
e.errors = {} e.errors = {};
} else { } else {
e.errors.message = `Should contain atleast ${min} value(s)` e.errors.message = `Should contain atleast ${min} value(s)`;
} }
} else { } else {
let v = Validator.validateRequired(f); let v = Validator.validateRequired(f);
if(!v.valid){ if (!v.valid) {
e.valid = false; e.valid = false;
e.errors[f.name] = v.errors; e.errors[f.name] = v.errors;
} }
@ -259,19 +276,19 @@ class Validator {
* @param {SerializerField} field * @param {SerializerField} field
* @returns {Object} * @returns {Object}
*/ */
static validateRequiredSerialzerField(field){ static validateRequiredSerialzerField(field) {
let e = { let e = {
valid: true, valid: true,
errors: {}, errors: {}
} };
if(field.required || (field.parent && field.parent.required)){ if (field.required || (field.parent && field.parent.required)) {
if(field.type != "checkbox"){ if (field.type != "checkbox") {
e.valid = field.field.validity.valueMissing !== true e.valid = field.field.validity.valueMissing !== true;
e.errors = "Is required" e.errors = "Is required";
} else { } else {
e.valid = field.field.checked e.valid = field.field.checked;
e.errors = "Not checked" e.errors = "Not checked";
} }
} }
return e; return e;

View File

@ -24,6 +24,10 @@ var testData = []*Person{
Surname: "Bar", Surname: "Bar",
Age: 57, Age: 57,
BirthDate: time.Date(1960, 1, 1, 0, 0, 0, 0, time.UTC), BirthDate: time.Date(1960, 1, 1, 0, 0, 0, 0, time.UTC),
Animal: "Dog",
Checklist: []string{
"Milk",
},
}, },
nil, nil,
&Person{ &Person{
@ -32,22 +36,26 @@ var testData = []*Person{
Age: 22, Age: 22,
BirthDate: time.Date(1995, 1, 5, 0, 0, 0, 0, time.UTC), BirthDate: time.Date(1995, 1, 5, 0, 0, 0, 0, time.UTC),
Languages: []Language{ Languages: []Language{
Language{ {
Language: "Dutch", Language: "Dutch",
SkillLevel: 9.99, SkillLevel: 9.99,
Native: true, Native: true,
}, },
Language{ {
Language: "English", Language: "English",
SkillLevel: 8.4, SkillLevel: 8.4,
Native: false, Native: false,
}, },
Language{ {
Language: "Japanese", Language: "Japanese",
SkillLevel: -9000.9, SkillLevel: -9000.9,
Native: false, Native: false,
}, },
}, },
Animal: "Dog",
Checklist: []string{
"Milk",
},
}, },
} }
@ -84,9 +92,11 @@ type Person struct {
Age int `json:"age"` Age int `json:"age"`
BirthDate time.Time `json:"birth_date"` BirthDate time.Time `json:"birth_date"`
Languages []Language `json:"languages"` Languages []Language `json:"languages"`
Checklist []string `json:"checklist"`
Animal string `json:"animal"`
} }
// Languages ... // Language ...
type Language struct { type Language struct {
Language string `json:"language"` Language string `json:"language"`
SkillLevel float64 `json:"skill_level"` SkillLevel float64 `json:"skill_level"`
@ -129,14 +139,21 @@ func isEq(p1, p2 Person) {
assert(p1.Surname, p2.Surname, "Surname") assert(p1.Surname, p2.Surname, "Surname")
assert(p1.Age, p2.Age, "Age") assert(p1.Age, p2.Age, "Age")
assert(p1.BirthDate, p2.BirthDate, "BirthDate") assert(p1.BirthDate, p2.BirthDate, "BirthDate")
assert(p1.Animal, p2.Animal, "Animal")
if !assert(len(p2.Languages), len(p1.Languages), "Languages length") { if !assert(len(p2.Languages), len(p1.Languages), "Languages length") {
return return
} }
if !assert(len(p2.Checklist), len(p1.Checklist), "Checklist length") {
return
}
for k := range p2.Languages { for k := range p2.Languages {
assert(p1.Languages[k].Language, p2.Languages[k].Language, "Languages.Language") assert(p1.Languages[k].Language, p2.Languages[k].Language, "Languages.Language")
assert(p1.Languages[k].SkillLevel, p2.Languages[k].SkillLevel, "Languages.SkillLevel") assert(p1.Languages[k].SkillLevel, p2.Languages[k].SkillLevel, "Languages.SkillLevel")
assert(p1.Languages[k].Native, p2.Languages[k].Native, "Languages.Native") assert(p1.Languages[k].Native, p2.Languages[k].Native, "Languages.Native")
} }
for k := range p2.Checklist {
assert(p1.Checklist[k], p2.Checklist[k], "Checklist")
}
} }
func assert(v, v2 interface{}, ident string) bool { func assert(v, v2 interface{}, ident string) bool {

View File

@ -32,6 +32,14 @@
<input name="skill_level" type="number" value="{{ .SkillLevel }}"> <input name="skill_level" type="number" value="{{ .SkillLevel }}">
</group> </group>
{{ end }} {{ end }}
<select multiple name="checklist">
<option selected value="Milk">Milk</option>
<option value="Eggs">Eggs</option>
</select>
<select name="animal">
<option value="Dog" selected>Dog</option>
<option value="Cat">Cat</option>
</select>
</group> </group>
{{ end }} {{ end }}
{{ end }} {{ end }}