parser

Написать ответ на текущее сообщение

 

 
   команды управления поиском

WebForms: валидация форм

Азат Разетдинов 17.05.2007 21:08 / 17.05.2007 21:20

Решил реализовать давнюю идею и написал класс для серверной валидации форм с использованием атрибутов, описанных в WebForms 2.0

Основные положения:

1. Все элементы формы называются field (это не описано в WebForms, но используется мной в xml-представлении формы).

2. Допустимы следующие дополнительные типы: datetime, datetime-local, date, time, month, week, time, number, range, email, url.

3. Допустимы следующие допольнительные атрибуты: required (поле должно быть заполнено), pattern (поле должно целиком соответствовать этому регулярному выражению), title (выводится, если поле не соответствует pattern), customError (флаг, задаётся скриптом, выводящим xml формы), validationMessage (выводится, если горит флаг customError).

4. В процессе валидации у элементов формы могут появиться следующие атрибуты-флаги (для последующей обработки в xslt): valueMissing (поле имеет атрибут required, но не заполнено), patternMismatch (поле имеет атрибут pattern, но не соответствует ему), typeMismatch (поле не соответствует своему типу), tooLong (длина поля превышает значение атрибута maxlength), valid=1/0 (равен нулю, если горит любой из предыдущих).

Недостатки:

1. Класс не умеет работать со зависимыми полями/блоками.

2. Поля title и validationMessage языкозависимы. Впрочем, этим занимается xslt, сам класс с этими атрибутами не работает.

Пример использования:
# выбираем все формы
$hForms[^xPage.select[//form]]
^for[i](0;$hForms-1){
#	выбираем все кнопки формы
	$hButtons[^hForms.$i.select[//field[@type='submit']]]
	^for[j](0;$hButtons-1){
		$sAction[^hButtons.$j.getAttribute[name]]
#		если пользователь нажал на одну из них
		^if(def $form:$sAction && $$sAction is junction){
			^use[WebForms.p]
#			и форма прошла валидацию
			^if(^WebForms:checkValidity[$hForms.$i]){
#				вызываем одноименный метод
				^self.$sAction[]
			}
		}
	}
}
Пример xslt-кода:
<xsl:if test='@valid = 0'>
	<span class='alert'>
		<xsl:if test='@valueMissing'>
			<br /><xsl:value-of select='$locale/form/valueMissing'/>
		</xsl:if>
		<xsl:if test='@typeMismatch'>
			<br /><xsl:value-of
			select='$locale/form/typeMismatch[@type=current()/@type]'/>
		</xsl:if>
		<xsl:if test='@tooLong'>
			<br /><xsl:value-of select='$locale/form/tooLong'/>
			<xsl:text> </xsl:text><xsl:value-of select='@maxlength'/>
		</xsl:if>
		<xsl:if test='@patternMismatch'>
			<br /><xsl:value-of select='@title'/>
		</xsl:if>
		<xsl:if test='@customError'>
			<br /><xsl:value-of select='@validationMessage'/>
		</xsl:if>
	</span>
</xsl:if>
Собственно, сам класс:
@CLASS
WebForms

@USE
Lib.p



@auto[]
$reDate[\d{4}-\d{2}-\d{2}]
$reTime[\d{4}:\d{2}(?::\d{2}(?:\.\d+)?)?]
$reNumber[-?\d+(?:\.\d+)?(?:e\d+)?]
$reUrl[(?:[a-z]+://)?[:\w~%{}./?=&@,#-]+]
#end @auto[]



@checkValidity[xForm][i;hFields;hAttr;sName;xAttr]
$hFields[^xForm.select[//field]]
^for[i](0;$hFields-1){
	$hAttr[^hFields.$i.attributes.foreach[sName;xAttr]{
		$.$sName[$xAttr.value]
	}]
	^hFields.$i.setAttribute[valid;1]
	^if(def $hAttr.value){
		^switch[$hAttr.type]{
			^case[datetime]{
				^if(!^hAttr.value.match[^^${reDate}T$reTime^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[datetime-local]{
				^if(!^hAttr.value.match[^^${reDate}T${reTime}Z^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[date]{
				^if(!^hAttr.value.match[^^$reDate^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[month]{
				^if(!^hAttr.value.match[^^\d{4}-\d{2}^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[week]{
				^if(!^hAttr.value.match[^^\d{4}-W\d{2}^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[time]{
				^if(!^hAttr.value.match[^^$reTime^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[number;range]{
				^if(!^hAttr.value.match[^^$reNumber^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[email]{
				^if(!^Lib:isEmail[$hAttr.value]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
			^case[url]{
				^if(!^hAttr.value.match[^^$reUrl^$]){
					^hFields.$i.setAttribute[typeMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}
		}
		^if(^hFields.$i.getAttribute[valid] == 1){
			^if(
				^hAttr.maxlength.int(0)
				&& ^hAttr.value.length[] > ^hAttr.maxlength.int(0)
			){
				^hFields.$i.setAttribute[tooLong;1]
				^hFields.$i.setAttribute[valid;0]
			}
		}
		^if(def $hAttr.pattern){
			^try{
				^if(!^hAttr.value.match[^^(?:$hAttr.pattern)^$]){
					^hFields.$i.setAttribute[patternMismatch;1]
					^hFields.$i.setAttribute[valid;0]
				}
			}{
				$exception.handled(true)
			}
		}
	}{
		^if(def $hAttr.required){
			^hFields.$i.setAttribute[valueMissing;1]
			^hFields.$i.setAttribute[valid;0]
		}
	}
	^if(def $hAttr.customError){
		^hFields.$i.setAttribute[valid;0]
	}
}
$result(!^xForm.select[//field[@valid = 0]])
#end @checkValidity[]
Комментарии и замечания, как всегда, приветствуются.