Spring webflow partial validation not working - spring

I am trying to implement partial validation in Spring Webflow 2.4.
I have looked at Spring webflow partial validation and the solution given there - but it does not work for me. Validation does not kick in at all when I add the groups (but works OK with no groups).
I have code and config as follows:
flow config
<view-state id="page1" view="apply/page1" model="myModel">
<binder>
<binding property="foo" />
</binder>
<transition on="next" to="page2" validate="true" validation-hints="'group1'"></transition>
</view-state>
<view-state id="page2" view="apply/page2" model="myModel">
<binder>
<binding property="baz" />
</binder>
<transition on="next" to="page3" validate="true" validation-hints="'group2'"></transition>
</view-state>
<view-state id="page3" view="apply/page3" model="myModel">
<on-render>
<set name="requestScope.foo" value="myModel.foo" />
<set name="requestScope.baz" value="myModel.baz" />
</on-render>
</view-state>
model class
public class MyModel implements Serializable {
#NotBlank(message = "Bad foo!", groups = {Group1.class})
private String foo;
#NotBlank(message = "Bad baz!", groups = {Group2.class})
private String baz;
...//getters and setters
public interface Group1 {
}
public interface Group2 {
}
}
html views like:
<form th:object="${myModel}" th:action="${flowExecutionUrl}" method="post">
<div class="error" th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}">
<span th:text="${err}">Input is incorrect</span>
</span>
</div>
<input type="text" th:field="*{foo}"/>
<button type="submit" name="_eventId_next">Next</button>
</form>
and
<form th:object="${myModel}" th:action="${flowExecutionUrl}" method="post">
<div class="error" th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}">
<span th:text="${err}">Input is incorrect</span>
</span>
</div>
<input type="text" th:field="*{baz}"/>
<button type="submit" name="_eventId_next">Next</button>
</form>
My intention is to use a single model for the backing form across a number of views that make up a wizard flow - doing partial validation as I go along. But when I introduce the groups needed to achieve this, the validation does not trigger at any point in the flow.
If I put typos in the validation hint names, I get errors - eg:
<transition on="next" to="page2"
validate="true" validation-hints="'group1zzzz'"></transition>
results in
Failed to resolve validation hint [group1zzzz]
so the groups are being picked up. But validation is not happening, and I can walk through the data-entry without triggering the errors.
I am using spring-webflow 2.4.2.RELEASE
Any ideas if I am missing something, or if this just does not work?
Update
Iv'e done some digging into the code for org.hibernate.validator.internal.engine.ValidatorImpl - where it decides if validation is required:
private boolean isValidationRequired(ValidationContext<?> validationContext,
ValueContext<?, ?> valueContext,
MetaConstraint<?> metaConstraint) {
if ( validationContext.hasMetaConstraintBeenProcessed(
valueContext.getCurrentBean(),
valueContext.getPropertyPath(),
metaConstraint
) ) {
return false;
}
if ( !metaConstraint.getGroupList().contains( valueContext.getCurrentGroup() ) ) {
return false;
}
return isReachable(
validationContext,
valueContext.getCurrentBean(),
valueContext.getPropertyPath(),
metaConstraint.getElementType()
);
}
at:
if ( !metaConstraint.getGroupList().contains( valueContext.getCurrentGroup() ) ) {
return false;
}
I see my instances of my group - eg 'MyModel$Group2' in both collections, but they appear not to be the same object instance, therefore the 'Collection.contains()' fails, and isValidationRequired() always returns false. See screenshots.
[![enter image description here][2]][2]
Agggggggggghhhhhhh - found what appears to be the cause of the issue
With spring-boot-devtools in the mix, there appears to be another classloader getting involved in loading other instances of the group marker interfaces. That is making the 'Collection.contains()' above fail every time.
If I take out spring-boot-devtools from my build stack, it works. The question now becomes:
"how to get validation groups working with spring-boot-devtools in the mix?"

Well, after all that, it appears that if you have spring-boot-devtools in the mix, that webflow causes some problems with the classloaders. Fix appears to be to create file:
/WEB-INF/spring-devtools.properties
and add webflow jar to the include list:
restart.include.webflow=/spring-webflow-2.4.2.RELEASE.jar
That seems to make webflow use the same devetools classloader, and validation now kicks-in OK when looking for the validation-hints interfaces.

Related

How to validate just one field for register a user for spring project with hibernate validator?

<input name="id" placeholder="input your id"/>
<button id="id_check">check</button>
<input name="password" placeholder="input your id"/>
<input type="submit" value="register" />
Currently, I uses a controller for user.
Controller code following...
#Controller
public class UserController {
#GetMapping("...")
public String registerForm(...) { ... }
#PostMapping("...")
public String register(#ModelAttribute #Validated ...) { ... }
.....
}
I want to check id using ajax call.
For id duplication check, can I use hibernate validator? (for one field. not submit action) Also it should be validate duplication of id.(using dao).
For example, one field for validation should be checked duplication and validated by hibernate validator(#NotEmpty, #Email...)

How to represent 2 model objects inside the thymeleaf template

i am working in this spring-boot project and i am returning a ModelAndView object from my controller method , i have added 2 objects to the ModelAndView. this part is working and i want to know how to represent the values inside the thymeleaf template.
public ModelAndView showEdit(#PathVariable int id,Customer cust,Model model){
ModelAndView view = new ModelAndView();
view.setViewName("editCustom");
view.addObject("cust",cust);
view.addObject("log",login);
}
inside the thymeleaf template.
<form action="#" th:action="#{/save}" th:object="${cust}" method="post">
Name:<input type="text" th:field="*{name}" />
i can fetch values in cust but i dont know how to get values from login.
i tried this but its not working.note that all input tags are inside the same form.
<input type="text" id="user" name="user" value="${login.uname}"/>
In your model you are adding login details as log and in your view your are using login
view.addObject("log",login);
versus
${login.uname}
Also thymeleaf uses an attribute processor which process attributes prefixed with th. Instead of using value use th:value as follows
<input type="text" id="user" name="user" th:value="${log.uname}"/>

how to check existing user in a database with Struts and Ajax

I want to verify if an email exists in my database before form submission:
<form action="add" method="post">
<input type="text" name="user.email" />
<input type="submit" value="Ajouter" />
</form>
How can I use Ajax to check if the email exists already in the database before submitting?
You can use the Validate() method when you extends the ActionSupport calls in your Controller ; then you check your email and return an ActionFieldError if it dosent' exist.
it's simple:
public void validate() {
if(!emailExist(this.email)){
addFieldError(email, "Email already exist;You must choose another one"); }
}
That assumes that you have a field with the name ="email" and a function that check mail existence called emailExist(String mail)
If you really want ajax validation , you can use Struts2 jQuery Plugin and you instead of :
<s:submit />
you make something like this:
<sj:submit
targets="result"
button="true"
validate="true"
value="AJAX Submit"
indicator="indicator"
/>

Spring WebFlow: view-state is not calling validation method before <evaluate> tag

This problem is driving me crazy.
I have following view-state:
<on-start>
<evaluate expression="new com.zxxztech.zecure.services.webflow.FormularioConfirmacionCorreo()"
result="flowScope.ccForm" />
</on-start>
<view-state id="activacionManual" model="ccForm" >
<transition on="enviar" to="resultado" bind="true">
<evaluate expression="usersManager.activarUsuario(ccForm.correo, ccForm.codigo)"
result="flowScope.resultado" />
</transition>
<transition on="cancelar" to="cancelar" validate="false" bind="false" />
</view-state>
And this is this the Validation class:
#Component
public class FormularioConfirmacionCorreoValidator {
#Autowired
private UsersManager usersManager;
public void validateActivacionManual(FormularioConfirmacionCorreo ccForm, ValidationContext validContext) {
...
[Validation logic]
}
public UsersManager getUsersManager() {
return usersManager;
}
public void setUsersManager(UsersManager usersManager) {
this.usersManager = usersManager;
}
}
When form is submited, webflow execute <evaluate> tag directly, without calling validation method.
I don't know what could I doing wrong.
Edit:
This is the activacionManual.jsp file:
...
<form:form cssClass="ym-form" modelAttribute="ccf" method="post" action="${flowExecutionUrl}">
<form:errors cssClass="ym-error" element="div" path="*"/>
<div class="ym-box">
<div class="ym-fbox">
<label for="correo"><spring:message
code="activacion.form.correo.label"
text="activacion.form.correo.label" /></label>
<form:input path="correo" />
</div>
<div class="ym-fbox">
<label for="codigo"><spring:message
code="activacion.form.codigo.label"
text="activacion.form.codigo.label" /></label>
<form:input path="codigo" />
</div>
<div class="ym-fbox-footer ym-fbox-button">
<input class="ym-button ym-gr" type="submit"
value="<spring:message code="formulario.button.cancelar" text="formulario.button.cancelar" />"
name="_eventId_cancelar">
<input class="ym-button ym-primary ym-gr" type="submit"
value="<spring:message code="formulario.button.enviar" text="formulario.button.enviar" />"
name="_eventId_enviar">
</div>
</div>
</form:form>
...
The second way is to define a separate object, called a Validator, which validates your model object. To do this, first create a class whose name has the pattern ${model}Validator, where ${model} is the capitialized form of the model expression, such as booking. Then define a public method with the name validate${state}, where ${state} is the id of your view-state, such as enterBookingDetails.
Thus, since your model attribute is ccForm, the validator Class must be named CcFormValidator. (Or rename your model attribute.)
(Also, I think your JSP is going to have problems using modelAttribute="ccf" instead of "ccForm". The model name needs to match across flow.xml, JSPs, and validators.)

Grails: getting object/model from AJAX response

I'm new to Grails and now I'm trying to retrieve objects/model from a controller to a template using AJAX. I want to make it so that sms properties would accessible to messageBox template, but this would always return me a null value. Could anyone help me with this? Any answer would be appreciated, here's my current code.
On my client
<g:form>
<label for="id">Sms ID </label>
<g:textField name="id" />
<g:submitToRemote value="search" update="msgBox"
url="[controller:'sms', action:'send']"/>
</g:form>
<g:render template="messageBox" model="${[sms:sms]}/>
My controller
SmsController{
def send = {
def sms = new Sms(...)
//assume properties have been set
...
...
render(template: messageBox, model:[sms:sms])
}
}
and my _messageBox.gsp
<div id="msgBox">
<span>Sms Property 1: ${sms?.property1}</span>
<span>Sms Property 2: ${sms?.property2}</span>
<span>Sms Property 3: ${sms?.property3}</span>
</div>
There seems to be a few logistical errors here
First, you seem to be rending the messageBox template twice. In your 'client' gsp you are calling...
<g:render template="messageBox" model="${[sms:sms]}/>
On page load 'sms' will always be null unless you are provided one on the page load. Then you also seem to be calling it again in your controller...
render(template: messageBox, model:[sms:sms])
Also, I would move the div:'msgBox' outside of the template and into your client something like...
<g:form>
<label for="id">Sms ID </label>
<g:textField name="id" />
<g:submitToRemote value="search" update="msgBox"
url="[controller:'sms', action:'send']"/>
</g:form>
<div id="msgBox">Waiting for some AJAX!</div>
Lastly, make sure you have a javascript library in your header like prototype or jquery. I'm not sure this will solve your problems but it will be a good start. Let me know!

Resources