Validation for selected row in JSF h:datatable - validation

I am having a tough time trying to find a solution to the following design related to h:dataTable.
I have certain number of rows predisplayed. The first column is only checkboxes. The rest of the columns are disabled by default. On selecting a checkbox the elements in the corresponding rows get enabled. On submit of the for the values in the enabled row have to be validated on the server side. I am able to validate for invalid inputs but am not finding a method to use required="true" conditionally. Or any other method. Could anyone please help me on this.
Thanks
Barun

I'm guessing you have a bean that looks something like this...
public class SomeBean {
boolean selected = false;
String someProperty;
...
}
If you have a controller for those beans your markup would look something like this...
<h:dataTable value="#{SomeController.someBeans}" var="someBean">
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectBooleanCheckbox value="#{someBean.selected}"/>
</h:column>
<h:column>
<f:facet name="header">Input</f:facet>
<h:inputText value="#{someBean.someproperty}" required="#{someBean.selected}"/>
</h:column>
</h:dataTable>

You should have a method like:
public boolean isSelected(){
return selected;
}

Related

JSF form validation skip specific validator for second submit when user wants to override

I am validating a user entered account number using two validators, one for basic standard format, and the other that validates the account number against values stored in a database. The database of valid account numbers may not always be up to date so I want to allow the user to override and submit their entered account number but only after the database validation has failed. I always want to validate its standard format 8 characters with no spaces.
<h:form id="formId">
<p:panelGrid>
<p:row>
<p:column>
<p:outputLabel value="Account : " for="acct" />
</p:column>
<p:column>
<p:selectOneMenu id="acct" value="#{bean.acct.acctNum}" effect="fold" editable="true" validator="acctLengthAndSpaceValidator" required="true" requiredMessage="Required">
<f:selectItems value="#{bean.mySavedAccounts}" var="acct"
itemLabel="#{acct.acctNumber} itemValue="#{acct.acctNumber}" />
<o:validator validatorId="accountDatabaseValidator" disabled="#{bean.skipDbValidation}" />
</p:selectOneMenu>
</p:column>
<p:column>
<p:messages for="acct" showDetail="true" skipDetailIfEqualsSummary="true" />
</p:column>
</p:row>
</p:panelGrid>
<br />
<p:selectBooleanCheckbox rendered="#{facesContext.validationFailed}" value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" listener="#{bean.testListener()}" />
</p:selectBooleanCheckbox>
<p:commandButton value="Submit" action="#{bean.submit()}" update="formId"/>
</h:form>
The checkbox does appear after the form is initially submitted and has any validation failure (I will figure out how to isolate to just the failed accountDatabaseValidator). But then when I select the checkbox, and submit again, both validators are still fired. I added the ajax listener to debug, and it isn't firing and the boolean value skipDbValidation is still false.
Perhaps my approach is not correct in achieving my concrete goal of validating against the database but then giving the user the option of skipping the db validation after initial failure.
EDIT
if i remove rendered="#{facesContext.validationFailed}" from the checkbox and have it visible all the time, the boolean skipDbValidation will get set to true if the checkbox is checked and then on subsequent submit, the skipDbValidation is ignored as expected. But I do not want the checkbox allowing the user to bypass visible at first. Only after validation fails.
The technical explanation that this doesn't work is that the rendered attribute is re-evaluated during processing the form submit. At this point the faces context is not validationFailed anymore (it was only validationFailed during the previous request) and thus the component is not rendered anymore and then the component's submitted value won't be applied. This matches #6 of commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated.
Your work around by rendering it client-side rather than server-side is acceptable. But I gather that you wanted to show it only when the specific validator has been invoked. There are at least 2 ways to achieve this:
Check UIInput#isValid() of the input component of interest. You can achieve that by binding the component to the view (not to the bean!) via component's binding attribute so you can reference it elsewhere in the same view.
<p:selectOneMenu binding="#{acct}" ...>
...
</p:selectOneMenu>
...
<p:selectBooleanCheckbox styleClass="#{acct.valid ? 'ui-helper-hidden' : ''}" ...>
...
</p:selectBooleanCheckbox>
Note that I took the opportunity to reuse the PrimeFaces-provided style class.
Or, make the validator a view scoped bean and reference it via <o:validator binding> instead.
#Named
#ViewScoped
public class AccountDatabaseValidator implements Validator, Serializable {
private boolean validationFailed;
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
validationFailed = !isValid(value);
if (validationFailed) {
throw new ValidatorException(createError("Invalid value"));
}
}
public boolean isValidationFailed() {
return validationFailed;
}
}
<p:selectOneMenu ...>
<o:validator binding="#{accountDatabaseValidator}" ... />
</p:selectOneMenu>
...
<p:selectBooleanCheckbox rendered="#{accountDatabaseValidator.validationFailed}" ...>
...
</p:selectBooleanCheckbox>
My work around to get the checkbox to programmatically display and so the checkbox would function was to hide and display using CSS instead of the render attribute.
style="#{facesContext.validationFailed ? 'Display: inline' : 'Display: none;'}"
<p:selectBooleanCheckbox style="#{facesContext.validationFailed ? 'Display: inline' : 'Display: none;'}" value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" />
</p:selectBooleanCheckbox>
But I still can't figure out how to display the checkbox for a specific validation failure.
I will post another question for that
EDIT
Here is how I ended up displaying the checkbox only after the Invalid Account validation failure.
<p:selectBooleanCheckbox style="#{facesContext.messageList.stream()
.anyMatch(v -> v.summary == 'Invalid Account') or
bean.skipDbValidation ? 'Display: inline' : 'Display: none;'}"
value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" />
</p:selectBooleanCheckbox>

Primefaces dataTable single selection validation

I can validate a p:selectOneMenu like this:
<p:selectOneMenu id="eventTimezoneDropdown"
value="#{myBean.eventTimeZone}"
required="true"
requiredMessage="The TimeZone must be specified."
effect="none">
<f:selectItems value="#{myBean.timeZoneItems}"/>
</p:selectOneMenu>
Conceptually speaking, using a p:dataTable with single row selection will achieve the same goal - it allows you to select one row using a selection attribute instead of value attribute and using value attribute instead of <f:selectItems>.
<p:dataTable id="ActivitiesTable" var="row"
value="#{myBean.rows}"
selection="#{myBean.selectedRow}"
rowKey="#{row.activityId}">
<p:column selectionMode="single"/>
...
</p:dataTable>
However I don't see any validation options (e.g. a required attribute) on the p:dataTable.
Is there a way or workaround to have a required selection validation on a p:dataTable similar to the required attribute on p:selectOneMenu?
Environment: Primefaces 5.3, JSF 2.2.8-14, Tomcat 7.0.68.
what i know in primefaces there are not this functionality in datatable, but you can play with your code,
i suggest that you can do the same event in your ManagedBean, so you can check if a row is selected or not, if yes then ok else you can create a simple message, that notifie the user that the selection of a row is required like that:
public void requiredSelect() {
if (myObject == null) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Error", "My object is required!"));
}
}
Another way to work around this issue is by using a hidden input field, bound to the same value which gets updated on row select.
<p:dataTable id="ActivitiesTable" var="row"
value="#{myBean.rows}"
selection="#{myBean.selectedRow}"
rowKey="#{row.activityId}">
<p:ajax event="rowSelectRadio" update="#parent:hiddenTimezoneInput"
<p:column selectionMode="single"/>
...
</p:dataTable>
<h:inputHidden id="hiddenTimezoneInput" value="#{myBean.selectedRow}"
required="true" requiredMessage="The TimeZone must be specified."/>
This way, you can add regular validators on the inputHidden (such as required) and have it processed on form submit.

In <p:dataTable> pagination not working with dynamicColumns+lazy+sorting

i'm having a lazyloading Datatable with dynamic columns generated
this is my dataTable
<p:dataTable var="iterator" id="dataTable"
value="#{MyManagedBean.lazyModel}"
paginator="true" rows="20"
lazy="true">
<p:columns value="#{MyManagedBean.columns}" var="column"
columnIndexVar="colIndex"
sortBy="#{iterator[column.property]}"
filterBy="#{iterator[column.property]}">
<f:facet name="header">
#{column.header}
</f:facet>
#{iterator[column.property]}
</p:columns>
</p:dataTable>
this works perfect without pagination. if i paginate, load() method does not give the SortField value, instead it gives me "property]"
can one help me in fixing this...
I believe this is a Primefaces bug.
I think the correct sort details should be passed to LazyDataModel.load() when paging in order to use in in your DB query.
Adding
|| (table.isLazy() && table.isPaginationRequest(context))
to the shouldDecode method in org.primefaces.component.datatable.feature.SortFeature should resolve this issue.
public boolean shouldDecode(FacesContext context, DataTable table) {
return isSortRequest(context, table) || (table.isLazy() && table.isPaginationRequest(context));
}
I've crated a new issue:
https://code.google.com/p/primefaces/issues/detail?id=7068

populate primefaces datatable from a nativeQuery

im learning jsf enviroment, sry if this is kind of easy case for you ,
Im trying to populate a primefaces datatable from a native query , this is what i got at the moment
//My native query is defined in my entity
#NamedNativeQueries({#NamedNativeQuery(name="Tallt089.bandejaCitas",
query ="select bandeja.ep_id_tallt089 idBandeja ...)})
...
...
I call this nativeQuery this way
public List**<TablaBandejaCitas>** bandejaCitas(String cia, String agencia, String division) {
Query query = em.createNamedQuery("Tallt089.bandejaCitas");
query.setParameter(1,cia);
query.setParameter(2,agencia);
query.setParameter(3,division);
return query.getResultList();
//this works fine retrieves correctly my query
}
And use it on my managedBean
public List**<TablaBandejaCitas>** bandejaCitas(String compania,
String agencia,String division){
return agendamientoSession.bandejaCitas(compania,agencia,division);
}
then referenced this on my jsf page like this
<p:dataTable id="bandeja_citas"
value="#{AgendamientoMBean.bandejaCitas(UsuarioMBean.compania,UsuarioMBean.agencia,
UsuarioMBean.divisionPK.diDivision)}"
var="bandeja"
paginator="true" rows="15" >
<f:facet name="header">
Bandeja Citas por confirmar/Llamadas por realizar
</f:facet>
<p:column headerText="Id Bandeja" >
<h:outputText value ="#{bandeja.idBandeja}"/>
</p:column>
<p:column headerText="Cliente" sortBy="#{bandeja.cliente}"
filterBy="#{bandeja.cliente}">
<h:outputText value ="#{bandeja.cliente}"/>
</p:column>
...
...
...
</p:dataTable>
I realized that the var property needs something like mapped of the fields of the query because the warnings on the jsf page tell me that this is an unkwon property
<h:outputText value ="#{bandeja.**cliente**}"/>
I dont know how to store the query in that variable so the data can be displayed
right now i got a for input string exception like the component its reading raw data instead of formmatted list with the correct variable filled with the query fields ..
hope you can understandme
apreciatte your comments in advance :D
Ok, I solved this little problem. I did it by creating an entity class (even is not a table in the DB) with the columns that I select in the nativeQuery and then using this class as the resultClass option in the native:
resultClass=com.talleresZeusWeb.entidades.BandejaCitas.class
I was trying to make that sqlresultsetmapping annotation but don't know to use it in this case.
Hope someone finds this useful at some point, thank you for your responses #Rich

h:inputText inside ui:repeater displays wrong value after an ajax update

I've got a JSF page with a ui:repeater tag that simply displays a list of strings and some controls to add a string to a list. When adding a string I use ajax to update the repeater tag and have the new string be shown immediately without the page refresh. Here's how my page looks like:
<h:body>
<h:form>
<p:inputText id="name" value="#{testController.newString}"/>
<p:commandButton value="Add" actionListener="#{testController.addString}" update="strings" />
</h:form>
<h:panelGroup id="strings">
<ui:repeat var="str" value="#{stringModel.strings}" varStatus="stringData">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:panelGroup>
</h:body>
Everything works except the inputText component. After ui-repeater is updated with Ajax is still displays the text from the previous string. For example, assume that initially i have a list with 2 strings, "val1" and "val2". I enter a new string called "val3" and submit the form. List is updated correctly on the server side and the repeater is updated, it now has 3 elements. However, while the h:outputText in the newly added element will correctly show "val3", the inputText will be displayed with "val2" as a value. So i end up with something looking like this:
output tag input tag
val1 val1
val2 val2
val3 val2 (???)
The backing beans are very simple:
A view scoped model bean
#Component
#Scope("view")
public class StringModel {
private List<String> strings = Lists.newArrayList("Value 1");
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
this.strings = strings;
}
}
And a request scoped controller bean:
#Component
#Scope("request")
public class TestController {
private String newString;
#Autowired private StringModel model;
public void addString() {
model.getStrings().add(newString);
}
public String getNewString() {
return newString;
}
public void setNewString(String newString) {
this.newString = newString;
}
}
I did some testing and this actually works the same way for any input component, be that textInput, textArea, etc. Any help would be highly appreciated.
I can't tell in detail exactly why it displays the wrong value after update (it'll be that the internal loop index of <ui:repeat> is broken — try a newer Mojarra version), but just referencing the string item by index from varStatus works. It'll also immediately fix the future problem of being unable to submit the edited string value when you put this list in a form, because the String class is immutable and doesn't have a setter.
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{stringModel.strings[loop.index]}" />
</div>
</ui:repeat>
EditableValueHolders inside ui:repeat are broken (by design) in the current version o JSF specs. It will not work, there is no way to fix it. Maybe new versions will make ui:repeat a proper component with support for saving states of its children. Maybe not.
If you change ui:repeat to h:dataTable, things should work (if not, then your problem is somewhere else and I was wrong).
Frankly, there is no workaround apart from using repeaters from some other libraries - you should find working repeaters in Tomahawk, Trinidad and many other places. Primefaces, AFAIR, does not have a pure repeater.
I also had exactly the same problem before. I solved it by putting the inputText in a form. I also copied your codes and put the h:inputText inside a h:form and it worked as well.
<h:form>
<ui:repeat value="#{stringModel.strings}" var="str" varStatus="loop">
<div>
<h:outputText value="#{str}" />
<h:inputText value="#{str}" />
</div>
</ui:repeat>
</h:form>

Resources