diff --git a/package/gluon-web/files/lib/gluon/web/view/model/lvalue.html b/package/gluon-web/files/lib/gluon/web/view/model/lvalue.html
index 8cbcc2d77ada9c3779f8adea7ad40cebf142d0dd..85ce596ad1470dc371ad2306323101b398708dbe 100644
--- a/package/gluon-web/files/lib/gluon/web/view/model/lvalue.html
+++ b/package/gluon-web/files/lib/gluon/web/view/model/lvalue.html
@@ -1,40 +1,43 @@
 <%
 	local br = self.orientation == "horizontal" and '&#160;&#160;&#160;' or '<br />'
+	local entries = self:entries()
 %>
 
 <% if self.widget == "select" then %>
 	<select class="gluon-input-select" data-update="change"<%=
 		attr("id", id) ..
 		attr("name", id) ..
-		attr("size", self.size)
+		attr("size", self.size) ..
+		attr("data-type", "minlength(1)") ..
+		attr("data-optional", self.optional)
 	%>>
-		<% for i, key in pairs(self.keylist) do -%>
+		<% for i, entry in pairs(entries) do -%>
 			<option<%=
-				attr("id", id.."."..key) ..
-				attr("value", key) ..
+				attr("id", id.."."..entry.key) ..
+				attr("value", entry.key) ..
 				attr("data-index", i) ..
-				attr("data-depends", self:deplist(self.valdeps[i])) ..
-				attr("selected", (self:cfgvalue() == key) and "selected")
-			%>><%=pcdata(self.vallist[i])%></option>
+				attr("data-depends", self:deplist(entry.deps)) ..
+				attr("selected", (self:cfgvalue() == entry.key) and "selected")
+			%>><%=pcdata(entry.value)%></option>
 		<%- end %>
 	</select>
 <% elseif self.widget == "radio" then %>
 	<div>
-		<% for i, key in pairs(self.keylist) do %>
+		<% for i, entry in pairs(entries) do %>
 			<label<%=
 				attr("data-index", i) ..
-				attr("data-depends", self:deplist(self.valdeps[i]))
+				attr("data-depends", self:deplist(entry.deps))
 			%>>
 				<input class="gluon-input-radio" data-update="click change" type="radio"<%=
-					attr("id", id.."."..key) ..
+					attr("id", id.."."..entry.key) ..
 					attr("name", id) ..
-					attr("value", key) ..
-					attr("checked", (self:cfgvalue() == key) and "checked")
+					attr("value", entry.key) ..
+					attr("checked", (self:cfgvalue() == entry.key) and "checked")
 				%> />
-				<label<%= attr("for", id.."."..key)%>></label>
-				<%=pcdata(self.vallist[i])%>
+				<label<%= attr("for", id.."."..entry.key)%>></label>
+				<%=pcdata(entry.value)%>
 			</label>
-			<% if i ~= #self.keylist then write(br) end %>
+			<% if i ~= #entries then write(br) end %>
 		<% end %>
 	</div>
 <% end %>
diff --git a/package/gluon-web/files/lib/gluon/web/www/static/resources/gluon-web.js b/package/gluon-web/files/lib/gluon/web/www/static/resources/gluon-web.js
index e572eebd30ff197a97d99234ffc12771d2f9123b..969ad5442f4ee7c4e8d357b182d449bc6cad15b0 100644
--- a/package/gluon-web/files/lib/gluon/web/www/static/resources/gluon-web.js
+++ b/package/gluon-web/files/lib/gluon/web/www/static/resources/gluon-web.js
@@ -1 +1 @@
-!function(){var e={};function t(e){return/^-?\d+$/.test(e)?+e:NaN}function n(e){return/^-?\d*\.?\d+?$/.test(e)?+e:NaN}var a={integer:function(){return!isNaN(t(this))},uinteger:function(){return t(this)>=0},float:function(){return!isNaN(n(this))},ufloat:function(){return n(this)>=0},ipaddr:function(){return a.ip4addr.apply(this)||a.ip6addr.apply(this)},ip4addr:function(){var e;return!!(e=this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))&&(e[1]>=0&&e[1]<=255&&e[2]>=0&&e[2]<=255&&e[3]>=0&&e[3]<=255&&e[4]>=0&&e[4]<=255)},ip6addr:function(){return this.indexOf("::")<0?null!=this.match(/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i):!(this.indexOf(":::")>=0||this.match(/::.+::/)||this.match(/^:[^:]/)||this.match(/[^:]:$/))&&(!!this.match(/^(?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}$/i)||(!!this.match(/^(?:[a-f0-9]{1,4}:){7}:$/i)||!!this.match(/^:(?::[a-f0-9]{1,4}){7}$/i)))},wpakey:function(){var e=this;return 64==e.length?null!=e.match(/^[a-f0-9]{64}$/i):e.length>=8&&e.length<=63},range:function(e,t){var a=n(this);return a>=+e&&a<=+t},min:function(e){return n(this)>=+e},max:function(e){return n(this)<=+e},irange:function(e,n){var a=t(this);return a>=+e&&a<=+n},imin:function(e){return t(this)>=+e},imax:function(e){return t(this)<=+e},minlength:function(e){return(""+this).length>=+e},maxlength:function(e){return(""+this).length<=+e}};function r(e){for(var t=0;t<e.length;t++){var n=!0;for(var a in e[t])n=n&&(r=a,i=e[t][a],o=void 0,(o=document.getElementById(r))?("checkbox"==o.type?o.checked:o.value?o.value:"")==i:"radio"==(o=document.getElementById(r+"."+i)).type&&o.checked);if(n)return!0}var r,i,o;return!1}function i(){var t=!1;for(var n in e){var a=e[n],o=document.getElementById(n),d=document.getElementById(a.parent);if(o&&o.parentNode&&!r(a.deps))o.parentNode.removeChild(o),t=!0;else if(d&&(!o||!o.parentNode)&&r(a.deps)){var u=void 0;for(u=d.firstChild;u&&!(u.getAttribute&&parseInt(u.getAttribute("data-index"),10)>a.index);u=u.nextSibling);u?d.insertBefore(a.node,u):d.appendChild(a.node),t=!0}d&&d.parentNode&&d.getAttribute("data-optionals")&&(d.parentNode.style.display=d.options.length<=1?"none":"")}t&&i()}function o(e,t,n,a){return e.addEventListener?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,function(){var e=window.event;return!e.target&&e.srcElement&&(e.target=e.srcElement),!!n(e)}),e}function d(e,t){var n=t.prefix;function a(a,l,s){for(var c=[];e.firstChild;){var p=e.firstChild;(f=+p.index)!=s&&("input"==p.nodeName.toLowerCase()?c.push(p.value||""):"select"==p.nodeName.toLowerCase()&&(c[c.length-1]=p.options[p.selectedIndex].value)),e.removeChild(p)}l>=0?(a=l+1,c.splice(l,0,"")):t.optional||0!=c.length||c.push("");for(var f=1;f<=c.length;f++){var v=document.createElement("input");if(v.id=n+"."+f,v.name=n,v.value=c[f-1],v.type="text",v.index=f,v.className="gluon-input-text",t.size&&(v.size=t.size),t.placeholder&&(v.placeholder=t.placeholder),e.appendChild(v),t.type&&u(v,!1,t.type),o(v,"keydown",i),o(v,"keypress",r),f==a)v.focus();else if(-f==a){v.focus();var h=v.value;v.value=" ",v.value=h}if(t.optional||c.length>1)(m=document.createElement("span")).className="gluon-remove",e.appendChild(m),o(m,"click",d(!1)),e.appendChild(document.createElement("br"))}var m;(m=document.createElement("span")).className="gluon-add",e.appendChild(m),o(m,"click",d(!0))}function r(e){var t=(e=e||window.event).target?e.target:e.srcElement;switch(3==t.nodeType&&(t=t.parentNode),e.keyCode){case 8:case 46:return 0!=t.value.length||(e.preventDefault&&e.preventDefault(),!1);case 13:case 38:case 40:return e.preventDefault&&e.preventDefault(),!1}return!0}function i(e){var t,r,i=(e=e||window.event).target?e.target:e.srcElement,o=0;if(i){for(3==i.nodeType&&(i=i.parentNode),o=i.index,t=i.previousSibling;t&&t.name!=n;)t=t.previousSibling;for(r=i.nextSibling;r&&r.name!=n;)r=r.nextSibling}switch(e.keyCode){case 8:case 46:if("select"==i.nodeName.toLowerCase()||0==i.value.length){e.preventDefault&&e.preventDefault();var d=i.index;return 8==e.keyCode&&(d=1-d),a(d,-1,o),!1}break;case 13:a(-1,o,-1);break;case 38:t&&t.focus();break;case 40:r&&r.focus()}return!0}function d(e){return function(t){for(var a=((t=t||window.event).target?t.target:t.srcElement).previousSibling;a&&a.name!=n;)a=a.previousSibling;return e?i({target:a,keyCode:13}):(a.value="",i({target:a,keyCode:8})),!1}}a(NaN,-1,-1)}function u(e,t,n){var r,i,d,u=(d=(r=n).match(/^([^\(]+)\(([^,]+),([^\)]+)\)$/))&&void 0!==(i=a[d[1]])?function(){return i.apply(this,[d[2],d[3]])}:(d=r.match(/^([^\(]+)\(([^,\)]+)\)$/))&&void 0!==(i=a[d[1]])?function(){return i.apply(this,[d[2]])}:a[r];if(u){var l=function(){if(e.form){e.className=e.className.replace(/ gluon-input-invalid/g,"");var n=e.options&&e.options.selectedIndex>-1?e.options[e.options.selectedIndex].value:e.value;0==n.length&&t||u.apply(n)||(e.className+=" gluon-input-invalid")}};o(e,"blur",l),o(e,"keyup",l),"SELECT"==e.nodeName&&(o(e,"change",l),o(e,"click",l)),l()}}!function(){var t,n,a,r,l;t=document.querySelectorAll("[data-depends]");for(var s=0;void 0!==(g=t[s]);s++){var c=parseInt(g.getAttribute("data-index"),10),p=JSON.parse(g.getAttribute("data-depends"));if(!isNaN(c)&&p.length>0)for(var f=0;f<p.length;f++)n=g,a=p[f],r=c,l=void 0,(l=e[n.id])||(l={node:n,parent:n.parentNode.id,deps:[],index:r},e[n.id]=l),l.deps.push(a)}t=document.querySelectorAll("[data-update]");for(s=0;void 0!==(g=t[s]);s++)for(var v,h=g.getAttribute("data-update").split(" "),m=0;void 0!==(v=h[m]);m++)o(g,v,i);t=document.querySelectorAll("[data-type]");for(s=0;void 0!==(g=t[s]);s++)u(g,"true"===g.getAttribute("data-optional"),g.getAttribute("data-type"));t=document.querySelectorAll("[data-dynlist]");var g;for(s=0;void 0!==(g=t[s]);s++){d(g,JSON.parse(g.getAttribute("data-dynlist")))}i()}()}();
\ No newline at end of file
+!function(){var e={};function t(e){return/^-?\d+$/.test(e)?+e:NaN}function n(e){return/^-?\d*\.?\d+?$/.test(e)?+e:NaN}var a={integer:function(){return!isNaN(t(this))},uinteger:function(){return t(this)>=0},float:function(){return!isNaN(n(this))},ufloat:function(){return n(this)>=0},ipaddr:function(){return a.ip4addr.apply(this)||a.ip6addr.apply(this)},ip4addr:function(){var e;return!!(e=this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))&&(e[1]>=0&&e[1]<=255&&e[2]>=0&&e[2]<=255&&e[3]>=0&&e[3]<=255&&e[4]>=0&&e[4]<=255)},ip6addr:function(){return this.indexOf("::")<0?null!=this.match(/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i):!(this.indexOf(":::")>=0||this.match(/::.+::/)||this.match(/^:[^:]/)||this.match(/[^:]:$/))&&(!!this.match(/^(?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}$/i)||(!!this.match(/^(?:[a-f0-9]{1,4}:){7}:$/i)||!!this.match(/^:(?::[a-f0-9]{1,4}){7}$/i)))},wpakey:function(){var e=this;return 64==e.length?null!=e.match(/^[a-f0-9]{64}$/i):e.length>=8&&e.length<=63},range:function(e,t){var a=n(this);return a>=+e&&a<=+t},min:function(e){return n(this)>=+e},max:function(e){return n(this)<=+e},irange:function(e,n){var a=t(this);return a>=+e&&a<=+n},imin:function(e){return t(this)>=+e},imax:function(e){return t(this)<=+e},minlength:function(e){return(""+this).length>=+e},maxlength:function(e){return(""+this).length<=+e}};function r(e){for(var t=0;t<e.length;t++){var n=!0;for(var a in e[t])n=n&&(r=a,i=e[t][a],o=void 0,(o=document.getElementById(r))?("checkbox"==o.type?o.checked:o.value?o.value:"")==i:"radio"==(o=document.getElementById(r+"."+i)).type&&o.checked);if(n)return!0}var r,i,o;return!1}function i(){var t=!1;for(var n in e){var a=e[n],o=document.getElementById(n),d=document.getElementById(a.parent);if(o&&o.parentNode&&!r(a.deps))o.parentNode.removeChild(o),t=!0;else if(d&&(!o||!o.parentNode)&&r(a.deps)){var u=void 0;for(u=d.firstChild;u&&!(u.getAttribute&&parseInt(u.getAttribute("data-index"),10)>a.index);u=u.nextSibling);u?d.insertBefore(a.node,u):d.appendChild(a.node),t=!0}d&&d.parentNode&&d.getAttribute("data-optionals")&&(d.parentNode.style.display=d.options.length<=1?"none":"")}t&&i()}function o(e,t,n,a){return e.addEventListener?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,function(){var e=window.event;return!e.target&&e.srcElement&&(e.target=e.srcElement),!!n(e)}),e}function d(e,t){var n=t.prefix;function a(a,l,s){for(var c=[];e.firstChild;){var p=e.firstChild;(f=+p.index)!=s&&("input"==p.nodeName.toLowerCase()?c.push(p.value||""):"select"==p.nodeName.toLowerCase()&&(c[c.length-1]=p.options[p.selectedIndex].value)),e.removeChild(p)}l>=0?(a=l+1,c.splice(l,0,"")):t.optional||0!=c.length||c.push("");for(var f=1;f<=c.length;f++){var v=document.createElement("input");if(v.id=n+"."+f,v.name=n,v.value=c[f-1],v.type="text",v.index=f,v.className="gluon-input-text",t.size&&(v.size=t.size),t.placeholder&&(v.placeholder=t.placeholder),e.appendChild(v),t.type&&u(v,!1,t.type),o(v,"keydown",i),o(v,"keypress",r),f==a)v.focus();else if(-f==a){v.focus();var h=v.value;v.value=" ",v.value=h}if(t.optional||c.length>1)(m=document.createElement("span")).className="gluon-remove",e.appendChild(m),o(m,"click",d(!1)),e.appendChild(document.createElement("br"))}var m;(m=document.createElement("span")).className="gluon-add",e.appendChild(m),o(m,"click",d(!0))}function r(e){var t=(e=e||window.event).target?e.target:e.srcElement;switch(3==t.nodeType&&(t=t.parentNode),e.keyCode){case 8:case 46:return 0!=t.value.length||(e.preventDefault&&e.preventDefault(),!1);case 13:case 38:case 40:return e.preventDefault&&e.preventDefault(),!1}return!0}function i(e){var t,r,i=(e=e||window.event).target?e.target:e.srcElement,o=0;if(i){for(3==i.nodeType&&(i=i.parentNode),o=i.index,t=i.previousSibling;t&&t.name!=n;)t=t.previousSibling;for(r=i.nextSibling;r&&r.name!=n;)r=r.nextSibling}switch(e.keyCode){case 8:case 46:if("select"==i.nodeName.toLowerCase()||0==i.value.length){e.preventDefault&&e.preventDefault();var d=i.index;return 8==e.keyCode&&(d=1-d),a(d,-1,o),!1}break;case 13:a(-1,o,-1);break;case 38:t&&t.focus();break;case 40:r&&r.focus()}return!0}function d(e){return function(t){for(var a=((t=t||window.event).target?t.target:t.srcElement).previousSibling;a&&a.name!=n;)a=a.previousSibling;return e?i({target:a,keyCode:13}):(a.value="",i({target:a,keyCode:8})),!1}}a(NaN,-1,-1)}function u(e,t,n){var r,i,d,u=(d=(r=n).match(/^([^\(]+)\(([^,]+),([^\)]+)\)$/))&&void 0!==(i=a[d[1]])?function(){return i.apply(this,[d[2],d[3]])}:(d=r.match(/^([^\(]+)\(([^,\)]+)\)$/))&&void 0!==(i=a[d[1]])?function(){return i.apply(this,[d[2]])}:a[r];if(u){var l=function(){if(e.form){e.className=e.className.replace(/ gluon-input-invalid/g,"");var n=e.options&&e.options.selectedIndex>-1?e.options[e.options.selectedIndex].value:e.value;0==n.length&&t||u.apply(n)||(e.className+=" gluon-input-invalid")}};o(e,"blur",l),o(e,"keyup",l),"select"==e.nodeName.toLowerCase()&&(o(e,"change",l),o(e,"click",l)),l()}}!function(){var t,n,a,r,l;t=document.querySelectorAll("[data-depends]");for(var s=0;void 0!==(g=t[s]);s++){var c=parseInt(g.getAttribute("data-index"),10),p=JSON.parse(g.getAttribute("data-depends"));if(!isNaN(c)&&p.length>0)for(var f=0;f<p.length;f++)n=g,a=p[f],r=c,l=void 0,(l=e[n.id])||(l={node:n,parent:n.parentNode.id,deps:[],index:r},e[n.id]=l),l.deps.push(a)}t=document.querySelectorAll("[data-update]");for(s=0;void 0!==(g=t[s]);s++)for(var v,h=g.getAttribute("data-update").split(" "),m=0;void 0!==(v=h[m]);m++)o(g,v,i);t=document.querySelectorAll("[data-type]");for(s=0;void 0!==(g=t[s]);s++)u(g,"true"===g.getAttribute("data-optional"),g.getAttribute("data-type"));t=document.querySelectorAll("[data-dynlist]");var g;for(s=0;void 0!==(g=t[s]);s++){d(g,JSON.parse(g.getAttribute("data-dynlist")))}i()}()}();
\ No newline at end of file
diff --git a/package/gluon-web/javascript/gluon-web.js b/package/gluon-web/javascript/gluon-web.js
index b8989878f27813d997eaf0e0ea60383dd5aab2bc..5a4c9a616183be601e957d33bbc8171574ddfcd9 100644
--- a/package/gluon-web/javascript/gluon-web.js
+++ b/package/gluon-web/javascript/gluon-web.js
@@ -471,7 +471,7 @@
 		bind(field, "blur",  validator);
 		bind(field, "keyup", validator);
 
-		if (field.nodeName == 'SELECT') {
+		if (field.nodeName.toLowerCase() == 'select') {
 			bind(field, "change", validator);
 			bind(field, "click",  validator);
 		}
@@ -497,7 +497,6 @@
 		var nodes;
 
 		nodes = document.querySelectorAll('[data-depends]');
-
 		for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
 			var index = parseInt(node.getAttribute('data-index'), 10);
 			var depends = JSON.parse(node.getAttribute('data-depends'));
@@ -509,7 +508,6 @@
 		}
 
 		nodes = document.querySelectorAll('[data-update]');
-
 		for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
 			var events = node.getAttribute('data-update').split(' ');
 			for (var j = 0, event; (event = events[j]) !== undefined; j++) {
@@ -518,14 +516,12 @@
 		}
 
 		nodes = document.querySelectorAll('[data-type]');
-
 		for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
 			validate_field(node, node.getAttribute('data-optional') === 'true',
-			                   node.getAttribute('data-type'));
+				node.getAttribute('data-type'));
 		}
 
 		nodes = document.querySelectorAll('[data-dynlist]');
-
 		for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
 			var attr = JSON.parse(node.getAttribute('data-dynlist'));
 
diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua
index e4257930bac22c5a8f13f4957b6bcd35a86d7aa8..27aee325057b193d54b5505d180a7eb155f7e235 100644
--- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua
+++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/model.lua
@@ -396,24 +396,52 @@ function ListValue:__init__(...)
 	self.size = 1
 	self.widget = "select"
 
-	self.keylist = {}
-	self.vallist = {}
-	self.valdeps = {}
+	self.keys = {}
+	self.entry_list = {}
 end
 
 function ListValue:value(key, val, ...)
-	if util.contains(self.keylist, key) then
+	if self.keys[key] then
 		return
 	end
 
 	val = val or key
-	table.insert(self.keylist, tostring(key))
-	table.insert(self.vallist, tostring(val))
-	table.insert(self.valdeps, {...})
+	self.keys[key] = true
+	table.insert(self.entry_list, {
+		key = tostring(key),
+		value = tostring(val),
+		deps = {...},
+	})
+end
+
+function ListValue:entries()
+	local ret = {unpack(self.entry_list)}
+
+	if self:cfgvalue() == nil or self.optional then
+		table.insert(ret, 1, {
+			key = '',
+			value = '',
+			deps = {},
+		})
+	end
+
+	return ret
 end
 
 function ListValue:validate()
-	return util.contains(self.keylist, self.data)
+	if self.keys[self.data] then
+		return true
+	end
+
+	if type(self.data) == "string" and #self.data == 0 then
+		self.data = nil
+	end
+
+	if self.data == nil then
+		return self.optional
+	end
+
+	return false
 end