Add support for select
This commit is contained in:
parent
61a7dbbe03
commit
1771eeb338
179
serializer.js
179
serializer.js
|
@ -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 checked;
|
|
||||||
} else {
|
|
||||||
return this.field.value;
|
return this.field.value;
|
||||||
}
|
}
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,12 +177,12 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 }}
|
||||||
|
|
Loading…
Reference in New Issue