Form Validation with Google Web Toolkit? - gwt

I'm new to GWT. What good solutions exist for form validation? I'd like to avoid rolling my own if possible.

In my GWT applications I always use my custom validator classes. I have created my own textbox class which extends gwt textbox. And I call CustomTextBox instead of gwt's textbox.
CustomTextBox.java
public class CustomTextBox extends TextBox implements HasValidators{
private static final String TEXTBOX_VALIDATION_ERROR_STYLE = "error-text-box";
private String errorMessage = "";
private List<Validator> validators = new ArrayList<Validator>();
public CustomTextBox() {
}
public CustomTextBox(String name) {
setName(name);
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public void addValidator(Validator validator) {
validators.add(validator);
}
public boolean validate() {
boolean validationResult = true;
for (Validator validator : validators) {
validationResult = validator.validate(getValue().trim());
if (!validationResult) {
errorMessage = validator.getErrorMessage();
break;
}
errorMessage = validator.getErrorMessage();
}
setErrorStyles(validationResult);
return validationResult;
}
private void setErrorStyles(boolean validationResult) {
if (validationResult) {
removeStyleName(TEXTBOX_VALIDATION_ERROR_STYLE);
setTitle("");
} else {
addStyleName(TEXTBOX_VALIDATION_ERROR_STYLE);
setTitle(errorMessage);
}
}
#Override
public void setValue(String s) {
removeStyleDependentName(TEXTBOX_VALIDATION_ERROR_STYLE);
super.setValue(s);
}
#Override
public String getValue() {
return super.getValue().trim();
}
}
Validator.java
public abstract class Validator {
public String errorMessage;
public abstract boolean validate(String value);
public abstract String getErrorMessage();
}
Sample Email validator
public class EmailValidator extends Validator {
public boolean validate(String value) {
if (value.matches("^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
errorMessage = "";
return true;
} else {
errorMessage = "Enter valid email Id";
return false;
}
}
public String getErrorMessage() {
return errorMessage;
}
}
My validation error looks like following
If you like this approach you can follow this. The problem here is we don't display the error message directly in the UI. Only in tooltip we are showing.

GXT and SmartGWT (both of them I'd highly advise against, for many reasons) have form validation.
GWT's Editor framework provides a mean to display javax.validation.ConstraintViolation (you'd still have to do the actual display by implementing HasEditorErrors to receive the errors for a given field; the Editor framework only dispatches the errors to the appropriate "fields") but nothing to validate on the client side.
The recently released, GWT 2.3 has preliminary support for JSR 303 Bean Validation on the client side but it's not yet finished: http://code.google.com/p/google-web-toolkit/wiki/BeanValidation
I think GWT 2.4 will have full (or almost full) support.
Note that GWT's stake on validation is on validating the objects, not about validating the "form fields" editing an object's properties.

We are currently using this project for forms validation:
http://code.google.com/p/gwt-validation/
It's the source code that serves as the basis of the new GWT 2.4 Validation framework.
Chris Buffalo has been doing an amazing work on that project. Works out of the box for us.

Related

No error message from Validator in spring-data-rest

Following the documentation http://docs.spring.io/spring-data/rest/docs/2.4.2.RELEASE/reference/html/#validation I set up a very simple Validator for a spring-data-rest repository invocation:
public class DealValidator implements Validator {
#Override
public boolean supports(Class<?> aClass) {
return Deal.class.isAssignableFrom(aClass);
}
#Override
public void validate(Object o, Errors errors) {
errors.reject("deal.error", "No deal");
}
}
And this is the configuration
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", new DealValidator());
}
#Configuration
static class I18nConfiguration {
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("classpath:messages");
return source;
}
}
The configuration seems to be alright, the validator is called correctly, the http-request yields an error response, but no error text is returned, neither from the messages.properties nor the default text. Is this a bug?
I came across the same issue. Only validation errors that reference a field are serialized by spring-data-rest.
So you could use rejectValue(String field, String errorCode, String defaultMessage) instead of reject
See org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage for implementation details. The implementation just processes org.springframework.validation.Errors#getFieldErrors().

Breaking cyclic dependency in Dagger

I am very new to dagger--I don't even know yet if it will work for my application
I have a search page that returns the latest news about a given celebrity.
I have written a test to verify that results appear on the page when we search for a popular celebrity.
The page has a searchField, which requires page in its constructor so the web driver I use for my tests can select it.
Celebrity Search Page Test
public class CelebritySearchPageTest {
#Test
public void testSearchResultsForKevinBaconVerifyHisPopularity() {
CelebritySearchPage searchPage = new CelebritySearchPage();
searchPage.searchFor("Kevin Bacon");
Assert.assertTrue(searchPage.getNumberOfResults() > 9999999, "Verify that Kevin Bacon is still relevant");
}
}
Celebrity Search Page
public class CelebritySearchPage extends Page {
#Inject
#Named("search field")
TextField searchField;
public void searchFor(String text) {
searchField.setText(text);
// ...
}
public int getNumberOfResults() {
// ...
}
}
Celebrity Search Page Module
#Module(injects = CelebritySearchPage.class)
public class CelebritySearchPageModule {
#Provides
#Named("search field")
public TextField provideSearchField() {
return new TextField(/* How do I get the page? */, "#searchField");
}
}
Page
public abstract class Page {
// ...
}
Text Field
public class TextField {
protected Page page;
protected String selector;
public TextField(Page page, String selector) {
this.page = page;
this.selector = selector;
}
public void setText(String text) {
// ...
}
}
The problem is that page needs searchField, but searchField needs page. How do I get over this cyclic dependency?
I can't initialize searchField inside of CelebritySearchPage
Consider this:
CelebritySearchPage
public class CelebritySearchPage extends Page {
private final Lazy<TextField> searchField;
// always prefer constructor injection
// avoid #Named if possible, since the compiler cannot check the string
#Inject
CelebritySearchPage(#Named("search field") Lazy<TextField> searchField) {
this.searchField = searchField;
}
}
Text Field
public class TextField {
protected final Lazy<Page> page;
protected final String selector;
#Inject TextField(Lazy<Page> page, String selector) {
this.page = page;
this.selector = selector;
}
/*
Lazy::get()
Return the underlying value, computing the value if necessary. All calls to the same Lazy instance will return the same result.
*/
}
I guess one Lazy should suffice as well.

NInject simplest example doesn't work

I've got three projects:
UI
PluginManager
PluginOne
PluginTwo
Inside my Plugin Manager a create a simple plugin interface:
public interface IPlugin<T>
{
void sayMessage(T message);
T createMessage();
}
So, in my other projects I've two IPlugin implementation:
In porject PluginOneProject -->
-------------------------------
public class PluginOne : IPlugin<IntMessage>
{
public void sayMessage(IntMessage message)
{
System.Console.WriteLine(message.ToString());
}
}
where:
public class IntMessage
{
private int message;
public IntMessage(int message)
{
this.message = message;
}
public override string ToString()
{
return this.message.ToString();
}
}
In porject PluginTwoProject -->
-------------------------------
public class PluginTwo : IPlugin<StringMessage>
{
public void sayMessage(StringMessage message)
{
System.Console.WriteLine(message.ToString());
}
}
where:
public class StringMessage
{
private String message;
public StringMessage(String message)
{
this.message = message;
}
public override string ToString()
{
return this.message.ToString();
}
}
Obviously, I've added the corresponding project references.
So, in my UI porject I've NInject, and I perform this convention mapping:
kernel.Bind(b => b.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom(typeof(IPlugin<>))
.BindAllInterfaces()
);
The graph is built correctly.
So, I don't know how to get an instance of PluginOne, or PluginTwo from UI project.
I'm trying to use this code, however, I've a problem with generic interfaces...
foreach (IPlugin<?> plugin in kernel.GetAll(typeof(IPlugin<>)))
{
plugin.sayMessage(plugin.createMessage());
}
Unfortunately, you must request a specific interface from Ninject, so you must request either IPlugin<IntMessage> or IPlugin<StringMessage>.
I would suggest trying to refactor your app so that you can request a more generic interface, such as IPlugin, and/or create a message interface like IMessage that each message implements. It's not clear from your question what you're trying to accomplish.

Spring List Binding

Thanks in advance for any help.
I have the following object association in my model:
public class Contract {
private Integer id;
private String name;
//getters/setters...
}
public class User {
....
private List<Contract> contracts;
....
}
Controller:
#RequestMapping(....)
public String getUser(#PathVariable Integer userId, Model model) {
....
model.addAttribute(userDao.findUser(userId));
model.addAttribute("contractsList", contractDao.findAllContracts());
....
}
#RequestMapping(....)
public String processUser(#ModelAttribute User user, Model model) {
....
//Create a copy of the user to update...
User userToUpdate = userDao.findUser(user.getId);
....
userToUpdate.setContracts(user.getContracts());
//set other properties...
userDao.updateUser(userToUpdate);
return "someSuccessView";
}
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Contract.class, new UserContractsPropertyEditor());
}
My PropertyEditor:
public class UserContractsPropertyEditor extends PropertyEditorSupport {
#Inject ContractDao contractDao;
#Override
public void setAsText(String text) throws IllegalArgumentException {
System.out.println("matching value: " + text);
if (text != "") {
Integer contractId = new Integer(text);
super.setValue(contractDao.findContract(contractId));
}
}
}
My JSP form:
<form:form commandName="user">
<%-- Other fields... --%>
<form:checkboxes items="${contractsList}"
path="contracts"
itemValue="id"
itemLabel="name" />
</form:form>
The form renders correctly. That is, the checkbox list of Contracts is generated and the correct ones are "checked." The problem is when I submit I get:
java.lang.IllegalArgumentException: 'items' must not be null
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.web.servlet.tags.form.AbstractMultiCheckedElementTag.setItems(AbstractMultiCheckedElementTag.java:83)
at org.apache.jsp.WEB_002dINF.jsp._005fn.forms.user_jsp._jspx_meth_form_005fcheckboxes_005f0(user_jsp.java:1192)
....
The custom property editor seems to be doing its job and there are no null/empty strings being passed.
If the form and controller makes the conversion when viewing the form, why is it having trouble when processing the form? What am I missing here?
You need to ensure that a call to getContract() returns a List instance:
public List<Contract> getContracts() {
if (contracts == null) contracts = new ArrayList<Contract>();
return contracts;
}
Thanks for your response. I guess a fresh set of eyes first thing in the morning does the trick again.
Apparently, my custom property editor had no clue what to do with the id value I was passing in since it couldn't access my DAO/service. So, I had to change the constructor:
public class UserContractsPropertyEditor extends PropertyEditorSupport {
private ContractDao contractDao;
public UserContractsPropertyEditor(ContractDao contractDao) {
this.contractDao = contractDao;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
Integer contractId = new Integer(text);
Contract contract = contractDao.findContract(contractId);
super.setValue(contract);
}
}
Then, modified the initBinder in my controller:
#Inject ContractDao contractDao;
....
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Contract.class, new UserContractsPropertyEditor(this.contractDao));
}
Maybe this will help someone else.

GWT ValueListBox Editor

I'm puzzled about how to use GWT's ValueListBox with an Editor. I'm getting this ERROR:
The method setValue(String) in the type TakesValueEditor<String>
is not applicable for the arguments (List<String>)
Here's the relevant code.
public class MyBean {
private List<String> dateFormats;
public List<String> getDateFormats() {
return dateFormats;
}
public void setDateFormats(List<String> dateFormats) {
this.dateFormats = dateFormats;
}
}
public interface MyBeanView extends IsWidget, Editor<MyBean> {
#Path("dateFormats")
IsEditor<TakesValueEditor<String>> getDateFormatEditor();
}
public class MyBeanViewImpl implements MyBeanView {
#UiField(provided=true) ValueListBox<String> dateFormats;
public MyBeanViewImpl() {
dateFormats = new ValueListBox<String>(PassthroughRenderer.instance(),
new ProvidesKey<String>() {
#Override
public Object getKey(String item) {
return item;
}
});
dateFormats.setAcceptableValues(Arrays.asList(new String[] {"YYYY"}));
// ... binder.createAndBindUi(this);
}
#Override
public IsEditor<TakesValueEditor<String>> getDateFormatEditor() {
return dateFormats;
}
}
Here's what's in ui.xml with xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<g:HTMLPanel>
Data Formats: <g:ValueListBox ui:field="dateFormats"> </g:ValueListBox>
</g:HTMLPanel>
I'm surely missing something obvious here. Much thanks.
The problem that you're running into has to do with trying to map the List<String> dateFormats from MyBean onto the ValueListBox<String> dateFormats editor. The datatypes are incompatible, since a ValueListBox<T> doesn't edit a List<T>, but instead a single instance of T chosen from a list provided by setAcceptableValues(). Given the example above, it would make sense for MyBean to have a String getDateFormat() property and rename the editor field to dateFormat.