Primefaces datatable cell edit dropdown problems - java

I have Primefaces <p:datatable> with cellEdit mode enabled, means when cell is clicked, it changes to editing mode and when you click somewhere else (onblur) cell returns to output mode and calls cellEdit ajax event if its changed.
In datatable editable cells i use <p:selectOneMenu> and <p:autoComplete> with dropdowns. Primefaces generates HTML code of dropdowns outside the cell container, so every time I select something from dropdown, the cell saves value and exits the edit mode, and I need it to stay in edit mode.
I know this works properly with <h:selectOneMenu>, but using other elements is not an option for me.
Is there a way to make cell edit to ignore clicks on drop down?
Or is there a way to prevent that onblur event from firing while drop down is open?
Columns of datatable are dynamic in my case.
I use :
Primefaces 5.3
PrimeFaces Extensions 4.0.0
Mojarra 2.2.9
Wildfly 8
A basic example of this issue:
xhtml:
<h:form id="form">
<p:dataTable id="cars" var="car" value="#{dtEditView.cars}" editable="true" editMode="cell" widgetVar="cellCars">
<p:ajax event="cellEdit" />
<p:column headerText="Car">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{car.title}" /></f:facet>
<f:facet name="input"><p:inputText value="#{car.title}" style="width:100%" label="Car"/></f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Color">
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{car.color}" /></f:facet>
<f:facet name="input">
<p:selectOneMenu value="#{car.color}" style="width:100%">
<f:selectItem itemLabel="Red" itemValue="Red" />
<f:selectItem itemLabel="Blue" itemValue="Blue" />
<f:selectItem itemLabel="Green" itemValue="Green" />
</p:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
</h:form>
Backing bean:
#ViewScoped
#Named("dtEditView")
public class TestController implements Serializable {
List<Car> cars=new ArrayList<Car>();
#PostConstruct
public void init(){
cars.add(new Car("BMW","Red"));
cars.add(new Car("Alfa Romeo","Green"));
}
public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
}
Car object:
public class Car {
String title;
String color;
Car(String title, String color){
this.title=title;
this.color=color;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}

I'm not sure if this is directly related, but in Primereact for the same DataTable component, if cell edit and a Dropdown is used together you get the same behaviour when you click the dropdown and select a value in the list, it's interpretted as a click outside of the cell and so edit mode ends without saving the value.
This is rectified by adding appendTo='self' to the Dropdown, making the list appear attached to the dropdown and so to the cell, meaning the click isn't classed as being outside of the cell. Setting this value on the Dropdown results in the expected behaviour of the cell remaining in edit mode.

Related

Loaded Records not showing in view

So to make it relatively simple: I have some Primefaces-Page, which is supposed to represent database records in a table-structure.
I keep these records in a List<Customer> which resides in a #ConversationScoped backing bean. I have verified via debugging, that the List gets correctly filled with records from the Database (hibernate db FWIW). This is accomplished with the help of a "businessInterface" Distributor class, that is in fact nothing but a decoupling mask for (Database)Service-Classes.
As mentioned, I have verified that the Database, as well as the Distributor correctly return the expected values. Unfortunately in the View there are no records present and instead the emptyMessage is displayed.
The application is running on a jBoss 7.1.1-Final Application Server.
For better readability I have excluded the h:head, as well as h:body, ui:composition, ui:define and h:form around the provided code, as well as shortened the columns to the two different usages (property display and action exposition)
The View (shortened and truncated):
<ui:define name="inhalt">
<p:growl id="msgGrowl" autoUpdate="true" showDetail="true" />
<h:form onkeypress="if (event.keyCode == 13) {return false; }">
<p:dataTable var="customeritem" id="customerTable"
rowkey="#{customeritem.id}" value="#{customerListController.customerList}"
paginator="true" rows="13" autoUpdate="true"
filteredValue="#{customerListController.filteredCustomers}"
emptyMessage="no customers found!"
sortFunction="#{customerListController.filteredCustomers}">
<p:column sortBy="name" filterBy="name" headerText="Kunde"
filterMatchMode="contains">
<h:outputText value="#{customeritem.name}" />
</p:column>
<p:column>
<f:facet name="header">
<p:commandButton value="Neuer Kunde"
action="${customerListController.addCustomer()}"
icon="ui-icon-plus" />
</f:facet>
<p:commandButton id="doViewDetailsButton" icon="ui-icon-clipboard"
action="${customerListController.viewDetails(customeritem.getId())}" />
<p:tooltip for="doViewDetailsButton" value="Details ansehen" />
</p:column>
</p:dataTable>
</h:form>
</ui:define>
The Backing Bean:
#Named
#ConversationScoped
public class CustomerListController implements Serializable {
private static final long serialVersionUID = -5961625401284927892L;
private List<Customer> customerList = new ArrayList<Customer>();
private List<Customer> filteredCustomers = new ArrayList<Customer>();
#Inject
CustomerEditController customerEditController;
#Inject
CustomerDetailsController customerDetailsController;
#Inject
CustomerDistributor businessInterface;
public String addCustomer() {
return editCustomer(0l);
}
public String editCustomer(long customerId) {
setFilteredCustomers(null);
customerEditController.recieveCustomerById(customerId);
return Pages.CUSTOMER_EDIT;
}
public String viewDetails(long customerId) {
setFilteredCustomers(null);
customerDetailsController.recieveCustomerById(customerId);
return Pages.CUSTOMER_DETAILS;
}
public String deleteCustomer(long customerIdToDelete) {
businessInterface.delete(customerIdToDelete);
setFilteredCustomers(null);
fillCustomerList();
return Pages.CUSTOMER_LIST;
}
#PostConstruct
public void fillCustomerList() {
customerList.clear();
customerList.addAll(businessInterface.loadAll());
}
public List<Customer> getCustomerList() {
return customerList;
}
public List<Customer> getFilteredCustomers() {
return filteredCustomers;
}
public void setFilteredCustomers(List<Customer> filteredCustomers) {
this.filteredCustomers = filteredCustomers;
}
}
This used to work, when I had the Backing Bean in #SessionScoped, but as that required hackish workarounds to produce intuitive (and expected) behavior I decided to move the Backing Bean to a smaller scope. I therefore chose the #ConversationScoped, because the BackingBean needs to stay longer than the request lifecycle... (Also running a query against a db for every request is damn expensive...)
A short explanation on the CustomerEditController and CustomerDetailsController. They are the responsible ones for Editing and Showing further information on the single records if they are requested by clicking one of the Buttons.
The non-working stuff is the #PostConstruct public void fillCustomerList(). Everything else works as expected...
If you need any further information, please ask, I will provide context as needed ;)
I have found a successful workaround for this, but it's extremely hackish and I really dislike the approach, as it introduces additional behavior in a getter. I modified the Backing Bean as follows:
public List<Customer> getCustomerList() {
if (customerList.size() == 0) {
fillCustomerList();
}
return customerList;
}
But let me state this again,
this is definitely not the desired behavior and not a good approach at solving this problem.
EDIT:
I found a different fix after a little more digging and a lucky link. I modified the backing bean as follows:
#Inject
Conversation conversation;
#PostConstruct
public void init() {
if(conversation.isTransient()) {
conversation.end();
}
conversation.setTimeout(120000);
conversation.start();
}
and now It works even without the hackish behavior in the getter (as demonstrated above).

Set an ENUM with setPropertyActionListener

I'm trying to set a enum property with setPropertyActionListener but I'm not sure how to do it. Here's the entity:
#Entity
public class Invoice {
public enum InvoiceStatus { ACTIVE, CANCELED }
...
#Enumerated(EnumType.STRING)
private InvoiceStatus status;
...
public InvoiceStatus getStatus() {
return status;
}
public void setStatus(InvoiceStatus status) {
this.status = status;
}
And here's the command button which is suppose to set the status to ACTIVE with setPropertyActionListener
...
<h:form id="invoiceCreatedSuccessfully">
<p:dialog header="#{msg['title.success']}" widgetVar="invoiceCreatedSuccessfullyDialog" resizable="false" showEffect="fade" hideEffect="fade">
<h:panelGrid columns="2" rows="3" style="margin-bottom: 10px">
<h:outputText value="#{msg['message.invoiceCreatedSuccessfully']}" />
</h:panelGrid>
<p:commandButton value="#{msg['label.acknowledged']}" actionListener="#{invoiceManager.reload}" action="viewInvoices">
<f:setPropertyActionListener target="#{invoiceManager.invoice.status}" value="ACTIVE" />
</p:commandButton>
</p:dialog>
</h:form>
No errors are reported but the field 'status' in the DB is not being set. Can someone tell me why?
Strings are not directly converted to Enums in EL , you would need to a custom converted in your faces-config , jsf has one enum converter that should work for you,
<converter>
<converter-for-class>java.lang.Enum</converter-for-class>
<converter-class>javax.faces.convert.EnumConverter</converter-class>
</converter>
Now looking into the source code of EnumConverter , it seems like it works only if targetClass is available in converter.
So you would need to extend it to work with your enum ,
public class MyEnumConverter extends EnumConverter {
public MyEnumConverter () {
super(MyEnum.class);
}
}
<converter>
<converter-id>MyEnum</converter-id>
<converter-class>com.test.MyEnumConverter</converter-class>
</converter>
add <f:converter converterId="MyEnum"/> in your component.
If you have many Enums and to make things easy , you can have a look into omnifaces http://showcase.omnifaces.org/converters/GenericEnumConverter

a4j:ajax listener exception MethodNotFoundException

I started study RichFaces 4.2.2 and have a problem in simple example, I have an xml:
<ui:define name="content">
<h:form>
<rich:panel style="width: 50%">
<h:panelGrid columns="2">
<h:outputText value="Name:"/>
<h:inputText id="inp" value="#{echoBean.name}">
<a4j:ajax event="keyup" render="echo count" listener="#{echoBean.countListener}"/>
</h:inputText>
<h:outputText value="Echo:"/>
<h:outputText id="echo" value="#{echoBean.name}"/>
<h:outputText value="Count:"/>
<h:outputText id="count" value="#{echoBean.count}"/>
</h:panelGrid>
<a4j:commandButton value="Submit" actionListener="#{echoBean.countListener}" render="echo, count"/>
</rich:panel>
</h:form>
</ui:define>
and a simple bean:
#Component("echoBean")
#Scope(value = "session")
public class EchoBean {
private String name;
private Integer count = 0;
//getter setter methods here
public void countListener(ActionEvent event) {
count++;
}
}
And when i try to print in inputText i have exception:
Caused by: javax.el.MethodNotFoundException: /home.xhtml #35,112 listener="#{echoBean.countListener}": Method not found: com.example.training.bean.EchoBean#d523fa.countListener()
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:102)
at org.ajax4jsf.component.behavior.MethodExpressionAjaxBehaviorListener.processAjaxBehavior(MethodExpressionAjaxBehaviorListener.java:71)
at javax.faces.event.AjaxBehaviorEvent.processListener(AjaxBehaviorEvent.java:113)
at javax.faces.component.behavior.BehaviorBase.broadcast(BehaviorBase.java:98)
at org.ajax4jsf.component.behavior.AjaxBehavior.broadcast(AjaxBehavior.java:348)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:763)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:775)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1267)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
... 19 more
But why? With button this same listener works just fine and in docs for "listener" parameter in a4j:ajax it says that:
The expression must evaluate to a public method that takes an ActionEvent parameter, with a return type of void, or to a public method that takes no arguments with a return type of void
Why it uses countListener() without ActionEvent parameter? I don't get it.
For you to be able to use the listener attribute with RF4, your listener method should take an argument of the AjaxBehaviorEvent type, not an ActionEvent type. The other alternative approach as you can see from the error message is to define a standard java method that doesn't take arguments and has a void return type as in
public void countListener();
Why it uses countListener() without ActionEvent parameter? I don't get it.
That's the contract for the API, you're required to conform to be able to use it.
Use the bean function with the following signature
void as return type
ActionEvent object as parameter
Example for the bean function is as below
public void countListener(ActionEvent event) {}

Stateful Session Bean updates(?) on buttonclick

I'm fairly new to Java EE and I'm building a simple webshop using maven web application . I have a problem with my Stateful Session Bean. I've been searching the net and tried different sollutions(most of them for using servlets) but it doesn't seem to work.
Anyway, my problem is that I'm trying to use my session bean to keep track of what's in the shopping cart. I am using an arrayList to store the items. However, when I add a new item it just replaces the old item instead of adding it to the list. I'm guessing the session bean somehow updates or a new instance of it is created but I just can't seem to find any sollution or information about this.
The stateful session bean
#Stateful
#LocalBean
public class CartSessionBean{
private List contents;
public CartSessionBean(){
contents= new ArrayList();
}
public List getContents() {
return contents;
}
public void addProduct(String title) {
contents.add(title);
}
}
The Managed Bean
#ManagedBean
#RequestScoped
public class ProductController {
private List cartList = new ArrayList();
private int nrOfCartItems=0;
#EJB private CartSessionBean cart;
public String doAddCart(String title)
{
cart.addProduct(title);
setCartList(cart.getContents());
setNrOfCartItems(cart.getContents().size());
return "products.xhtml";
}
}
The Facelet
<h:form>
<p>
your cart contains <h:outputLabel class="" value="#{productController.nrOfCartItems}" /> items.
<ui:repeat value="#{productController.cartList}" var="cart">
<h:outputLabel value="#{cart}" />
</ui:repeat>
<h:commandButton value="go to checkout"/>
</p>
</h:form>
<h:form>
<h:dataTable value="#{productController.productList}" var="pr" border="0">
<h:column>
<h:graphicImage value="images/#{pr.picture}" />
</h:column>
<h:column>
<h2><h:outputText value="#{pr.product_name}"/></h2>
<p> in stock: <h:outputText value="#{pr.stock}"/><br/>
price: <h:outputText value="#{pr.price}"/> SEK<br/><br/>
<h:outputText value="#{pr.description}"/><br/></p>
<h:commandButton value="add to cart" action="#{productController.doAddCart(pr.product_name)}"/>
</h:column>
</h:dataTable>
</h:form>
Your managed bean should be SessionScope to live during the session.
In your case you always creating new ProductController bean for each request and because of that you always inject different CartSessionBean (there is no way how could container know that it should inject the same SessionBean into your RequestScope Managed Bean).

Why input (for example h:inputText) nested in h:dataTable do not update Bean model? [duplicate]

This question already has an answer here:
Using <h:dataTable><h:inputText> on a List<String> doesn't update model values
(1 answer)
Closed 6 years ago.
I have jsf page:
....
<form jsfc="h:form" action="">
<h:dataTable value="#{newMusician.strings}" var="preferredMusicGenre" id="musicGenresSelectTable">
<h:column>
<h:inputText value="#{preferredMusicGenre}" immediate="true"/>
</h:column>
</h:dataTable>
<p>
<input type="submit" jsfc="h:commandButton" value="Add" action="#{newMusician.saveNewMusician}"/>
</p>
</form>
....
And managed bean that has ArrayList of Strings:
#ManagedBean
#ViewScoped
public class NewMusician {
private ArrayList<String> strings = new ArrayList<String>();
public NewMusician() {
strings.add("olo");
}
public ArrayList<String> getStrings() {
return strings;
}
public void saveNewMusician() {
.....
}
....
}
Problem: When I change text in and press save button, in saveNewMusician() method I can see that ArrayList "strings" contain the same old value "olo", but not that one I inserted in input field.
The same problem if use h:selecOneMenu.
Situation is changed if use not string, but object that aggregate string and set value into string.
So if I'll use some POJO and change inputText to:
<h:inputText value="#{preferredMusicGenrePojo.string}" immediate="true"/>
Everything becomes Ok.
Question:
Why usage of 1 level getter <h:inputText value="#{preferredMusicGenre}"/> is incorrect, but usage of 2 level getter: <h:inputText value="#{preferredMusicGenrePojo.text}"/> is Ok?
A String is immutable. It doesn't have a setter for the value. You need to wrap this around in a bean (or POJO as you call it).
public class Musician {
private String preferredGenre;
// Add/generate constructor, getter, setter, etc.
}
Then change your managed bean as follows.
#ManagedBean
#ViewScoped
public class NewMusician {
private ArrayList<Musician> musicians = new ArrayList<Musician>();
public NewMusician() {
musicians.add(new Musician("olo"));
}
public ArrayList<Musician> getMusicians() {
return musicians;
}
public void saveNewMusician() {
// ...
}
// ...
}
And your datatable:
<h:dataTable value="#{newMusician.musicians}" var="musician">
<h:column>
<h:inputText value="#{musician.preferredGenre}" />
</h:column>
</h:dataTable>

Categories

Resources