HTML5 validation / how to wrap form tag around form view

Hi there,

I am wondering how to make use of the HTML5 validation attributes when creating a webix form. To make html5 validation attributes work, the form must be rendered within a form element tag. I found an example using html5 validation at http://docs.webix.com/samples/13_form/04_validation/10_html5_validation.html

This example is using existing HTML markup but I have a JS only app. I could start creating the form tag using JS (document.createElement) before creating my webix view of type form but this doesn’t look clean to me.

Is there any “webix way” to wrap my form view within a html form tag? Or did I miss something?

Many thanks!

Without mentioned tag, HTML5 validation attributes work in Firefox, but not in Chrome.

So yes, you need to create aform tag and wrap it around the view:

http://webix.com/snippet/ed0a184c

I had some layout issues wrapping the webix view with a form tag. I fixed this by replacing the views div with a form tag using a custom component. Maybe someone stumbles upon the same issue, so here is what I did (just replace view: ‘form’ with view: ‘h5form’ to use this component):

webix.protoUI({
name:‘h5form’,
_default_height:-1,
_form_classname:“webix_form”,
_form_vertical:true,
defaults:{
type:“form”,
autoheight:true
},
$init:function(config){
var oldobj = this._viewobj;
console.log(this._viewobj);

	this._contentobj = this._viewobj = webix.html.create("FORM",{
			'class':'webix_view webix_form webix_custom_h5form',
			'action': 'javascript:0;',
		});
	
	while(oldobj.childNodes.length > 0) {
		this._viewobj.appendChild(oldobj.childNodes[0]);
	}
	this.$view = this._viewobj;
},
$getSize:function(dx, dy){
	if (this._scroll_y && !this._settings.width) dx += webix.ui.scrollSize;

	var sizes = webix.ui.layout.prototype.$getSize.call(this, dx, dy);

	if (this._settings.scroll || !this._settings.autoheight){
		sizes[2] =  this._settings.height || this._settings.minHeight || 0;
		sizes[3] += 100000;
	}
	
	return sizes;
}

}, webix.ui.toolbar);

Here is an enhanced version which automatically invalidates fields based on the html5 validation rules. There are only two caveats for using this:

a) This requires jQuery additionally

b) As of 3.2.4 webix does not add the name attribute to some input field. For now you have to do some ugly fix as described here: http://forum.webix.com/discussion/6734/bug-in-text-component

Here’s the new h5form:

if (typeof webix === ‘undefined’) {
throw new Error(‘h5form requires webix’);
}
if (typeof jQuery === ‘undefined’) {
throw new Error(‘h5form requires jQuery’);
}
webix.protoUI({
name:‘h5form’,
_default_height:-1,
_form_classname:“webix_form”,
_form_vertical:true,
defaults:{
type:“form”,
autoheight:true
},
$init:function(config){
var oldobj = this._viewobj;

	this._contentobj = this._viewobj = webix.html.create("FORM",{
			'class':'webix_view webix_form webix_custom_h5form',
			'action': 'javascript:0;',
		});
	
	while(oldobj.childNodes.length > 0) {
		this._viewobj.appendChild(oldobj.childNodes[0]);
	}
	this.$view = this._viewobj;
	
	var self = this;
	
	config.elementsConfig = config.elementsConfig || {};
	config.elementsConfig.on = config.elementsConfig.on || {};
	
	var savedOnChange = config.elementsConfig.on.onChange;
	config.elementsConfig.on.onChange = function(newV, oldV) {
		if (typeof savedOnChange === 'function')
			savedOnChange.call(this, newVm, oldV);			
		if (!self._validateFormRule(this.$view))
			return;
		this.validate();
	}
	
	config.rules = config.rules || {};

	if (typeof config.rules.$all === 'function') {
		var savedAllRule = config.rules.$all;			
		config.rules.$all = function(newV, oldV, fieldName) {
			return self._validateFormRule(this.$view, fieldName) && savedAllRule.call(this, arguments);
		}
	} 
	else 
	{
		config.rules.$all = function(newV, oldV, fieldName) {
			return self._validateFormRule(this.$view, fieldName);
		}
	}
	
	$(this.$view).on('submit', function(e) {
		if (this.checkValidity && !this.checkValidity()) {
			self._validateFormRule();
			$(self.$view).find(':invalid').first().focus();
		}
		e.preventDefault();		
		return false;
	});
},
_validateFormRule: function(root, fieldName) {
	var self = this;
	root = root || this.$view;
	if (!this.$view.checkValidity || this.$view.checkValidity()) {
		return true;
	}
	
	var status = true;
	
	// Find all invalid fields within the form.
    $(root).find((fieldName?'[name='+fieldName+']' : '')+':invalid').each(function(index, node) {
		var name = $(node).attr('name');
		var mesg = (node.validationMessage || 'Please check your input.');
		if ($(node).is('[title]'))
			mesg += ' '+$(node).attr('title');
		if (name && name.length) {			
			self.markInvalid(name, mesg);
		}
		status = false;
    });
	return status;
},
$getSize:function(dx, dy){
	if (this._scroll_y && !this._settings.width) dx += webix.ui.scrollSize;

	var sizes = webix.ui.layout.prototype.$getSize.call(this, dx, dy);

	if (this._settings.scroll || !this._settings.autoheight){
		sizes[2] =  this._settings.height || this._settings.minHeight || 0;
		sizes[3] += 100000;
	}
	
	return sizes;
}

}, webix.ui.toolbar);

Thanks to maksim I have managed to get this running without any caveats and without jQuery. It’s simply a drop in replacement for the form view:

if (typeof webix === ‘undefined’)
throw new Error(‘h5form requires webix’);

webix.protoUI({
name:‘h5form’,
$init:function(config) {
var oldobj = this._viewobj;
this._contentobj = this._viewobj = webix.html.create(“FORM”,{
‘class’:‘webix_view webix_form webix_custom_h5form’,
‘action’:‘javascript:0;’,
});

	while(oldobj.childNodes.length > 0)
		this._viewobj.appendChild(oldobj.childNodes[0]);
	
	this.$view = this._viewobj;
	
	if (!this.$view.checkValidity)		
		return; // We are done if we have no validation API
	
	var self = this;
	
	config.elementsConfig = config.elementsConfig || {};
	config.elementsConfig.on = config.elementsConfig.on || {};
	
	var savedOnChange = config.elementsConfig.on.onChange;
	config.elementsConfig.on.onChange = function(newV, oldV) {
		self.clearValidation();
		self.validate();
		if (typeof savedOnChange === 'function')
			savedOnChange.call(this, newVm, oldV);
	}
	
	config.rules = config.rules || {};
	var savedAllRule = config.rules.$all;
	config.rules.$all = function(newV, oldV, fieldName) {
		
		var valid = true;
		var inputView = self.elements[fieldName];
		if (inputView) {
			var node = inputView.getInputNode();
			if (node && typeof node.willValidate !== 'undefined' && node.validity && !node.validity.valid) {
				self.markInvalid(fieldName, node.validationMessage || 'Please check your input.');
				valid = false;
			}
		}			
		return valid && (!savedAllRule !== 'function' || savedAllRule.call(this, arguments));
	}
	
	config.on = config.on || {};
	var savedHandler = config.on.onBeforeValidate;
	config.on.onBeforeValidate = function() {
		self.$view.checkValidity()
		if (savedHandler === 'function')
			savedHandler.call(this, arguments);	
	}
	
	this.$view.onsubmit = function(e) {
		self.clearValidation();
		self.validate();
		e.preventDefault();		
		return false;
	};
},

}, webix.ui.form);

Just uploaded the latest version to GitHub in case someone else has use for it: GitHub - IDNT/Webix-Components: Collection of webix components developed by IDNT