I have a problem with validation of input fields in a dataTable, which you can add / delete lines. I wanted to validate key params unique in whole table. I get it by incorrect way I think.
Here's a simplified version:
In xhtml:
<p:dataTable value="#{bean.parameters}" var="parameter">
<p:column>
<p:inputText id="name" value="#{parameter.name}">
<f:validator validatorId="duplicateInputValidator" />
</p:inputText>
</p:column>
<p:column>
<p:inputText id="value" value="#{parameter.value}"/>
</p:column>
</p:dataTable>
And my validator:
#FacesValidator("duplicateInputValidator")
public class DuplicateInputValidator implements Validator {
private final List<String> values = new ArrayList<>();
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (values.contains(value.toString())) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "error validation", ""));
}
values.add(value.toString());
}
}
In this simple case, it works, but if validation is also related to different columns (in this example: value) it will not work.
Is there any better way to do this? I was thinking about the possibility to retrieve all the data from datatable and validate them. 'findComponent' not working if I have repeteable rows.
Related
In JSF 2.X, can I render a component only when the validation success?
In my application I have many fields that must be filled. These data can be imported from a WebService through a search key.
When the user enter a valid search key the system searches the other fields and render them with the new values. But when the user enter a nonexistent key (or any other validation error) the server generates a validation error but still renders the fields, thus losing any data that there were filled.
What I need is that the user can perform the query and that if the query does not return results, this does not affect any data that he has already entered.
Below is a code example. Thus, if the user has filled in the fields inside updateThisOnSuccess and just after making an attempt to query without success, the value that is filled in is not lost.
<h:inputText value="#{controller.searchWebService}" >
<f:ajax execute="#this" render="updateThisOnSuccess messages" />
</h:inputText>
<h:panelGroup id="updateThisOnSuccess">
<h:inputText value="#{controller.field}" />
<!-- other fields -->
</h:panelGroup>
Submit the field values to run the search also does not seem an option as this will cause need to validate the fields inside updateThisOnSuccess.
Note: I saw the answer given by #BalusC to a similar question, but this is different from what I'm wondering why, in that case, foo-holder is always rendered and foo is conditioning. It's not my case, since this approach would make the controls do not appear when the validation fails.
Try this
<h:panelGroup id="updateThisOnSuccess">
<ui:fragment rendered="#{not facesContext.validationFailed}">
<h:inputText value="#{controller.field}" />
<!-- other fields -->
</ui:fragment>
</h:panelGroup>
Plaase try this. The requirements are that you must implement model validations with Bean Validation and the search field must implement JSF validation if required.
If you write "123456" then data is returned, else nothing is returned and a message is printed.
The backing bean:
#Named
#ViewScoped
public class yourBean implements Serializable{
private static final long serialVersionUID = 1L;
#Size(min=2)
private String field01;
private String searchWebService;
public void saveF(){
System.out.println("save");
}
public void searchWebServiceF(){
Boolean successWS = ("123456").equals(this.searchWebService);
if(successWS){
this.setField01("WS data");
}else{
FacesContext.getCurrentInstance().
addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "WS fails", ""));
}
}
public String getSearchWebService() {
return searchWebService;
}
public void setSearchWebService(String searchWebService) {
this.searchWebService = searchWebService;
}
public String getField01() {
return field01;
}
public void setField01(String field01) {
this.field01 = field01;
}
}
In your page:
<h:form id="form01">
<h:messages id="message"/>
<h:inputText id="wsid" value="#{pruebasBorradorBean.searchWebService}">
<f:validateLength maximum="6"/>
<f:ajax execute="#form" render="#form" listener="#{pruebasBorradorBean.searchWebServiceF()}" />
</h:inputText>
<h:panelGroup id="thedata">
<h:inputText value="#{pruebasBorradorBean.field01}">
<f:validateBean disabled="#{param['javax.faces.source']!='form01:save'}"/>
</h:inputText>
<!-- other fields -->
</h:panelGroup>
<h:commandButton id="save" value="submit">
<f:ajax render="thedata message" execute="#this thedata" listener="#{pruebasBorradorBean.saveF()}"/>
</h:commandButton>
</h:form>
You can change the components that will be processed in render phase changing the Collection at getRenderIds() of PartialViewContext. According to documentation this Collection is mutable.
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().remove("formName:updateThisOnSuccess");
To test this solution, I used this controller:
#Named
#ViewScoped
public class Controller implements Serializable {
private static final long serialVersionUID = 1L;
private final static List<String> LIST_VALID_WEB_SERVICE_SEARCHS =
Arrays.asList(new String[] {"foo", "bar"});
private String webServiceParameter;
private Integer field01;
public void searchWebService() {
if (LIST_VALID_WEB_SERVICE_SEARCHS.contains(getWebServiceParameter())) {
setField01(123);
} else {
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.getPartialViewContext().getRenderIds().remove("formFields");
FacesMessage facesMessage = new FacesMessage("Search not found in WebService.");
facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
facesContext.addMessage("formName:searchWebService", facesMessage);
}
}
public void submit() {
System.out.println("submitted");
}
// Getters and Setters
}
And used this view:
<h:form id="formSearch">
<h:inputText id="webServiceParameter" value="#{controller.webServiceParameter}">
<f:ajax execute="#this" render="formFields messages" listener="#{controller.searchWebService}" />
</h:inputText><br />
</h:form>
<h:form id="formFields">
<h:inputText id="field01" value="#{controller.field01}" required="true">
<f:validateLongRange minimum="2" maximum="345" />
</h:inputText><br />
<!-- other fields -->
<h:commandButton value="submit" action="#{controller.submit}">
<f:ajax render="#form messages" execute="#form" />
</h:commandButton>
</h:form>
<h:messages id="messages" />
You can do something like that:
<f:ajax execute="#this" render="#{controller.success} message"/>
where success is a String attribute that will be empty if the WS fails and will be "updateThisOnSuccess" if not .
Or you could get rid of the JSF validation mechanism for informing the user the WS has failed. Think of it, it is not really a validation of the Model. You could draw an icon beside the WS Id field in red color or something similar using a boolean flag attribute in the backing bean.
I have a JSF form. I want a message to be displayed, when a user entered 0 in qty field and clicked on the Add To Card button.
Here is the JSF form:
<h:form>
<h:inputText id="qtyField" value="#{booksBean.qty}">
<!--What kind of validation should i use here?-->
<f:ajax event="blur" render="qtyMsg"/>
</h:inputText>
<h:message id="qtyMsg" for="qtyField"/>
<h:commandButton value="Add To Card"
action="#{booksBean.orderBook()}"
rendered="#{booksBean.qty>0}">
<f:ajax execute="#form" rendered="#form"/>
</h:commandButton>
</h:form>
Do I need a custom validator class just to simply compare a number value with a zero?
Like this:
#FacesValidator("myValidator")
public class MyValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) {
if (intValue== 0 || intValue <0) {
throw new ValidatorException(new FacesMessage(...));
}
//...
}
Is there any shorter way without creating a custom validator class?
You can use f:validateLongRange for this.
<h:inputText value="#{backingBean.input1}">
<f:validateLongRange minimum="1" />
</h:inputText>
Checks whether the local value of a component is within a certain
range. The value must be any numeric type or String that can be
converted to a long.
I'm a bit stuck in PrimeFaces dataTable part. I created a table with rowEditor and sortBy built-in methods, but when I modify a row in the table order of entries are not updated.
In theory, it should work: update=":form:fruits". But it doesn't update the modified row. So I tried to update the whole form: update=":form". But in this case I loose all data from the table except the updated one without any desing.
ScreenShot01
ScreenShot02
Here is a very short sample code to reproduce the problem.
Fruit.java:
public class Fruit {
private Integer id;
private String name;
public Fruit(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object o) {
if (!(o instanceof Fruit)) {
return false;
}
return id == ((Fruit) o).getId();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Fruits.java:
#ManagedBean(name = "fruits")
#ViewScoped
public class Fruits implements Serializable {
private static final long serialVersionUID = 1L;
private ArrayList<Fruit> list = new ArrayList<Fruit>() {
{
add(new Fruit(1, "apple"));
add(new Fruit(2, "orange"));
add(new Fruit(3, "banana"));
add(new Fruit(4, "pineapple"));
add(new Fruit(5, "cocoa"));
}
};
public void onRowEdit(RowEditEvent event) {
Fruit fruit = (Fruit) event.getObject();
list.set(list.indexOf(fruit), fruit);
}
public ArrayList<Fruit> getList() {
return list;
}
public void setList(ArrayList<Fruit> list) {
this.list = list;
}
}
fruits.xhtml:
<h:head />
<h:body>
<h:form id="form">
<p:dataTable id="fruits" value="#{fruits.list}" var="fruit"
editable="true" sortBy="#{fruit.name}">
<p:ajax event="rowEdit" listener="#{fruits.onRowEdit}" update=":form"></p:ajax>
<p:column headerText="Name" sortBy="#{fruit.name}">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{fruit.name}" />
</f:facet>
<f:facet name="input">
<h:inputText value="#{fruit.name}" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:rowEditor />
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
Any ideas how I could fix the problem?
Today I had the same problem and found a workaround:
Create a remoteCommand which updates the table and call this remoteCommand as oncomplete action after the cellEdit ajax event.
Snippet:
<h:form id="form">
<p:remoteCommand name="remoteCmd" update="table" />
<p:dataTable id="table" ...>
<p:ajax event="cellEdit" listener="#{anyClass.onCellEdit}"
oncomplete="remoteCmd();" />
<p:column headerText="header">
values
</p:column>
</p:dataTable>
</h:form>
try to use #form instead of :form
using :form:fruits is the correct form I don't know why it's not working with you.
I already tried all of possible keywords (#this, #form, #all), but none of these helped.
Today I added a Refresh button to the end of form. Using this button I can refresh the table as expected. But I don't really undrestand why update=":form"doesn't works with event "rowEdit".
<p:commandButton value="Refresh">
<p:ajax update=":form"></p:ajax>
</p:commandButton>
I have the same or a similar problem in PF 6.1. If I don't sort the datatable by clicking a sortable header, I can edit a cell just fine (I'm using cell edit, not row edit) and all is good. If I sort the table on any of the sortable headers and then edit a cell, the model data is not corrupted but the edited cell displays the value of a seemingly random cell above or below it in the table, as if it is displaying the value the cell would have had if the table not sorted. The other cells in the row are fine. If I click in the cell to re-edit it, the displayed value in the input element is correct; if I then move focus out of the cell, the incorrect value reappears. If I then refresh the page, or do another sort, the values displayed in the table are correct.
I have encountered a scenario where the model data is corrupted but I cannot reproduce it now.
I tried Gregor's workaround with RemoteCommand, no change.
I also have a "Refresh" button which does clear the problem but it's not intuitive to the user do use it for this purpose.
I know this isn't an answer but it may help someone troubleshooting the same problem. Just adding to the knowledgebase.
I have the following xhtml, validator, and managedBean:
<h:form id="form">
<ui:repeat var="item" value="#{myBean.usersEmail}" varStatus="status">
<p:inputText id="userEmail" value="#{item.email}">
<f:validator validatorId="MyValidator"/>
</p:inputText>
<p:commandButton value="++++" update=":form" action="#{myBean.addEmail()}" />
</ui:repeat>
</h:form>
#FacesValidator("MyValidator")
public class ValidationClass extends Validator {
#Override
public void validate(FacesContext ctx, UIComponent component, Object value) throws ValidatorException {
String email = value.toString();
EmailValidator validator = EmailValidator.getInstance();
if(StringUtils.isNotBlank(email) && !validator.isValid(email)) {
FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_ERROR);
message.setSummary("Email is not valid.");
message.setDetail("Email is not valid.");
ctx.addMessage("userEmail", message);
throw new ValidatorException(message);
}
}
}
#ManagedBean
public class MyBean{
#Getter
#Setter
List<UserEmail> usersEmail = new ArrayList<UserEmail>();
public void addEmail(){
usersEmail.add(new UserEmail());
}
}
public class UserEmail{
#Getter
#Setter
String email = "";
}
The email addition works fines until the first validation fail.
When this happens, all inputText components show the same values.
For example, first I add "user1#gmail.com", this works ok.
Then I add "user2#gmail.com", this also works ok.
Then I change "user1#gmail.com" to "", this throws a validation exception, which is shown on the screen, and everything is still ok.
But then I correct the "" with "user3#gmail.com" and submit, this time all inputText start showing "user2#gmail.com", even when I add a new InputText, which also shows "user2#gmail.com".
It seems that when the validation fail, all components inside ui:repeat get bound to the value of the last item. Any thoughts?
I changed my implementation to use the c:forEach tag from JSTL and now it's working fine, even on Mojarra 2.2.6, here it's what I did:
<c:forEach var="item" items="#{myBean.usersEmail}" varStatus="status">
<p:inputText id="id${status.index}" value="${item.email}" validator="MyValidator" />
<p:message for="id${status.index}" />
<p:commandButton value="+" update=":form" action="#{myBean.addEmail()}" />
</c:forEach>
I have a ajax event blur that validate if a email already exist on database and it works.
<h:outputText value="Email:" />
<p:inputText id="email" value="#{pessoaBean.pessoa.email}"
required="true" requiredMessage="Informar o email."
validatorMessage="Formato de email inválido" >
<p:ajax event="blur" listener="#{pessoaBean.verificaEmail}"
update="mensagens" />
<f:validateRegex
pattern="^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$" />
</p:inputText>
Bean Method:
public void verificaEmail() {
if (new PessoaDao().verificaEmail(pessoa) == true) {
FacesContext.getCurrentInstance().addMessage("formCadastrar:email",
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error",
"email already exist"));
}
}
When I submit my form with commandbutton it passes through my ajax validaton and submit my form even it have my error message displayed on screen.
My button code:
<p:commandButton value="Cadastrar"
actionListener="#{pessoaBean.cadastrar}"
update=":formPrincipal:tabelaPessoas,formCadastrar"
oncomplete="if (!args.validationFailed){PF('dialogCadastrar').hide();} "
/>
What happened here?
Don't do the validation work in a listener method, as JSF has proper validators for that. Acording to the official docs:
Individual Validators should examine the value and component that they are passed, and throw a ValidatorException containing a FacesMessage, documenting any failures to conform to the required rules.
So let's implement the one you need:
#FacesValidator("emailValidator")
public class EmailValidator implements Validator{
#Override
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
if (new PessoaDao().verificaEmail(value.toString()) == true) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error",
"email already exist"));
}
}
}
}
Then, you only need to attach the validator to the input you're interested in:
<p:inputText id="email" value="#{pessoaBean.pessoa.email}"
required="true" requiredMessage="Informar o email."
validatorMessage="Formato de email inválido" validator="emailValidator" />
This could of course be improved, you could add the regex validation into the code itself and also implement your own e-mail input composite component to be reused, with the validator already included.
See also:
Custom validation in JSF 2
JSF Composite Component tutorial