Primefaces enum output facet updates wrong - jsf-2

I'm facing some problems using a primefaces cell editing table. So, I have a datatable, which contains an editable column. This datatable is populated through a list of jpa entity.
Focusing on wich matters, my editable cell, has an outputText in the output facet and a selectOneMenu in the input facet, which is populated by an enum.
My problem is that, datatable is correctly loaded at beginning, I can successfully edit the wanted field, selectOneMenu is correctly populated with enum. If I choose an option in the selectOneMenu, it gets fine, HOWEVER when I click outside the datatable (to quit editing mode), it gets a wrong value, since it gets the code, and it should get description.
My code:
Enum:
public enum EnumSimNao implements DetalheDominioEnum {
/**
* Sim
*/
S("Sim"),
/**
* Não
*/
N("Não");
Enum has a getter that refreshes the value based in some services. It always get those values from the service. I've tested it and the values are right here. When I say description, I mean "Sim" or "Nao", and the codes are respectively "S" or "N". From the database it comes a code, that is associated with enum through an #Enumerated attribute in an jpa entity. When I have #{tp.respostaObrigatoria.description} it returns me "Sim" or "Não" based on the returned code.
public String getDescription() {
DetalheEstaticoDominioEnumHelper.INSTANCE.fillDescriptions(this);
return description == null ? defaultDescription : description;
}
#Override
public void setDescription(String description) {
this.description = description;
}
xhtml:
<p:cellEditor>
<f:facet name="output">
<h:outputText
value="#{tp.respostaObrigatoria.description}" />
</f:facet>
<f:facet name="input">
<h:selectOneMenu value="#{tp.respostaObrigatoria}">
<f:selectItems value="#{Factories.enumSimNao}" var="simNao"
itemLabel="#{simNao.description}" itemValue="#{simNao}" />
</h:selectOneMenu>
</f:facet>
</p:cellEditor>
tp is an entity whcih comes from a list that comes from backing bean:
So, when I edit the cell, I can see both descriptions ("Sim" or "Nao"), but when I exit the edit mode it shows "S" or "N". Finally, if I refresh the page, it gets the correct description value I had choose.
Do you have any tip?
Thanks

Primefaces 3.5 has this bug which apparently was filed under this issue http://code.google.com/p/primefaces/issues/detail?id=6116 and solved in version 3.5.15 which is only available on Elite version. Version 4.0 seems to have this fixed.
I found a workaround for 3.5 which involves re-rendering the form enclosing the datatable which is working fine for me.
What you need to do is to use an ajax event listener inside the selectOneMenu component which triggers the render of the form like this:
<p:cellEditor>
<f:facet name="output">
<h:outputText
value="#{tp.respostaObrigatoria.description}" />
</f:facet>
<f:facet name="input">
<h:selectOneMenu value="#{tp.respostaObrigatoria}">
<f:selectItems value="#{Factories.enumSimNao}" var="simNao"
itemLabel="#{simNao.description}" itemValue="#{simNao}" />
<f:ajax listener="#{bean.submit}" render="#form" />
</h:selectOneMenu>
</f:facet>
</p:cellEditor>

Related

Saving p:dataTable selectionMode="multiple" for all different models on same table in same view

I have a datatable that contains checkbox and I want that when a user selects one or more items .. the list of his choices will be displayed in another page jsf
I have a selectOneMenu that allows the user to select a category(a datatable is filled with products from this category)..user can select one or more products from this table..The list of choices is displayed in another jsf page ..
I want that user can select items from the categiory 1 and add to this list articles from another category (2 or 3...) .. My problem is that once the user switches to another category (in the selectOneMenu)the list of choices is not recorded and if it goes to the confirmation page .. list of choices contains only the last items selected..Some one can tell me how can I save the chackbox choices even if the user select another category?
The code is visible in my previous question: checkbox in datatble using primefaces5
this are the changes I have done in my jsf ..But my problem persist ..Can you help me to find what wrong in my code (because even if #BalusC have generously offered me the above solution which seems very logical I don't know where is my mistake)Any guidance will be appreciated.Thanks in advance :)
<p:selectOneMenu id="category" value="#{catBean.category}" valueChangeListener="#{catBean.changeCategory}" var="line" style="width:150px">
<p:ajax listener="#{catBean.onCategoryChange}" update="product" />
<p:ajax process="product" listener="#{catBean.updateProducts}" update="product" />
<f:selectItem itemLabel="Select Category" itemValue="" noSelectionOption="true" />
<f:selectItems value="#{catBean.myItems}" />
</p:selectOneMenu>
<p:outputLabel for="product" value="" />
<p:dataTable id="product" var="line" value="#{catBean.myProducts}" selection="#{catBean.selectedItems}" style="margin-bottom:0" rowKey="#{line.cdProduct}">
On change of the dropdown, process the data table as well, remember the previous selection via a valueChangeListener, copy the current selection into a Map and restore the new selection from it in the ajax listener method:
<p:selectOneMenu value="#{bean.category}" valueChangeListener="#{bean.changeCategory}">
...
<p:ajax process="#this products" listener="#{bean.updateProducts}" update="products" />
</p:selectOneMenu>
<p:dataTable id="products" value="#{bean.products}" selection="#{bean.selection}" ...>
...
</p:dataTable>
With
private String category;
private String previousCategory;
private List<Product> products;
private List<Product> selection;
private Map<String, List<Product>> selections = new HashMap<>();
public void changeCategory(ValueChangeEvent event) {
previousCategory = (String) event.getOldValue();
}
public void updateProducts() {
selections.put(previousCategory, selection);
products = productService.find(category);
selection = selections.get(category);
}

can't refresh a selectOneMenu

first excuse my english if it's not correct ...
I've a probleme with a primeface's component, I'm trying to refresh a p:selectOneMenu from a p:commandButton, but it's doesn't work (it 's work on another xhtml page, but not here and I can't understand why ...)
First I select an item from an p:autocomplete which update a backingbean's attribute ( for example : userChoose ).
then the p:commandButton is able to call his listener and add userChoose to a list, but i can't refresh the selectOneMenu that display the list. I have to use another p:commandButton to refresh the list.
My form is included into a p:tabMenu in another xhtml page.
<p:autoComplete id="acPojo" value="#{forumBean.user}"
completeMethod="#{autoCompleteBean.completeUser}"
converter="#{userConverter}" forceSelection="true"
var="usr" itemLabel="#{usr.loginUtilisateur}" itemValue="#{usr}">
<p:column>
<h:outputText value="#{usr.loginUtilisateur}"/>
</p:column>
</p:autoComplete>
<p:commandButton value="ajouter" process="acPojo #this "
udpate=":tabView:formSujet:listeUser" actionListener="#{forumBean.addUser}"/>
<p:selectOneMenu value="#{forumBean.user}" converter="#{userConverter}" var="us" id="listeUser"
itemValue="#{us}" itemLabel="#{us.loginUtilisateur}">
<f:selectItems value="#{forumBean.newSujet.listeUserAllowed}" var="User"
itemValue="#{User}" itemLabel="#{User.loginUtilisateur}" />
<p:column>
<h:outputText value="#{us.loginUtilisateur}"/>
</p:column>
<p:ajax process="#this" />
</p:selectOneMenu>
<p:commandButton id="refreshAdmin" icon="ui-icon-arrowrefresh-1-w"
update=":tabView:formSujet:listeUser" />
Thanks for help.
From your code:
udpate=":tabView:formSujet:listeUser"
This has at least 2 (potential) mistakes. The right attribute name is update, not udpate. The one on your other button has however the right attribute name.
It that still doesn't work, then the other potential mistake is that the client ID tabView:formSujet:listeUser does not exist in HTML DOM tree (and thus JavaScript/jQuery is unable to find and replace it). That can happen if the <p:tabView> is dynamic (i.e. you're using <p:tabView value="#{bean.tabs}" var="tab">, because it prepents the tab index number in the final client ID like so tabView:0:formSujet:listeUser if it's the 1st tab.
But, after all, as both the dropdownlist and the commandbutton are in the same NamingContainer parent, you don't need an absolute client ID at all. Just the relative client ID should suffice:
update="listeUser"
Fix that on your both buttons.
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

Conditional cell edit in PrimeFaces datatable

I want to allow user to edit cells in data table only if some condition is met.
Initially I have tried <choose> to achieve this:
<p:dataTable var="item" value="${bean.items}" editable="true" editMode="cell">
<p:column headerText="column A">
<c:choose>
<c:when test="${item.isEditable}">
<p:cellEditor id="title">
<f:facet name="output">
<h:outputText value="#{item.title}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{item.title}"/>
</f:facet>
</p:cellEditor>
</c:when>
<c:otherwise>
<h:outputText value="#{item.title}"/>
</c:otherwise>
</c:choose>
</p:column>
...
but it does not work. Another approach is to use rendered attribute:
<p:column headerText="column A">
<p:cellEditor rendered="${item.isEditable}">
<f:facet name="output">
<h:outputText value="#{item.title}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{item.title}"/>
</f:facet>
</p:cellEditor>
<h:outputText value="#{item.title}" rendered="#{!item.isEditable}"/>
</p:column>
that works fine - user are able to edit only allowed cells.
But even if cell is not editable it still has ui-cell-editing class and looks like editable cell for user.
What is a correct way to apply condition to cell editing?
Thank you!
To properly learn the lesson of JSTL fail, it failed for the reason explained in the following answer: JSTL in JSF2 Facelets... makes sense? In a nutshell: #{item} isn't available at the moment JSTL runs.
Coming back to the concrete question: that style class is been inserted due to the combination editMode="cell" and the physical presence of <p:cellEditor> component in the <p:column>. The PrimeFaces datatable renderer isn't at all considering if the <p:cellEditor> is rendered or not. It just outright inserts the ui-editable-column style class which in turn triggers the ui-cell-editing style via JS/jQuery. You were looking in the right direction for the solution, JSTL which can conditionally physically add/remove JSF components in the JSF component tree, but unfortunately it won't work in this construct.
Your best bet is to post an issue report to the PrimeFaces guys whereby you ask to not only consider the physical presence of <p:cellEditor> component, but also its isRendered() outcome. Considering PrimeFaces version 3.5, that would be in line 796 of DataTableRenderer class which originally looks like this (newlines introduced for readability):
String styleClass = selectionEnabled
? DataTable.SELECTION_COLUMN_CLASS
: (column.getCellEditor() != null)
? DataTable.EDITABLE_COLUMN_CLASS
: null;
And should be modified as follows:
String styleClass = selectionEnabled
? DataTable.SELECTION_COLUMN_CLASS
: (column.getCellEditor() != null && column.getCellEditor().isRendered())
? DataTable.EDITABLE_COLUMN_CLASS
: null;
If you can't wait, in the meanwhile you could homegrow a custom renderer.
package com.example;
import org.primefaces.component.datatable.DataTableRenderer;
public class MyDataTableRenderer extends DataTableRenderer {
#Override
protected void encodeCell(FacesContext context, DataTable table, UIColumn column, String clientId, boolean selected) throws IOException {
// Copypaste here the original encodeCell() source code and make modifications where necessary.
}
}
Then, to get it to run, register it as follows in faces-config.xml:
<render-kit>
<renderer>
<description>Overrides the PrimeFaces table renderer with customized cell renderer.</description>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.DataTableRenderer</renderer-type>
<renderer-class>com.example.MyDataTableRenderer</renderer-class>
</renderer>
</render-kit>

JSF2+PF: Update dialog header when it opens or shows

Is there a way to update the dialog header text when it opens? I used to use the dynamic attribute and set it to true. But due to issues with Form and Bean state (the dialog has a form inside), I had to abandon its usage. There is a hook to the js attribute onShow but I'm not sure how to update the header text from there.
Here is a simplified version of what my dialog looks like. It is defined as a composite component (not shown):
<composite:interface componentType="addEditDialog">
...
</composite:interface>
<composite:implementation>
<p:dialog>
<f:facet name="header">
#{cc.headerText}
</f:facet>
....
</p:dialog>
...
</composite:implementation>
I have some custom code in the backing NamingContainer class that determines the actual header text when it is opened:
#FacesComponent("addEditDialog")
public class AddEditCompositeComponent extends UINamingContainer {
public String getHeaderText() {
....
}
}
I ended up wrapping the header text in a PF outputPanel with autoUpdate=true and it works for me.
<p:dialog>
<f:facet name="header">
<p:outputPanel autoUpdate="true">#{cc.headerText}</p:outputPanel>
</f:facet>
....
</p:dialog>

Does a converter always run before Model View is updated?

Example:
<h:form>
<h:selectOneMenu value="#{bean.timezone}>
<f:selectItems value="#{bean.availableTimezones} ... >
<f:ajax render="currenttime" />
</h:selectOneMenu>
</h:form>
<h:form id="currenttime">
<h:outputText value="#{bean.currentTime}" >
<f:convertDateTime dateStyle="short" type="both" timeZone="#{bean.timezone}" />
</h:outputText>
</h:form>
<!-- bean.currentTime is of type 'Date' -->
In the example, changing the timezone should cause the text in currenttime to show in the proper timezone. But it doesn't.
I figured this happens because converters are calculated in "Apply Request" phase and the value of the selected timezone is updated in "Update Model" phase.
Am I right?
Should I not use converters for this?
Thanks!
Your concrete problem is caused because <f:convertDateTime> is initialized during view build time, not during view render time (exactly like JSTL and so on). Indeed, this runs far before update model values phase and hence the converter will not get set with the user-submitted timezone during view render time.
This problem has basically the same grounds as answered in the following answers:
How to set converter properties for each row of a datatable?
JSF convertDateTime with timezone in datatable
One of the ways is managing and binding the converter instance as a bean property.
private DateTimeConverter converter;
#PostConstruct
public void init() {
converter = new DateTimeConverter();
converter.setDateStyle("short");
converter.setType("both");
}
public DateTimeConverter getDateTimeConverter() {
converter.setTimeZone(timezone);
return converter;
}
With
<h:outputText value="#{bean.currentTime}" >
<f:converter binding="#{bean.dateTimeConverter}" />
</h:outputText>
An alternative is using OmniFaces <o:converter which supports rendertime evaluation of converter's properties:
<h:outputText value="#{bean.currentTime}" >
<o:converter converterId="javax.faces.DateTime" dateStyle="short" type="both" timeZone="#{bean.timezone}" />
</h:outputText>

Resources