ConversationScope value not updated after conversation started - jsf

I have a jsf page that has 2 parts: a table displaying a list of records and a dialog to add a new record. I added some validation features but I can't get them to work properly.
I need it to:
1) Validation error not appear the first time dialog shows up
2) If any validation error happens, keep it open and show error messages.
3) If no validation error and back-end executed successfully, update the table.
This is what I have:
<h:body>
<h:form id="form01">
<p:dataTable id="tbl1" value="#{welcomeController.teams}" var="team" >
<p:column headerText="Id">
<h:outputText value="#{team.seq}" />
</p:column>
<p:column headerText="Name">
<h:outputText value="#{team.name}" />
</p:column>
</p:dataTable>
<p:dialog id="teamDialog" closable="false" visible="#{welcomeController.addMode}"
widgetVar="teamDialog_w" modal="true" resizable="false" draggable="true"
header="New Team Detail">
<p:messages />
<p:panelGrid columns="2">
<h:outputText value="Name" />
<p:inputText id="name" value="#{welcomeController.newTeam.name}" />
</p:panelGrid>
<p:commandButton value="Submit" ajax="true" actionListener="#{welcomeController.addNewTeam}"
update=":form01"oncomplete="teamDialog_w.hide(); console.log(args);" />
</p:dialog>
<p:commandButton value="ADD" actionListener="#{welcomeController.startAdd}"
oncomplete="teamDialog_w.show(); console.log(args);" update="teamDialog" />
</h:form>
The bean:
#Named
#ConversationScoped
public class WelcomeController implements Serializable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final long serialVersionUID = 1L;
private List<TeamDto> teams;
#Inject SessionManager sessionMan;
#Inject DatabaseUtil dbCache;
#Inject TeamService teamService;
#Inject Conversation conversation;
private TeamDto newTeam = new TeamDto();
private boolean addMode = false;
public List<TeamDto> getTeams() throws IOException {
if (teams == null || teams.size() == 0) {
teams = teamService.getAll();
}
return teams;
}
public void setTeams(List<TeamDto> teams) {
this.teams = teams;
}
public void reload() {
conversationBegin();
}
public void conversationBegin() {
if (conversation.isTransient()) {
conversation.begin();
}
}
public void conversationEnd() {
if(!conversation.isTransient()){
conversation.end();
}
}
public void startAdd() {
reload();
newTeam = new TeamDto();
addMode = true;
}
public TeamDto getNewTeam() {
return newTeam;
}
public void setNewTeam(TeamDto newTeam) {
this.newTeam = newTeam;
}
public void addNewTeam() throws IOException, ValidatorException {
if (newTeam.getName().isEmpty()) {
sessionMan.addGlobalMessageFatal("INVALID INFO", null);
return;
}
teamService.addTeam(newTeam);
teams.add(newTeam);
newTeam = new TeamDto();
addMode = false;
}
public boolean isAddMode() {
return addMode;
}
public void setAddMode(boolean addMode) {
this.addMode = addMode;
}
}
I have 2 problems here:
1) After I submit an empty string, I expect the dialog to still open (because addMode is true) but it's not. Why is it?
2) If I put the "ADD" button like this:
<p:commandButton value="ADD" actionListener="#{welcomeController.startAdd}" oncomplete="teamDialog_w.show(); console.log(args);" >
<f:ajax render="teamDialog" />
</p:commandButton>
at least when I open the dialog again, I can see the error message. But in my code, I can't see the error message. Why is it so? Aren't they equivalent?
Is there anything wrong with my understanding?
Please help. Thank you very much.

Dialogs should operate with their own form in JSF, this is crucial.
To prevent your dialog from closing on validation you can use in Primefaces :
if (args &&
!args.validationFailed){PF('eventDialog').hide();}
like this:
<p:commandButton value="ADD"
actionListener="#{welcomeController.startAdd()}"
oncomplete="if (args && !args.validationFailed){PF('teamDialog').hide();}"
update="your_dialog_formID:messages, other_updated_IDs" />
here
if (args && !args.validationFailed){PF('teamDialog').hide();}
you can obvoiusly add else clause and do #3 "If no validation error and back-end executed successfully, update the table."
here BalusC gives great explenation why:Keep p:dialog open when a validation error occurs after submit

Related

selectBooleanCheckbox is not set after remoteCommand

I have a remmoteCommand that updates a List, after the items are loaded a repeat render selectBooleanCheckbox. However, the values are not correctly set at the checkbox, all of them are unchecked beside there are some items with selected property (which is Boolean) set to true.
If I just set an Item in the backing bean after the list is loaded it works only for that Item. What could be the problem?
index.xhtml
<h:form>
<h:panelGroup id="itemsHolder">
<h:panelGrid rendered="#{backingBean.items != null}">
<h:selectBooleanCheckbox value="#{backingBean.item.selected}" disabled="#{backingBean.rendered}" /> #{backingBean.item.name}
<ui:repeat value="#{backingBean.items.toArray()}" var="option" >
<h:selectBooleanCheckbox value="#{option.selected}" disabled="#{backingBean.rendered}" />
#{option.name}
<br />
</ui:repeat>
</h:panelGrid>
</h:panelGroup>
<p:remoteCommand rendered="#{backingBean.rendered}"
name="initItems"
actionListener="#{backingBean.initItems()}"
process="#this"
update="itemsHolder"/>
</h:form>
<ui:fragment rendered="#{backingBean.rendered}">
<script>
$(document).ready(function () {
setTimeout(function () {
initItems();
}, 3000);
});
</script>
</ui:fragment>
BackingBean.java
#Component
#ManagedBean
#Scope("view")
public class BackingBean {
private boolean rendered = true;
private Set<Item> items;
private Item item;
public void initItems() {
items = new LinkedHashSet<>();
items.add(new Item("item 1", true, "1"));
items.add(new Item("item 2", false, "2"));
items.add(new Item("item 3", true, "3"));
item = new Item("lonely item", true, "4");
}
//Getters and setters
}
UPDATE:
I created a copy of the real code in a SandBox, I am still not able to reproduce, I think I will have to add more dependencies from real code.
I've recreated your case and it is working just fine with following simple managed bean
#ManagedBean(name = "bean")
#ViewScoped
public class YourBean implements Serializable {
List<YourItem> items=new ArrayList<>();
YourItem anItem=new YourItem();
#PostConstruct
public void init() {
System.out.println("Bean is created");
//initialize items
anItem.setSelected(false);
//add 5 items to list
for (int i=0;i<5;i++){
YourItem item=new YourItem();
item.setSelected(false);
items.add(item);
}
}
public void onUpdateItems(){
System.out.println("Items will be updated...");
anItem.setSelected(true);
//modify 1st and 3rd item
items.get(0).setSelected(true);
items.get(2).setSelected(true);
//remove 4th item
items.remove(3);
}
public List<YourItem> getItems() {
return items;
}
public void setItems(List<YourItem> items) {
this.items = items;
}
public YourItem getAnItem() {
return anItem;
}
public void setAnItem(YourItem anItem) {
this.anItem = anItem;
}
}
p:command should be implemented on following way
<h:form id="form">
<h:panelGroup id="itemHolder">
<h:panelGrid rendered="#{bean.items != null}">
<h:selectBooleanCheckbox value="#{bean.anItem.selected}" />
<ui:repeat value="#{bean.items}" var="item">
<h:selectBooleanCheckbox value="#{item.selected}" />
#{item.selected}
<br />
</ui:repeat>
</h:panelGrid>
</h:panelGroup>
<p:remoteCommand name="startUpdate"
actionListener="#{bean.onUpdateItems()}"
update=":form:itemHolder"/>
<p:commandButton value="Update items" onclick="startUpdate();"/>
</h:form>
Compare your code with this example. Very probably there is problem in your managed bean method for updating items List or with the way how you are updating h:panelGroup.

Primefaces p:messages won't show first FacesMessage

Prerequisites:
- JSF 2.1
- Primefaces 5.2
- Glassfish 3.1
Story:
I've created a p:dialog used for displaying FacesMessages on a p:messages element. This dialog is needed, because the user has to commit specific FacesMessages with an "OK"-Button before proceeding.
Dialog:
<p:outputPanel id="modalMessage">
<p:dialog id="dlgMessageDialog" dynamic="true" style="z-index: 100"
closable="false" widgetVar="wigVarMessageDialog" modal="true"
appendTo="#(body)">
<f:facet name="header">
<h:outputText id="messageDialogHeader"
value="#{messageDialogBean.header}" />
</f:facet>
<p:outputPanel id="modalMessagePanel">
<h:form id="messageForm" enctype="multipart/form-data">
<p:messages id="messages" escape="false" closable="false"
showDetail="true" autoUpdate="true"
for="#{messageDialogBean.messageDialogId}"></p:messages>
<p:spacer height="20px"></p:spacer>
<p:commandButton value="#{msg.btnOk}"
oncomplete="PF('wigVarMessageDialog').hide()" />
</h:form>
</p:outputPanel>
</p:dialog>
</p:outputPanel>
Bean:
#Named("messageDialogBean")
#SessionScoped
public class MessageDialogBean implements Serializable {
private static final long serialVersionUID = 1L;
private final String messageDialogId = "messageDialogId";
private FacesMessage message = new FacesMessage();
private String header = "test";
public void showMessage(final String pHeader, final FacesMessage pMessage) {
if (pMessage != null) {
setHeader(pHeader);
this.message = pMessage;
show();
}
}
public void showWarn(final String pHeader, final String pSummary, final String pDetail) {
setHeader(pHeader);
this.message = new FacesMessage(FacesMessage.SEVERITY_WARN, pSummary, pDetail);
show();
}
public void showInfo(final String pHeader, final String pSummary, final String pDetail) {
setHeader(pHeader);
this.message = new FacesMessage(FacesMessage.SEVERITY_INFO, pSummary, pDetail);
show();
}
public void showError(final String pHeader, final String pSummary, final String pDetail) {
setHeader(pHeader);
this.message = new FacesMessage(FacesMessage.SEVERITY_ERROR, pSummary, pDetail);
show();
}
public void updateDialog() {
RequestContext context = RequestContext.getCurrentInstance();
context.update("mainForm:messageDialogHeader");
}
private void show() {
updateDialog();
RequestContext context = RequestContext.getCurrentInstance();
context.execute("PF('wigVarMessageDialog').show();");
FacesContext.getCurrentInstance().addMessage(this.messageDialogId, this.message);
}
public String getMessageDialogId() {
return this.messageDialogId;
}
public void setHeader(final String pHeader) {
this.header = pHeader;
}
public String getHeader() {
return this.header;
}
public FacesMessage getLastMessage() {
return this.message;
}
}
One of the messages which have to be commited:
this.messageDialogBean.showInfo("Title", "Summary", "Detail");
Problem:
The p:messages element of the dialog does not show the message when the dialog is opened the first time. After opening and hiding it once it shows all further FacesMessages just fine.
Question:
So far i am useing opening and closeing the dialog once when the interface is initialized as a workarround. Does annyone know what causes this problem in the first place and also how to solve it properly?
Thanks for answers
First of all it is not allowed to put a form inside of another, as stated in W3C XHTML specification, "form must not contain other form elements." visit: https://www.w3.org/TR/xhtml1/#prohibitions.
So your dialog should not be inside of the main form, you have to sperate the dialog from the form, your code sould be orginsed like this :
<form id="mainForm" >
<!--your main page-->
</form>
<p:dialog id="dlgMessageDialog" >
<h:form id="messageForm" enctype="multipart/form-data">
<f:facet name="header">
<h:outputText id="messageDialogHeader"
value="#{messageDialogBean.header}" />
</f:facet>
<p:messages id="messages" escape="false" closable="false"
showDetail="true" autoUpdate="true"
for="#{messageDialogBean.messageDialogId}"></p:messages>
<p:spacer height="20px"></p:spacer>
<p:commandButton value="#{msg.btnOk}"
oncomplete="PF('wigVarMessageDialog').hide()" />
</h:form>
</p:dialog>
Another thing, you have to update the whole dialog, so when the dialog is opened the messges is automaticly updated :
context.update("dlgMessageDialog");

Certain method is not called by a view

I have a very strange problem with JSF, but I cannot solve it on my own, because there is no error message I can google for. The problem is, that I have one view for submitting a new article or updating an existing article.
The method getArticle() returns - if ?id=x is set via url - the article POJO with the id of x. Otherwise a pure empty new article. Depending of id is set, the mode editArticle is set to true or false.
So if I pass an id via URL, the form changes to "Update article" and the values of the article are shown. But if I hit the submit button, nothing happens. No output, no error. Using HTTP Live Headers in Firefox, I see a request to the server. Looking with wireshark on the loopback interface, there is traffic, too.
But on the other hand, the "Create new article"-Button (if id is not set) without a problem.
The environment: JSF 2.2, Apache Tomcat 7/8, Java 7/8, Windows/Ubuntu (tried different envs, but always the same, so it seems to be a problem "by design" :-) ). Tried also renaming methods, calling via ActionListener, changing the order of the panelGrids... but no way. The method updateArticle() of the bean is never called by hitting the button.
<h:form id="idMasterForm" enctype="multipart/form-data">
<h:panelGroup rendered="#{userController.loggedIn}">
<h:panelGrid columns="2" cellpadding="5">
<p:outputLabel for="title" value="Title" />
<p:inputMask id="title"
value="#{editArticleController.article.title}" />
<p:outputLabel for="author" value="Author" />
<p:inputText id="author" value="#{userController.id}"
readonly="true" />
<p:outputLabel for="date" value="Date" />
<p:inputMask id="date"
value="#{editArticleController.article.date}" readonly="true" />
<p:outputLabel for="link" value="File" />
<p:selectOneMenu id="link"
value="#{editArticleController.article.link}">
<f:selectItems value="#{uploadFilesController.filesItems}" />
</p:selectOneMenu>
<p:outputLabel for="editor" value="" />
<p:editor id="editor" widgetVar="editorWidget"
value="#{editArticleController.article.text}" width="600" />
</h:panelGrid>
<h:panelGrid columns="2" style="margin-top: 10px" rendered="#{!editArticleController.editArticle}">
<p:commandButton value="Submit new article"
action="#{editArticleController.createArticle()}"
icon="ui-icon-disk" />
<p:commandButton value="Clear" type="button"
onclick="PF('editorWidget').clear();" icon="ui-icon-close" />
</h:panelGrid>
<h:panelGrid columns="2" style="margin-top: 10px" rendered="#{editArticleController.editArticle}" >
<p:commandButton value="Update article"
action="#{editArticleController.updateArticle()}"
icon="ui-icon-disk" />
<p:commandButton value="Clear" type="button"
onclick="PF('editorWidget').clear();" icon="ui-icon-close" />
</h:panelGrid>
<h:outputText rendered="#{ ! userController.loggedIn}"
value="#{res.globNotLoggedIn}" />
</h:panelGroup>
</h:form>
And here is the Managed Bean:
import java.util.Date;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import hwr.Util;
import hwr.g2.model.Archive;
import hwr.g2.model.Article;
import hwr.g2.model.Author;
import hwr.user.UserController;
#ManagedBean
public class EditArticleController {
private Article article;
private Boolean editArticle;
private Integer editArticleID;
public EditArticleController() {
this.article = new Article();
System.out.println("EditArticleController was called");
}
public Article getArticle() {
//System.out.println("getArticle() was called");
if (this.getEditArticle()) {
this.article = new Article(this.editArticleID);
}
return this.article;
}
public void setArticle(Integer articleID) {
this.article = new Article(articleID);
}
public void setArticle(Article article) {
System.out.println("setArticle(Article article) was called");
this.article = article;
}
public void setText(String text) {
this.article.setText(text);
}
public String getText() {
return this.article.getText();
}
public void setTitle(String title) {
this.article.setTitle(title);
}
public String getTitle() {
return this.article.getTitle();
}
public Boolean getEditArticle() {
// https://stackoverflow.com/questions/550448/get-request-and-session-parameters-and-attributes-from-jsf-pages
HttpServletRequest req = (HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest();
try {
this.editArticleID = Integer.valueOf(req.getParameter("id"));
if (this.editArticleID > 0) {
this.setEditArticle(true);
} else {
this.setEditArticle(false);
}
} catch (Exception e) {
this.setEditArticle(false);
}
return editArticle;
}
public void setEditArticle(Boolean editArticle) {
this.editArticle = editArticle;
}
public Date getActualDate() {
Date d = new Date();
return d;
}
public void rangChanged() {
// nothing
}
public String createArticle() {
System.out.println("New Article...");
Util util = new Util();
UserController user = (UserController) util.getBean("userController");
this.article.setAuthor(new Author(user.getId()));
this.article.setDate(new Date());
try {
Archive archive = new Archive();
archive.addArticle(this.article);
} catch (Exception e) {
System.out.println("something goes wrong here...");
e.printStackTrace();
}
return "g2_home?faces-redirect=true";
}
public String updateArticle() {
System.out.println("*** Update Article... ***");
System.out.println("Debug: Update article, ID "
+ this.article.getId() + "! title: " + this.article.getTitle()
+ " and text: " + this.article.getText()
+ " and written by "
+ this.article.getAuthor().getFirst());
try {
Archive archive = new Archive();
archive.updateArticle(this.article);
} catch (Exception e) {
System.out.println("something goes wrong here...");
e.printStackTrace();
}
return "g2_home?faces-redirect=true";
}
}
I used a checklist of BalusC (action method is not called in JSF), but didn't found something.
It whould be really great, if someone can point me in the right direction. By the way, is there an better way to fill the form? In my kind of way, the method getArticle() is called several times by the view.
I Think you should define the managed bean scope #ViewScoped or any other scope you want .
look at this question [question] : What is the default Managed Bean Scope in a JSF 2 application?
If you don't set the scope for the bean, it will be #RequestScoped by default, which means the bean will be re created per user request. Change it to #ViewScoped at least in order for the bean to keep alive while the user is interacting with the same view in multiple requests (e.g. ajax requests).
#ManagedBean
#ViewScoped
public class EditArticleController {
//rest of class definition
}

Edit row using Radio button and commandButton

Can I use radio button to select a single row then edit some of data on that row then use
commandButton to submit what I edit it in that row. I'm trying to edit username cell for now as test.
this a snap of my code:
Xadmin.xhtml
<h:form id="form" enctype="multipart/form-data">
<p:growl id="msgs" showDetail="true" />
<p:dataTable id="DT" value="#{Jadmin.messages}"
var="o"
selection="#{Jadmin.selectedUser}"
rowKey="#{o.id}"
style="margin-bottom:20px">
<f:facet name="header">
Users List
</f:facet>
<p:column selectionMode="single" />
<p:column>
<f:facet name="header">
<h:outputText value="id" />
</f:facet>
<h:outputText value="#{o.id}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="username" />
</f:facet>
<p:inputText value="#{o.username}" />
</p:column>
<f:facet name="footer">
<h:commandButton value="Update" action="#{Jadmin.update}" />
<p:commandButton value="Delete"
action="#{Jadmin.delete}"
ajax="false"
update=":form:msgs"/>
</f:facet>
</p:dataTable>
JadminBeans.java
#ManagedBean(name = "Jadmin")
#SessionScoped
public class JadminBeans implements Serializable {
private static final long serialVersionUID = 1L;
private JadminController selectedUser;
List<JadminController> userslist = new ArrayList<JadminController>();
public List<JadminController> getMessages() {
System.out.println("List<JadminController> getMessages()");
userslist = JadminDAO.getAllUsers();
return userslist;
}
public void delete() {
//System.out.println(usr);
//System.out.println(itemList.remove(item)+"!!");
System.out.println("JadminBeans >> delete() ---------- id= ");
JadminDAO.deleteUser(selectedUser);
}
public JadminController getSelectedUser() {
return selectedUser;
}
public void setSelectedUser(JadminController selectedUser) {
this.selectedUser = selectedUser;
}
public void update() {
//o=(JadminBeans) objct;
JadminDAO.updateUser(selectedUser);
}
}
JadminDAO.java
public static void deleteUser(JadminController user) {
try {
PreparedStatement preparedStatement = connection.prepareStatement("delete from users where id=?");
// Parameters start with 1
preparedStatement.setLong(1, user.getId());
preparedStatement.executeUpdate();
System.out.println("JadminDAO >> deleteUser ----------");
} catch (SQLException e) {
System.out.println("JadminDAO >> deleteUser----------- SQLException :(");
e.printStackTrace();
}
}
public static void updateUser(JadminController user) {
try {
PreparedStatement preparedStatement = connection.prepareStatement("update users username=?, password=?, permission=? where username=?");
// Parameters start with 1
//System.out.println(new java.sql.Date(user.getRegisteredon().getTime()));
preparedStatement.setString(1, user.getUsername());
preparedStatement.setString(2, user.getPassword());
preparedStatement.setString(3, user.getPermission());
//preparedStatement.setDate(3, new java.sql.Date(user.getRegisteredon().getTime()));
preparedStatement.setString(4, user.getUsername());
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
JadminController.java
public class JadminController implements Serializable {
private static final long serialVersionUID = 1L;
private String username, password, permission;
private long id;
// Getters and setters.
}
If you use Editable Datatable you don't need any command button to submit what you've edited.
As shown in the same link, inside in datatable is usually used a selectOneMenu instead of radioButton for making choices.
To use input element inside your datatable don't forget to put <f:facet name="output"></f:facet> and <f:facet name="input"></f:facet>
I hope it helps.

JSF and Bean Validation group

I use JavaEE 7 (JSF 2.2, Bean Validation 1.1...) and primefaces 5.1 in WildFly 8.1 runtime
I want to use different validation groups according to the pressed button. A save button associated with a small number of constraints and a submit button associated with a larger number of constraints.
The bean validation groups seem to be what I need but I have some issues with the web interface.
I wan't that the invalid inputs appear in red according to the validation mode : save or submit
So I made 3 interfaces :
public interface LifeCycleValidation {}
public interface Save extends LifeCycleValidation {}
public interface Submit extends Save {}
I annotate fields using BeanVal with the appropiate group in the model object
#Size(min=3, max = 300, groups = Save.class)
#NotNull(groups = Save.class)
private String title ;
#Size(min = 3, max = 5, groups = Submit.class)
private List<String> keywords ;
I made some methods in the JSF managed bean
private final String formIdPrefix = "bookEditForm:" ;
public void save() {
save(Save.class) ;
}
public void submit() {
save(Submit.class) ;
}
private void save(Class<? extends LifeCycleValidation> groupClass) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ;
Validator validator = factory.getValidator();
Set<ConstraintViolation<Book>> violations = validator.validate(this, groupClass);
violations.stream()
.forEach(violation -> addErrorMessage(formIdPrefix+violation.getPropertyPath().toString(), violation.getMessage()));
if(violations.isEmpty()) {
// save
addInfoMessage("","sucess") ;
} else {
addErrorMessage("",violations.size()+" error(s)") ;
}
}
private void addErrorMessage(String id, String msg) {
addMessage(id,FacesMessage.SEVERITY_ERROR,"Error",msg) ;
}
private void addInfoMessage(String id, String msg) {
addMessage(id,FacesMessage.SEVERITY_INFO,"Info",msg) ;
}
private void addMessage(String clientId, FacesMessage.Severity severity, String summary, String detail) {
FacesMessage message = new FacesMessage(severity, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message);
}
The messages display correctly if the id of the input correspond to the path of the property but the input aren't red
<h:form id="bookEditForm">
<p:messages showDetail="true" showSummary="true"/>
<p:panelGrid columns="3">
<p:outputLabel value="title" for="title"/>
<p:inputText value="#{book.title}" id="title"/>
<p:message showDetail="true" showSummary="true" for="title"/>
<p:outputLabel value="keywords" for="keywords"/>
<p:inputText value="#{book.keywords}" id="keywords" converter="converter.ListString"/>
<p:message showDetail="true" showSummary="true" for="keywords"/>
</p:panelGrid>
<p:commandButton value="save" action="#{book.save()}" update="#form"/>
<p:commandButton value="submit" action="#{book.submit()}" update="#form"/>
</h:form>
(in my exemple the model and the controller is in the same class, it was just a test)
I try an other solution using f:validateBean validationGroups
So I made ValidationMode enum:
public enum ValidationMode {
SAVE(Save.class), SUBMIT(Submit.class);
public final Class<? extends LifeCycleValidation> cl ;
private ValidationMode(Class<? extends LifeCycleValidation> cl) {
this.cl = cl ;
}
}
and put it in the JSF managed bean:
private ValidationMode validationMode ;
public void setValidationMode(String validationModeTitle) {
this.validationMode = ValidationMode.valueOf(validationModeTitle);
}
public String getValidationGroups() {
if(validationMode==null) {
return ValidationMode.SAVE.cl.getCanonicalName() ;
}
return validationMode.cl.getCanonicalName() ;
}
and try to set the appropriate mode in the actionListener of ther commandButton
<h:form id="bookEditForm">
<p:messages showDetail="true" showSummary="true"/>
<p:panelGrid columns="3">
<p:outputLabel value="title" for="title"/>
<p:inputText value="#{book.title}" id="title">
<f:validateBean validationGroups="#{book.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="title"/>
<p:outputLabel value="keywords" for="keywords"/>
<p:inputText value="#{book.keywords}" id="keywords" converter="converter.ListString">
<f:validateBean validationGroups="#{book.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="keywords"/>
</p:panelGrid>
<p:commandButton value="save" action="#{book.save()}" actionListener="#{book.setValidationMode('SAVE')}" update="#form"/>
<p:commandButton value="submit" action="#{book.submit()}" actionListener="#{book.setValidationMode('SUBMIT')}" update="#form"/>
</h:form>
but that doesn't work
The I think of writing my own jsf validator but I don't know how to implement the validator to validate the field according to the right group
I finnaly found something, I trigger the setValidationMode when an onmouseover occured like that the validation mode is set before the action: save or submit is called
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{book.setValidationMode('SAVE')}"/>
<p:commandButton value="save" action="#{book.save()}" update="#form"/>
</h:panelGroup>
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{book.setValidationMode('SUBMIT')}"/>
<p:commandButton value="submit" action="#{book.submit()}" update="#form"/>
</h:panelGroup>
so you have nothing to do in the controller
private ValidationMode validationMode = ValidationMode.SAVE ;
public void setValidationMode(String validationModeTitle) {
this.validationMode = ValidationMode.valueOf(validationModeTitle);
}
public String getValidationGroups() {
return validationMode.cl.getCanonicalName() ;
}
public void save() {
// save
addInfoMessage("","saved") ;
}
public void submit() {
// submit
addInfoMessage("","submited") ;
}
Edit
here is the clean and complete MVC solution with the progressbar calculated in bonus :
the view:
<h:form id="bookEditForm">
<p:progressBar value="#{bookBean.progress}" labelTemplate="{value}%" id="progress"/>
<p:messages showDetail="true" showSummary="true"/>
<p:panelGrid columns="3">
<p:outputLabel value="title" for="title"/>
<p:inputText value="#{bookBean.book.title}" id="title">
<f:validateBean validationGroups="#{bookBean.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="title"/>
<p:outputLabel value="keywords" for="keywords"/>
<p:inputText value="#{bookBean.book.keywords}" id="keywords" converter="converter.ListString">
<f:validateBean validationGroups="#{bookBean.validationGroups}"/>
</p:inputText>
<p:message showDetail="true" showSummary="true" for="keywords"/>
</p:panelGrid>
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{bookBean.setValidationMode('SAVE')}"/>
<p:commandButton value="save" action="#{bookBean.save()}" update="#form"/>
</h:panelGroup>
<h:panelGroup layout="span">
<p:ajax event="mouseover" listener="#{bookBean.setValidationMode('SUBMIT')}"/>
<p:commandButton value="submit" action="#{bookBean.submit()}" update="#form"/>
</h:panelGroup>
</h:form>
the controller:
#Named
#ViewScoped
public class BookBean implements Serializable {
private ValidationMode validationMode = ValidationMode.SAVE;
private Book book ;
public BookView() {
book = new Book() ;
}
public void setValidationMode(String validationModeTitle) {
this.validationMode = ValidationMode.valueOf(validationModeTitle);
}
public String getValidationGroups() {
return validationMode.cl.getCanonicalName() ;
}
public Book getBook() {
return book;
}
public void save() {
// save...
addMessage("",FacesMessage.SEVERITY_INFO,"Success","Infos saved") ;
}
public void submit() {
//submit...
addMessage("",FacesMessage.SEVERITY_INFO,"Success","Infos submitted") ;
}
private void addMessage(String clientId, FacesMessage.Severity severity, String summary, String detail) {
FacesMessage message = new FacesMessage(severity, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message);
}
public int getProgress() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ;
Validator validator = factory.getValidator();
int nbConstraints = validator.getConstraintsForClass(Book.class).getConstrainedProperties().size() ;
int nbViolations = validator.validate(book, Submit.class).size();
return 100 - (nbViolations*100) / (nbConstraints) ;
}
}
the model:
public class Book implements Serializable {
#NotNull(groups = Save.class)
#Size(min=1, max = 140, groups = Save.class)
private String title ;
#NotNull(groups = Submit.class)
#Size(min = 3, max = 5, groups = Submit.class)
private List<String> keywords ;
// getters and setters
}
ValidationMode, LifeCycleValidation, Save and Submit are the same as in the question
And OmniFaces seems to have a nice solution :
http://showcase.omnifaces.org/validators/validateBean
Could be interesting...

Resources