JSR 303 Bean Validation + Javascript Client-Side Validation
Asked Answered
I

7

45

What is the best way to perform client-side form validation using Javascript (with minimal code duplication) when using JSR 303 bean validation on the server side? I'm currently using Spring 3 and the Hibernate Validator.

Ironmonger answered 25/3, 2010 at 8:5 Comment(0)
G
5

I would suggest that you look at Spring JS, which relies heavily on Dojo. A tutorial can be found here.

Easiest way for yourself to start playing with it is to download Spring Roo, create the petclinic sample application with one of the example-scripts (this takes you 5 minutes) and then play around with how the javascript is integrated. Spring Roo creates an app with the same technology stack that you use (Spring+hibernate+implementation of jsr 303)

Golem answered 25/3, 2010 at 8:43 Comment(0)
B
5

I found this open source project but it looks dead, maybe it is worth resurrecting.

http://kenai.com/projects/jsr303js/pages/Home

This library provides client side validation of an HTML form based on JSR-303 and Hibernate Validator annotations, integrated with Spring MVC. The library provides a JavaScript validation code base that handles basic interaction with HTML forms, as well as JavaScript functions implementing the validation annotations supported by Hibernate Validator (including those not from the JSR-303 spec). This JavaScript code base can be included in a page by using a provided taglib or by extracting the JavaScript file from the jar and including it using a tag. Once this code base has been included in a page, a second taglib is used to generate the JavaScript code for validating an HTML form. You can also provide a JSON object in the body of the tag to specify additional configuration information.

Brandiebrandise answered 14/6, 2012 at 10:2 Comment(1)
Definitely going to test this. Setting the validation on the bean and having it automatically replicated on the client is the way to goGalloglass
T
4

Here's how I'm doing it using Spring MVC + JQuery + Bootstrap, partially based on a recent blog post at SpringSource:

AddressController.java

@RequestMapping(value="/validate")
public @ResponseBody ValidationResponse processForm(Model model, @Valid AddressForm addressForm, BindingResult result) {
    ValidationResponse res = new ValidationResponse();
    if (result.hasErrors()) {
        res.setStatus("FAIL");
        for (ObjectError error : result.getAllErrors()) {
            if (error instanceof FieldError) {
                FieldError fieldError = (FieldError) error;
                res.addError(fieldError.getField(), fieldError.getDefaultMessage());
            }    
        }
    }
    else {
        res.setStatus("SUCCESS");
    }
    return res;
}

AddressForm.java

public class AddressForm {
    @NotNull
    @Size(min=1, max=50, message="Address 1 is required and cannot be longer than {max} characters")
    private String address1;

    @Size(max=50, message="Address 2 cannot be longer than {max} characters")
    private String address2;

    // etc
}

ValidationResponse.java:

public class ValidationResponse {
    private String status;
    private Map<String,String> errors;
    // getters, setters
}

address.jsp:

<f:form commandName="addressForm">
    <div class="control-group">
        <label for="address1">Address 1</label>
        <div class="controls">
            <f:input path="address1" type="text" placeholder="Placeholder Address 1" class="wpa-valid" />
            <span class="help-inline"></span>
        </div>
    </div>
    <!-- etc -->
    <div class="form-actions">
        <button type="submit" class="btn btn-primary">Save</button>
        <button type="button" class="btn">Cancel</button>
    </div>
</f:form>

<script type="text/javascript">
function collectFormData($fields) {
    var data = {};
    for (var i = 0; i < $fields.length; i++) {
        var item = $($fields[i]);
        data[item.attr("id")] = item.val();
    }

    return data;
}

function clearErrors($fields) {
    for (var i = 0; i < $fields.length; i++) {
        var item = $($fields[i]);
        $("#"+item.attr("id")).parents(".control-group").removeClass("error");
        $("#"+item.attr("id")).siblings(".help-inline").html("");
    }
}

function markErrors(errors) {
    $.each(errors, function(key, val) {
        $("#"+key).parents(".control-group").addClass("error");
        $("#"+key).siblings(".help-inline").html(val);
    });
}

$(document).ready(function() {
    var $form = $("form.validate");
    $form.bind("submit", function(e) {
        var $fields = $form.find(".validate");

        clearErrors($fields);
        var data = collectFormData($fields);

        var validationUrl = "validate";
        $.get(validationUrl, data, function(response) {
            $("#alert").removeClass();

            if (response.status == "FAIL") {
                markErrors(response.errors);

                $("#alert").addClass("alert alert-error");
                $("#alert").html("Correct the errors below and resubmit.");
            } else {
                $("#alert").addClass("alert alert-success");
                $("#alert").html("Success!");

                $form.unbind("submit");
                $form.submit();
            }
        }, "json");

        e.preventDefault();
        return false;
    });
});
</script>

It could use some refactoring, but this will do an ajax GET with the form data and update the page based on the result.

Treadle answered 13/9, 2012 at 3:14 Comment(0)
C
1

Richfaces supports this. They have a small demo on their site.

Cordy answered 23/6, 2012 at 8:46 Comment(0)
H
1

PrimeFaces Client Side Validation Framework Supports Bean Validation.

http://blog.primefaces.org/?p=2874

Heartburning answered 28/8, 2013 at 12:12 Comment(0)
C
0

Here is an open source alternative to JSR-303.

This solution can perform all validation of the request message, but no hassle coding is required.

https://github.com/ckpoint/CheckPoint

With Check-Point, all validation is possible without further code, just by changing the annotation of the Controller method.

After that, all validation settings can be easily made in Admin Page.

I think this video can help your understand. https://youtu.be/I1aEIztXlhE

Check-Point Admin-Page Setting Screen

Clipper answered 30/7, 2018 at 12:21 Comment(0)
M
0

Edit:

Indeed JSR 303 is the best way (IMO) to handle client side validation. The best thing is that if you have proper js libraries on the fronted you can use the same validation (the same code) on the server (if you would use node.js). I've created library @stopsopa/validation for this purposes I'm validating forms like this (In React.js):

import React, { Component } from "react";
import ReactDOM from "react-dom";

import validator, {
  Collection,
  Required,
  Optional,
  NotBlank,
  Length,
  Email,
} from "@stopsopa/validator";

class App extends Component {
  constructor(...args) {
    super(...args);
    this.state = {
      data: {
        name: "",
        email: ""
      },
      errors: {},
      validate: false
    };
  }
  onSubmit = async e => {
    e.preventDefault();

    const errors = await validator(this.state.data, new Collection({
      name: new Required([
        new NotBlank(),
        new Length({min: 3}),
      ]),
      email: new Required([
        new NotBlank(),
        new Email(),
      ])
    }));
    this.setState({
      errors: errors.getFlat(),
      validate: true,
    });

    if ( ! errors.count()) {

      console.log('send data to server', this.state.data);
    }
  };
  onChange = (name, value) => {
    this.setState(state => ({
      ...state,
      data: { ...state.data, ...{ [name]: value } }
    }));
  };
  render() {
    const s = this.state;
    return (
      <form onSubmit={this.onSubmit}>
        <label>
          name:
          <input
            value={s.data.name}
            onChange={e => this.onChange("name", e.target.value)}
          />
        </label>
        {s.validate && s.errors.name && (
          <div className="error">{s.errors.name}</div>
        )}
        <br />
        <label>
          email:
          <input
            value={s.data.email}
            onChange={e => this.onChange("email", e.target.value)}
          />
        </label>
        {s.validate && s.errors.email && (
          <div className="error">{s.errors.email}</div>
        )}
        <br />
        <input type="submit" value="submit" />
      </form>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

live example is available here: https://codesandbox.io/s/ymwky9603j

Mila answered 5/12, 2018 at 0:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.