I'm trying to build a fully dynamic DataTable with Primefaces 5.0.
I've got a Config-Object for eachs column and I want the DT to sort by one of them from the beginning.
Here's my DT:
<p:dataTable id="ticketTable"
widgetVar="ticketTable"
value="#{ticketBean.ticketDataModell}"
var="ticket"
lazy="true"
paginator="true"
rows="20"
sortBy="#{dataPortletConfigBean.sortByKey}"
>
The sortByKey is a String. It's the name of the Variable I want to sort by.
So I want to replace something like sortBy="key" with sortBy="#{bean.GiveMeAKey}".
But it doesn't work.
I get this: could not resolve property: sortByKey of: [...]
How can I get this to work?
Thanks
I got it working now.
First: The Problem.
I wanted to give the Data-Table the possibilty to have a default-sort (on load).
Thats what the sortBy-Tag in <p:dataTable> does or should do.
As long ass you write your sort-field in the DT-Tag e.g. sortBy="name" it works.
But when you pass a ValueExpression as for Example sortBy="#{bean.giveMeSomeKey}". He just cuts the '#{', everything bevor the '.' and the last '}' away an tries to sort by the field with the name of his result String. In this case 'giveMeSomeKey'.
That makes a default sorting with dynamic values impossible. (At least in PF 5.0)
Second: The Resolve.
I checked out the PF 5.0-Sources and modified the DataTableTemplate-File. (It's a template, wich is filled when PF is being built. It will later be compiled into the DataTable-Class).
There I modified the Method protected String resolveSortField().
Before:
protected String resolveSortField() {
UIColumn column = this.getSortColumn();
String sortField = null;
ValueExpression tableSortByVE = this.getValueExpression("sortBy");
Object tableSortByProperty = this.getSortBy();
if(column == null) {
sortField = (tableSortByVE == null)
? (String) tableSortByProperty
: resolveStaticField(tableSortByVE);
}
After:
protected String resolveSortField() {
UIColumn column = this.getSortColumn();
String sortField = null;
ValueExpression tableSortByVE = this.getValueExpression("sortBy");
Object tableSortByProperty = this.getSortBy();
if(column == null) {
sortField = (tableSortByVE != null)
? tableSortByVE.getExpressionString().contains("[")
? resolveDynamicField(tableSortByVE)
: resolveStaticField(tableSortByVE)
: (String) tableSortByProperty;
}
After building and including it into my project it worked.
So now I can tell my DT:
<p:dataTable [...] sortBy="#{ticket[dataPortletConfigBean.sortByKey]}"
And it will default-sort my DT After the (String)key I pass him with sortByKey, as long as it is a field in my Ticket.
It's not the perfect solution, but it works.
I tested your requirement and it worked fine.
Check if your property sortByKey in dataPortletConfigBean is returning a string like in:
public String getSortByKey() {
return "ticket.number";
}
Also ensure that the targeted column defines the sortBy attribute:
<p:column sortBy="#{ticket.number}" headerText="Number">
#{ticket.number}
</p:column>
I don't remember if this was available in PF 5.0 but now in 5.1 you can use the sortField attribute present in datatable. This was implemented for this exact purpose:
From Primefaces documentation:
sortField: Name of the field to pass lazy load method for
sorting. If not specified, sortBy expression is
used to extract the name.
After that you have to define a similar value for each column using the field attribute. If the columns are not dynamic just the sort then the field attribute will be the same value as the column itself has.
Snippet of code:
<p:dataTable var="repo" value="#{repoStrategy.flaggedRepos}" sortOrder="${repoStrategy.sortOrder}" sortField="${repoStrategy.sortBy}"
style="width: 100%" paginator="true" rows="10" rowIndexVar="rowIndex" >
<p:column headerText="Name" sortBy="#{repo.name}" field="repo.name">
#{repo.name}
</p:column>
Related
I am using display tag and spring mvc.
Basically, i have a simple table like this. Note the use of external.
<display:table id="tableId" name="data" sort="external" defaultsort="1" sort="external">
<display:column property="id" title="ID" sortable="true" sortName="id" />
<display:column property="firstName" sortable="true" sortName="firstName" title="First Name" />
<display:column property="lastName" sortable="true" sortName="lastName" title="Last Name" />
<display:column property="address" sortable="true" sortName="address" title="Email Address"/>
</display:table>
On the controller side, i get the sorted column index and the sort order.
String c = request.getAttribute(new ParamEncoder("tableId").encodeParameterName(TableTagParameters.PARAMETER_SORT));
To get the order (ASC/DESC):
String order = request.getAttribute(new ParamEncoder("tableId").encodeParameterName(TableTagParameters.PARAMETER_ORDER)));
All this is working fine. For the last step i used customized comparator to sort my list and put it back into the attribute "data" mapped himself to the table (see display tag "name" property).
public void populateModel(Model model, HttpRequest request){
String c = request.getAttribute(new ParamEncoder("tableId").encodeParameterName(TableTagParameters.PARAMETER_SORT));
// here the comparator is computed with column and order value eg
Comparator comp = new DefaultComparator();
if(c == 1){
comp = new NumericComparator();
}
List<Employe> list = Collection.sort(dao.getEmployee(), comp );
model.addAttribute("data", list);
// here the list is sorted properly
return "mypage";
}
Unfortunatly the display in the final jsp does not care about order in the list. It seem's something is overriding to a default and alphanumeric sorter...so even if i use my NumericComparator the column is still wrong sorted and i assume the "list" object has been sorted back by the librairie before the display.
To summup:
Get display tag indexes : ok
Sort the list and put it back to jsp : ok (list is sorted depending on display tag params in the controller)
Display the sorted list in the jsp side with display tag : KO (the library do not care about list order, sorting it with default sort)
Display the sorted list in the jsp side without display tag : OK
Any got the explanation about this, is there something wrong or missing ?
the problem
I'm trying to work with form validation in jsf 1.2. I have a form with rows of two input text fields.
I enter two rows of data, with one bad cell, like this:
| :) | :/ |
| :) | :) |
The validator is called once for each row, but checks both fields.
Each UIInput that fails validation is added to a list of failed UIComponents.
The method for the submit action finally gets to run.
First it restores any saved styles.
Then it loops over the failed UIComponents.
Inside the loop, it saves the current style, then sets the style to "badInput".
But when the page loads, both end-cells have the "badInput" style:
| :) | :/ |
| :) | :/ |
my code
This is my validator, a method on the managed bean that handles this page:
public void validateTime(FacesContext context, UIComponent component, Object value)
{
UIInput out = (UIInput) component.findComponent("out");
for (UIComponent uic : Arrays.asList(component, out))
{
String time = (String) ((UIInput)uic).getSubmittedValue();
if (!StringToTime.isValid(time))
{
// mark that we found invalid times
validTimes = false;
// save the failed component
// the click method will change the style during the render phase
failedUics.add(uic); // List<UIComponent>
badComps.put(uic.getClientId(context), uic); // Map<String, UIComponent>
}
}
}
And here's the table of input fields:
<h:dataTable binding="#{entryHandler.tableAttends}" value="#{entryHandler.attends}" var="range">
<h:column>
<div>
<h:outputLabel>
<h:outputText value="In: " />
<h:inputText value="#{range.start}" id="in" validator="#{entryHandler.validateTime}" />
</h:outputLabel>
<h:outputLabel>
<h:outputText value="Out: " />
<h:inputText value="#{range.end}" id="out" />
</h:outputLabel>
<h:commandLink action="#{entryHandler.delAttend}" value="X" styleClass="removeTime" />
</div>
</h:column>
</h:dataTable>
I've tried applying the bad input style these two ways:
for (UIComponent target : failedUics)
{
log.debug("target client id: " + target.getClientId(context));
Map<String, Object> attr = target.getAttributes();
// save the style before changing it
String style = (String) attr.get("styleClass");
originalStyle.put(target.getClientId(context), style);
// add the badInput css class
if (style == null) style = "";
attr.put("styleClass", "badInput " + style);
}
failedUics = new ArrayList<UIComponent>();
and the second:
UIComponent root = context.getViewRoot();
for (String clientId : badComps.keySet())
{
root.invokeOnComponent(context, clientId, new BadInputCallback(originalStyle));
}
badComps = new HashMap<String, UIComponent>();
where this is the callback function:
private static class BadInputCallback implements ContextCallback
{
private final Map<String, String> originalStyle;
public BadInputCallback(Map<String, String> originalStyle)
{
this.originalStyle = originalStyle;
}
#Override
public void invokeContextCallback(FacesContext context, UIComponent target)
{
Map<String, Object> attr = uic.getAttributes();
// save the style before changing it
String style = (String) attr.get("styleClass");
originalStyle.put(target.getClientId(context), style);
// add the badInput css class
if (style == null) style = "";
attr.put("styleClass", "badInput " + style);
}
}
Your concrete problem is caused because there is physically only one input component in the component tree, whose state changes whenever the parent UIData component iterates over every item of the model. When you want to set the styleClass dynamically, you basically need to let it depend on the currently iterated item, like so:
<h:dataTable ... var="item">
<h:column>
<h:inputText ... styleClass="#{item.bad ? 'badInput' : ''}" />
</h:column>
</h:dataTable>
Or when you're already on JSF 2.x, then check UIInput#isValid() instead whereby the UIInput is referenced via implicit EL variable #{component}, like so:
<h:dataTable ... var="item">
<h:column>
<h:inputText ... styleClass="#{component.valid ? '' : 'badInput'}" />
</h:column>
</h:dataTable>
This problem is already identified before and taken into account in among others the JSF 1.2 targeted SetFocusListener phase listener on The BalusC Code and the JSF 2.0 <o:highlight> component of JSF utility library OmniFaces.
Both have under the covers the same approach: they collect the client IDs of all invalidated input components and pass them as an array to JavaScript code which in turn sets the desired class name via HTML DOM.
See also:
What exactly is #{component} in EL?
how to set ui-state-error class to h:selectOneMenu on validation error
Styling input component after validation failed
Eclipse errors on #{component.valid}: "valid cannot be resolved as a member of component”
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
I have rich:select component inside a rich:dataTable column.
I added a4j:ajax tag to handle onblur event , to process some values related to this selected row.
But this worked only correct for the first row of the datatable , in the second row it do the same as in the first row.
When I traced my code I found that the datamodel.getRowData() method return the first row always.
Also I found that the datamodel.getRowCount() return the correct number of rows.
BUT datamodel.getRowIndex() return ZERO always.
ANY help ???
Here is my ManagedBean code (the needed code only) **
#ManagedBean(name = "saleBacking")
#SessionScoped
public class SaleBacking implements Serializable{
private DataModel<SaleLine> model ;
public DataModel<SaleLine> getModel() {
if (model == null) {
model = new ListDataModel<SaleLine>(salesLines);
}
return model;
}
public void updateRowData(AjaxBehaviorEvent event)
{
currentSaleLine = (SaleLine)getModel().getRowData();
System.out.println("Row count= "+getModel().getRowCount() + " , Row index= " + getModel().getRowIndex()) ;// this always return the correct rowCount but the index equal Zero (the first row of the datamodel)
if(currentSaleLine.getItemId() != null && currentSaleLine != null)
{
currentSaleLine.setItemPrice(currentSaleLine.getItemId().getItemDefEndUserPrice());
currentSaleLine.setPl(currentSaleLine.getItemId().getItemDefEndUserPrice());
currentSaleLine.setQuantity(1d);
currentSaleLine.setNetValue(currentSaleLine.getItemPrice() * currentSaleLine.getQuantity()) ;
calculateTotalSale();
}
}
}
Sale.xhtml
<rich:dataTable value="#{saleBacking.salesLines}" var="line" rows="50" id="datatable">
<rich:column>
<f:facet name="header"><h:outputLabel value="#{msgs.item}" style="font-size:15px;"/></f:facet>
<rich:select enableManualInput="true"
value="#{line.itemId}"
clientFilterFunction="containsFilter"
converter="#{itemConverter}"
defaultLabel="please write the item name" onlistshow="alert('jhjhj');"
>
<f:selectItems value="#{saleBacking.items}" var="item" itemValue="#{item}" itemLabel="#{item.code} #{item.name}"/>
<a4j:ajax event="blur" execute="#this" listener="#{saleBacking.updateRowData}" render="datatable master2" oncomplete="document.getElementById('myform:datatable:#{line.viewNo-1}:price').focus();"/>
</rich:select>
</rich:column>
</rich:dataTable>
I haven't used this construct before, so I'm not sure. But try SaleLine
currentSaleLine = context.getApplication().evaluateExpressionGet(context,
"#{line}", SaleLine.class);
To check if it returns the current row.
#BalusC yesterday
I've created a custom validator for my project, it simply checks the select ones value and 'validates' the value is not '0'. We have a standard (I'm sure not uncommon) of manually setting the first value of our selectOneMenu compents to:
<f:selectItem itemValue="0"
itemLabel="-- Select One --"/>
Which works fine, but then makes the component always pass the required check. So this validator simply treats this value as if there was no selection made.
SelectOneMenu example:
<h:selectOneMenu id="eligibility"
value="#{reg.eligibility}"
required="#{reg.fieldsRequired}">
<f:selectItem itemValue="0"
itemLabel="-- Select One --"/>
<f:selectItems value="#{reg.eligibilityList}" />
<f:validator validatorId="selectOneValidator"
disabled="#{!reg.fieldsRequired}"/>
Custom Validator:
#FacesValidator("selectOneValidator")
public class SelectOneValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent uiComponent, Object o) throws ValidatorException {
String val = null;
if (uiComponent instanceof HtmlSelectOneMenu) {
HtmlSelectOneMenu oneMenu = (HtmlSelectOneMenu) uiComponent;
if (oneMenu.isRequired() && !oneMenu.isDisabled()) {
if (o instanceof String) {
val = (String) o;
} else if (o instanceof Number) {
val = String.valueOf(o);
}
if ("0".equals(val)) {
FacesMessage msg = new FacesMessage();
msg.setSummary("Please select a value from the list.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
}
}
This has worked just fine thus far, however, the current use-case I'm running into trouble.
My page has multiple SelectOneMenus one of which toggles the required and disabled attributes via an ajax call for the page. I'm not having any issues with the required="#{reg.fieldsRequired}", however; the disabled="#{!reg.fieldsRequired}" attribute on my custom validator does not seem to make a difference. I'm just thinking out loud, but when the page first loads the #{reg.fieldsRequired} expression is false. If I then change the SelectOneMenu to set this boolean value to true, then press the submit button, the disabled attribute doesn't seem to have been set. I wondered if this is simply a ajax issue and that all of components simply needed to be re-renderd so I added the #form in my ajax call: <a4j:ajax render="#form" listener="#{reg.saveActionChanged}"/> in hopes that would fix the problem, but no difference in the behavior.
Environment:
JSF 2.0.3
Tomcat 6.0.14
ajax call being made with RichFaces 4.0
Any help is most appreciated!
Sorry, I can't reproduce your problem with JSF 2.1.3. The <f:validator disabled> works as expected.
However, as a completely different alternative, you can in this particular case just make use of the standard required validator. You only need to set the item value to #{null} instead of 0.
<f:selectItem itemValue="#{null}" itemLabel="-- Select One --"/>
This way you don't need the custom validator. The message can be set as requiredMessage attribute of the input component.
<h:selectOneMenu requiredMessage="Please select a value from the list.">