Validation

Home
View
Created by david on 2009-06-24 09:45:57.0

Validating input parameters in JSXP can be done during the validation phase in the life cycle. The framework will call the "validate" method on your view controller, and in this method you can place all your validation logic. You can use the "Validator" of your view controller, which has some convenience methods you'll probably need.

First, let's define 2 views: One which contains an input form (index.xhtml) and a result page. The style in the head of index.xhtml is to display the validation errors in a nice red block: The framework will add validation messages as flash errors (This is the default behaviour, you can change that if you want).

index.xhtml


<?xml version="1.0" encoding="UTF-8" ?>

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:jsxp="http://www.jsxp.org/spec/1.0">
	<head>
		<title>Validation Test: Input Form</title>
		<style type="text/css">
		<!--
.jsxp_flasherrors {
	border: 1px solid red;
	background: #fcc;
}
		-->
		</style>
	</head>
	<body>
		<div jsxp:id="jsxp:flash_messages"/>
		
		<p>
			First Input: Not empty, length &gt; 3, length &lt; 10<br/>
			Second Input: Only numbers<br/>
			The length of first input plus the length of second input must be less than 12
		</p>
		<form action="result.xhtml" method="post">
			First Input*: <input type="text" name="firstInput" jsxp:id="firstInput" size="40" value="(.FirstInput)"/><br/>
			Second Input: <input type="text" name="secondInput" jsxp:id="secondInput" size="40" value="(.SecondInput)"/><br/>
			<input type="submit" value="Submit"/>
		</form>
	</body>
</html>

The variables "(.FirstInput)" and "(.SecondInput)" are needed later, when we want to restore the original user input after a validation error.

result.xhtml


<?xml version="1.0" encoding="UTF-8" ?>

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:jsxp="http://www.jsxp.org/spec/1.0">
	<head>
		<title>Validation Test: Input Form</title>
	</head>
	<body>
		Input 1: (.Input1)<br/>
		Input 2: (.Input2)<br/>
		<a href="index.xhtml">back</a>
	</body>
</html>

The validation now takes place in the controller of the result view:

ResultXhtmlController.java


package org.jsxp.test.validationtest.web;

import org.jsxp.framework.StringUtil;
import org.jsxp.test.validationtest.web.generated.ResultXhtmlControllerGenerated;

public class ResultXhtmlController extends ResultXhtmlControllerGenerated<ResultXhtmlController.InputParameter> {

	public class InputParameter {
		private String firstInput;
		private String secondInput;
		public String getFirstInput() {
			return firstInput;
		}
		public void setFirstInput(String firstInput) {
			this.firstInput = firstInput;
		}
		public String getSecondInput() {
			return secondInput;
		}
		public void setSecondInput(String secondInput) {
			this.secondInput = secondInput;
		}
	}
	
	private InputParameter input = new InputParameter();
	
	@Override
	public InputParameter getInputParameter() {
		return input;
	}

	@Override
	public void validate() throws Exception {
		getValidator().validate(!StringUtil.isEmpty(input.firstInput), 
				"The first input must not be empty", 
				IndexXhtmlController.getFieldTypeFirstInput());
		if(!getValidator().hasErrors()) {
			getValidator().validate(input.firstInput.length()>3, 
					"The first input must contain more than 3 characters", 
					IndexXhtmlController.getFieldTypeFirstInput());
			getValidator().validate(input.firstInput.length()<10, 
					"The first input must contain less than 10 characters", 
					IndexXhtmlController.getFieldTypeFirstInput());
		}
		getValidator().validate(StringUtil.isEmpty(input.secondInput) || input.secondInput.matches("\\d*"), 
				"The second input must only contain numbers", 
				IndexXhtmlController.getFieldTypeSecondInput());
		
		if(input.firstInput!=null && input.secondInput!=null 
				&& input.firstInput.length()+input.secondInput.length()>=12) {
			getValidator().addValidationError("The length of both input fields combined must be less than 12");
		}
	}
	
	@Override
	public void execute() {
		System.out.println("Executing result.xhtml");
		setVariableInput1(input.firstInput);
		setVariableInput2(input.secondInput);
	}

}

The interesting part here is the "validate" method. To validate our input, we use the convenience methods from the "Validator" class. We get a validator object by calling "getValidator". You can use your own implementation of Validator by overriding the method "protected Validator createValidator()" in your view controller.

The validator has 3 methods:
hasErrors returns true if there have been any previous validation errors.
validate gets 2 or more parameters: A condition to validate, a message to display if the validation fails, and an optional list of Field Type classes which identify the fields that should be marked invalid in the original input form.
addValidationError gets 1 or more parameters: A message to display if the validation fails, and an optional list of Field Type classes which identify the fields that should be marked invalid in the original input form.

The default implementation of the validate method calls "addValidationError" if the condition is false. To create your own validator, you only have to override addValidationError.

In case of a validation error, the framework now skips the execution of the result view controller and redirects to the previous view (index.xhtml). In the previous view it will mark the invalid input fields (which we identified by their field info classes) red and display the validation messages as flash errors. One thing is still missing: We want to restore the previous user input in the input form of index.xhtml. The framework helps us with that, but we have to write some code in the controller of this view:

IndexXhtmlController.java


package org.jsxp.test.validationtest.web;

import org.jsxp.test.validationtest.web.generated.IndexXhtmlControllerGenerated;

public class IndexXhtmlController extends IndexXhtmlControllerGenerated<Object> {
	public class PreviousFormValues {
		private String firstInput;
		private String secondInput;
		public String getFirstInput() {
			return firstInput;
		}
		public void setFirstInput(String firstInput) {
			this.firstInput = firstInput;
		}
		public String getSecondInput() {
			return secondInput;
		}
		public void setSecondInput(String secondInput) {
			this.secondInput = secondInput;
		}
	}

	private PreviousFormValues previousValues = new PreviousFormValues();
	
	@Override
	public Object getPreviousFormValuesBean() {
		return previousValues;
	}

	@Override
	public void restorePreviousFormValues() throws Exception {
		setVariableFirstInput(previousValues.firstInput);
		setVariableSecondInput(previousValues.secondInput);
	}
	
}

We can tell the framework to fill a bean with the previous input of the form by overriding "getPreviousFormValuesBean". If a validation error happend on a result of our form, the framework will call this method, fill the returned bean with the previous input, and call the method "restorePreviousFormValues". Here we can set the values in our view.

To mark the input fields as invalid, the framework calls the method "public void markValidationErrorElement(Element element, String message)" on our application class. The default implementation adds a tooltip with the message to the element, and adds a style attribute with the value "border: 1px solid red;". You can change this behaviour by overriding the "markValidationErrorElement" in your application class.