Add support for select
This commit is contained in:
		
							parent
							
								
									61a7dbbe03
								
							
						
					
					
						commit
						1771eeb338
					
				
					 3 changed files with 131 additions and 89 deletions
				
			
		
							
								
								
									
										187
									
								
								serializer.js
									
										
									
									
									
								
							
							
						
						
									
										187
									
								
								serializer.js
									
										
									
									
									
								
							|  | @ -1,4 +1,4 @@ | |||
| HTMLElement.prototype.isChildOf = function(parent){ | ||||
| HTMLElement.prototype.isChildOf = function(parent) { | ||||
| 	var node = this.parentNode; | ||||
| 	while (node != null) { | ||||
| 		if (node == parent) { | ||||
|  | @ -7,7 +7,7 @@ HTMLElement.prototype.isChildOf = function(parent){ | |||
| 		node = node.parentNode; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| }; | ||||
| 
 | ||||
| class SerializerField { | ||||
| 	/** | ||||
|  | @ -16,11 +16,11 @@ class SerializerField { | |||
| 	 * @param {HTMLElement} f Input field (input|select|textarea) | ||||
| 	 * @returns {SerializerField} | ||||
| 	 */ | ||||
| 	constructor(f){ | ||||
| 	constructor(f) { | ||||
| 		this.name; | ||||
| 		this.parent; | ||||
| 		this.field = f; | ||||
| 		this.required = f.required | ||||
| 		this.required = f.required; | ||||
| 		this.type = this.field.getAttribute("type"); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -29,23 +29,36 @@ class SerializerField { | |||
| 	 * | ||||
| 	 * @returns {(String|Number|Boolean|Date)} | ||||
| 	 */ | ||||
| 	serialize(){ | ||||
| 		if(this.type == "number" || this.type == "range"){ | ||||
| 	serialize() { | ||||
| 		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); | ||||
| 		} 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); | ||||
| 		} else if(this.type == "checkbox") { | ||||
| 		}  | ||||
| 
 | ||||
| 		if (this.type == "checkbox") { | ||||
| 			let checked = this.field.checked; | ||||
| 			if(this.field.hasAttribute("string")){ | ||||
| 				if(!checked) { | ||||
| 			if (this.field.hasAttribute("string")) { | ||||
| 				if (!checked) { | ||||
| 					return null; | ||||
| 				} | ||||
| 				return this.field.value | ||||
| 				return this.field.value; | ||||
| 			} | ||||
| 			return checked; | ||||
| 		} else { | ||||
| 			return this.field.value; | ||||
| 		}	 | ||||
| 		} | ||||
| 
 | ||||
| 		return this.field.value; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -56,8 +69,10 @@ class Serializer { | |||
| 	 * @param {HTMLElement} element | ||||
| 	 * @returns {Serializer} | ||||
| 	 */ | ||||
| 	constructor(element){ | ||||
| 		let fields = element.querySelectorAll(`:scope input:not([type="submit"]), :scope select, :scope textarea, :scope group`); | ||||
| 	constructor(element) { | ||||
| 		let fields = element.querySelectorAll( | ||||
| 			`:scope input:not([type="submit"]), :scope select, :scope textarea, :scope group` | ||||
| 		); | ||||
| 		let groups = [].slice.call(element.querySelectorAll(`:scope group`)); | ||||
| 
 | ||||
| 		this.name = ":root"; | ||||
|  | @ -67,8 +82,8 @@ class Serializer { | |||
| 
 | ||||
| 		this.required = element.getAttribute("required") !== null || this.min != -1; | ||||
| 
 | ||||
| 		for(let f of fields){ | ||||
| 			if(f.isChildOf(element) && groups.every(g => !f.isChildOf(g))){ | ||||
| 		for (let f of fields) { | ||||
| 			if (f.isChildOf(element) && groups.every(g => !f.isChildOf(g))) { | ||||
| 				this.addField(f); | ||||
| 			} | ||||
| 		} | ||||
|  | @ -79,39 +94,41 @@ class Serializer { | |||
| 	 * | ||||
| 	 * @param {HTMLElement} f | ||||
| 	 */ | ||||
| 	addField(f){ | ||||
| 	addField(f) { | ||||
| 		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; | ||||
| 		if(f.tagName == "GROUP"){ | ||||
| 			field = new Serializer(f) | ||||
| 		if (f.tagName == "GROUP") { | ||||
| 			field = new Serializer(f); | ||||
| 		} else { | ||||
| 			field = new SerializerField(f); | ||||
| 		} | ||||
| 
 | ||||
| 		if(field.type == "file"){ | ||||
| 		if (field.type == "file") { | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		field.name = fieldName; | ||||
| 		field.parent = this; | ||||
| 
 | ||||
| 		f.serializer = field | ||||
| 		f.serializer = field; | ||||
| 
 | ||||
| 		if(isArray){ | ||||
| 			if(!this._fields.has(fieldName)){ | ||||
| 				this._fields.set(fieldName, []) | ||||
| 		if (isArray) { | ||||
| 			if (!this._fields.has(fieldName)) { | ||||
| 				this._fields.set(fieldName, []); | ||||
| 			} | ||||
| 			if(isArray[1] != ""){ | ||||
| 				this._fields.get(fieldName)[isArray[1]] = field | ||||
| 			if (isArray[1] != "") { | ||||
| 				this._fields.get(fieldName)[isArray[1]] = field; | ||||
| 			} else { | ||||
| 				this._fields.get(fieldName).push(field) | ||||
| 				this._fields.get(fieldName).push(field); | ||||
| 			} | ||||
| 		} else { | ||||
| 			this._fields.set(fieldName, field) | ||||
| 			this._fields.set(fieldName, field); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -120,12 +137,12 @@ class Serializer { | |||
| 	 * | ||||
| 	 * @param {(HTMLElement|Serializer|SerializerField)} f Serializer child | ||||
| 	 */ | ||||
| 	removeField(f){ | ||||
| 		if(this._fields.has(f.name)){ | ||||
| 			let fields = this._fields.get(f.name) | ||||
| 			if(Array.isArray(fields)){ | ||||
| 	removeField(f) { | ||||
| 		if (this._fields.has(f.name)) { | ||||
| 			let fields = this._fields.get(f.name); | ||||
| 			if (Array.isArray(fields)) { | ||||
| 				let index = fields.indexOf(f); | ||||
| 				this._fields.set(f.name, fields.splice(index, 1)) | ||||
| 				this._fields.set(f.name, fields.splice(index, 1)); | ||||
| 			} else { | ||||
| 				this._fields.delete(); | ||||
| 			} | ||||
|  | @ -137,20 +154,20 @@ class Serializer { | |||
| 	 * | ||||
| 	 * @returns {Object} | ||||
| 	 */ | ||||
| 	serialize(){ | ||||
| 	serialize() { | ||||
| 		let json = {}; | ||||
| 		for(let [k, f] of this._fields){ | ||||
| 			if(Array.isArray(f)){ | ||||
| 				if(k != ""){ | ||||
| 		for (let [k, f] of this._fields) { | ||||
| 			if (Array.isArray(f)) { | ||||
| 				if (k != "") { | ||||
| 					json[k] = []; | ||||
| 				} else { | ||||
| 					json = []; | ||||
| 				} | ||||
| 				for(let key in f){ | ||||
| 					if(f[key]){ | ||||
| 				for (let key in f) { | ||||
| 					if (f[key]) { | ||||
| 						let d = f[key].serialize(); | ||||
| 						if(d !== null){ | ||||
| 							if(k == ""){ | ||||
| 						if (d !== null) { | ||||
| 							if (k == "") { | ||||
| 								json[key] = d; | ||||
| 								continue; | ||||
| 							} | ||||
|  | @ -160,14 +177,14 @@ class Serializer { | |||
| 				} | ||||
| 			} else { | ||||
| 				let d = f.serialize(); | ||||
| 				if(f !== null){ | ||||
| 					if(k == ""){ | ||||
| 						json = d | ||||
| 				if (f !== null) { | ||||
| 					if (k == "") { | ||||
| 						json = d; | ||||
| 						continue; | ||||
| 					} | ||||
| 					json[k] = d | ||||
| 					json[k] = d; | ||||
| 				} | ||||
| 			}	 | ||||
| 			} | ||||
| 		} | ||||
| 		return json; | ||||
| 	} | ||||
|  | @ -181,18 +198,18 @@ class Validator { | |||
| 	 * @param {(erializer|SerializerField)} field | ||||
| 	 * @returns {Object} | ||||
| 	 */ | ||||
| 	static validateRequired(field){ | ||||
| 	static validateRequired(field) { | ||||
| 		let e = { | ||||
| 			valid: true, | ||||
| 			errors: {}, | ||||
| 		} | ||||
| 			errors: {} | ||||
| 		}; | ||||
| 		let v; | ||||
| 		if(field instanceof Serializer){ | ||||
| 		if (field instanceof Serializer) { | ||||
| 			v = Validator.validateRequiredSerializer(field); | ||||
| 		} else { | ||||
| 			v = Validator.validateRequiredSerialzerField(field) | ||||
| 			v = Validator.validateRequiredSerialzerField(field); | ||||
| 		} | ||||
| 		if(!v.valid){ | ||||
| 		if (!v.valid) { | ||||
| 			e.valid = false; | ||||
| 			e.errors = v.errors; | ||||
| 		} | ||||
|  | @ -206,43 +223,43 @@ class Validator { | |||
| 	 * @param {Serializer} field | ||||
| 	 * @returns {Object} | ||||
| 	 */ | ||||
| 	static validateRequiredSerializer(field){ | ||||
| 	static validateRequiredSerializer(field) { | ||||
| 		let e = { | ||||
| 			valid: true, | ||||
| 			errors: {}, | ||||
| 		} | ||||
| 		 | ||||
| 			errors: {} | ||||
| 		}; | ||||
| 
 | ||||
| 		let min = field.min; | ||||
| 		let validFieldsCount = 0; | ||||
| 		for(let [k, f] of field._fields){ | ||||
| 			if(Array.isArray(f)){ | ||||
| 		for (let [k, f] of field._fields) { | ||||
| 			if (Array.isArray(f)) { | ||||
| 				e.errors = { | ||||
| 					message: null, | ||||
| 					message: null | ||||
| 				}; | ||||
| 				let validCount = 0; | ||||
| 				for(let sf of f){ | ||||
| 					if(!(sf.name in e.errors)){ | ||||
| 				for (let sf of f) { | ||||
| 					if (!(sf.name in e.errors)) { | ||||
| 						e.errors[sf.name] = []; | ||||
| 					} | ||||
| 
 | ||||
| 					let v = Validator.validateRequired(sf) | ||||
| 					if(!v.valid){ | ||||
| 						console.log(e.errors) | ||||
| 						e.errors[sf.name].push(v.errors);		 | ||||
| 					let v = Validator.validateRequired(sf); | ||||
| 					if (!v.valid) { | ||||
| 						console.log(e.errors); | ||||
| 						e.errors[sf.name].push(v.errors); | ||||
| 						continue; | ||||
| 					} | ||||
| 					validCount++ | ||||
| 					validCount++; | ||||
| 				} | ||||
| 				 | ||||
| 				e.valid = min <= validCount | ||||
| 				if(e.valid){ | ||||
| 					e.errors = {} | ||||
| 
 | ||||
| 				e.valid = min <= validCount; | ||||
| 				if (e.valid) { | ||||
| 					e.errors = {}; | ||||
| 				} else { | ||||
| 					e.errors.message = `Should contain atleast ${min} value(s)` | ||||
| 					e.errors.message = `Should contain atleast ${min} value(s)`; | ||||
| 				} | ||||
| 			} else { | ||||
| 				let v = Validator.validateRequired(f); | ||||
| 				if(!v.valid){ | ||||
| 				if (!v.valid) { | ||||
| 					e.valid = false; | ||||
| 					e.errors[f.name] = v.errors; | ||||
| 				} | ||||
|  | @ -259,19 +276,19 @@ class Validator { | |||
| 	 * @param {SerializerField} field | ||||
| 	 * @returns {Object} | ||||
| 	 */ | ||||
| 	static validateRequiredSerialzerField(field){ | ||||
| 	static validateRequiredSerialzerField(field) { | ||||
| 		let e = { | ||||
| 			valid: true, | ||||
| 			errors: {}, | ||||
| 		} | ||||
| 			errors: {} | ||||
| 		}; | ||||
| 
 | ||||
| 		if(field.required || (field.parent && field.parent.required)){ | ||||
| 			if(field.type != "checkbox"){ | ||||
| 				e.valid = field.field.validity.valueMissing !== true | ||||
| 				e.errors = "Is required" | ||||
| 		if (field.required || (field.parent && field.parent.required)) { | ||||
| 			if (field.type != "checkbox") { | ||||
| 				e.valid = field.field.validity.valueMissing !== true; | ||||
| 				e.errors = "Is required"; | ||||
| 			} else { | ||||
| 				e.valid = field.field.checked | ||||
| 				e.errors = "Not checked" | ||||
| 				e.valid = field.field.checked; | ||||
| 				e.errors = "Not checked"; | ||||
| 			} | ||||
| 		} | ||||
| 		return e; | ||||
|  |  | |||
|  | @ -24,6 +24,10 @@ var testData = []*Person{ | |||
| 		Surname:   "Bar", | ||||
| 		Age:       57, | ||||
| 		BirthDate: time.Date(1960, 1, 1, 0, 0, 0, 0, time.UTC), | ||||
| 		Animal:    "Dog", | ||||
| 		Checklist: []string{ | ||||
| 			"Milk", | ||||
| 		}, | ||||
| 	}, | ||||
| 	nil, | ||||
| 	&Person{ | ||||
|  | @ -32,22 +36,26 @@ var testData = []*Person{ | |||
| 		Age:       22, | ||||
| 		BirthDate: time.Date(1995, 1, 5, 0, 0, 0, 0, time.UTC), | ||||
| 		Languages: []Language{ | ||||
| 			Language{ | ||||
| 			{ | ||||
| 				Language:   "Dutch", | ||||
| 				SkillLevel: 9.99, | ||||
| 				Native:     true, | ||||
| 			}, | ||||
| 			Language{ | ||||
| 			{ | ||||
| 				Language:   "English", | ||||
| 				SkillLevel: 8.4, | ||||
| 				Native:     false, | ||||
| 			}, | ||||
| 			Language{ | ||||
| 			{ | ||||
| 				Language:   "Japanese", | ||||
| 				SkillLevel: -9000.9, | ||||
| 				Native:     false, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Animal: "Dog", | ||||
| 		Checklist: []string{ | ||||
| 			"Milk", | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
|  | @ -84,9 +92,11 @@ type Person struct { | |||
| 	Age       int        `json:"age"` | ||||
| 	BirthDate time.Time  `json:"birth_date"` | ||||
| 	Languages []Language `json:"languages"` | ||||
| 	Checklist []string   `json:"checklist"` | ||||
| 	Animal    string     `json:"animal"` | ||||
| } | ||||
| 
 | ||||
| // Languages ... | ||||
| // Language ... | ||||
| type Language struct { | ||||
| 	Language   string  `json:"language"` | ||||
| 	SkillLevel float64 `json:"skill_level"` | ||||
|  | @ -129,14 +139,21 @@ func isEq(p1, p2 Person) { | |||
| 	assert(p1.Surname, p2.Surname, "Surname") | ||||
| 	assert(p1.Age, p2.Age, "Age") | ||||
| 	assert(p1.BirthDate, p2.BirthDate, "BirthDate") | ||||
| 	assert(p1.Animal, p2.Animal, "Animal") | ||||
| 	if !assert(len(p2.Languages), len(p1.Languages), "Languages length") { | ||||
| 		return | ||||
| 	} | ||||
| 	if !assert(len(p2.Checklist), len(p1.Checklist), "Checklist length") { | ||||
| 		return | ||||
| 	} | ||||
| 	for k := range p2.Languages { | ||||
| 		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].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 { | ||||
|  |  | |||
|  | @ -32,6 +32,14 @@ | |||
| 								<input name="skill_level" type="number" value="{{ .SkillLevel }}"> | ||||
| 							</group> | ||||
| 						{{ 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> | ||||
| 				{{ end }} | ||||
| 			{{ end }} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue