I am trying to create an editable data table with dynamically generated columns.
The table renders as it should and when clicked the cells go into edit mode, but when enter is press or when focus leaves the cell I get a null pointer exception on the server. The onCellEdit
method gets called but the cell value is not set in car object.
- Am I doing something weird in the EL expression for the
value
attribute forp:inputText
orp:outputText
? Should I use some other property for thevar
attribute onp:dataTable
orp:columns
? What kind of EL expressions are valid here? - Have I missed something else?
- Or could this be a bug in PrimeFaces? I can see that the error occurs in
CellEditFeature.encode
when it expects to get aCellEditor
but that turns out to be null.
I have tried this with Primefaces 4.0 and 5.0 with the same result. I'm using JSF 2.2, Glassfish 4.0 and Java 1.8.0_20.
If anyone can help me figure out the cause of the problem I would be most grateful. It would also be useful to have a working example of dynamic editable datatables on Stack Overflow.
Bellow is a minimal example using the Primefaces showcase example of dynamically generated columns to which I have tried to add editing, and the exception stack trace.
Web page:
<p:dataTable id="cars" var="car" value="#{dtColumnsView.cars}" widgetVar="carsTable"
editable="true" editMode="cell" >
<p:ajax event="cellEdit" listener="#{dtColumnsView.onCellEdit}" update=":form:msgs" />
<p:columns value="#{dtColumnsView.columns}" var="column" columnIndexVar="colIndex"
styleClass="ui-editable-column">
<f:facet name="header">
<h:outputText value="#{column.header}" />
</f:facet>
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{car[column.property]}" /></f:facet>
<f:facet name="input"><p:inputText id="modelInput" value="#{car[column.property]}" /></f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>
Backing bean:
@ManagedBean(name = "dtColumnsView")
@ViewScoped
public class ColumnsView implements Serializable {
private static final long serialVersionUID = 1L;
private List<ColumnModel> columns;
private List<Car> cars;
@ManagedProperty("#{carService}")
private CarService service;
@PostConstruct
public void init() {
cars = service.createCars(10);
columns = Arrays.asList(
new ColumnModel("Id", "id"),
new ColumnModel("Brand", "brand"),
new ColumnModel("Year", "year"));
}
public List<Car> getCars() {
return cars;
}
public List<String> getBrands() {
return service.getBrands();
}
public List<String> getColors() {
return service.getColors();
}
public void setService(CarService service) {
this.service = service;
}
public List<ColumnModel> getColumns() {
return columns;
}
public void onCellEdit(CellEditEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
System.out.println("Cell Changed, Old: " + oldValue + ", New:" + newValue);
if (newValue != null && !newValue.equals(oldValue)) {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Cell Changed", "Old: " + oldValue + ", New:" + newValue);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
public void dumpCars() {
for (Car c : cars) System.out.println(c.brand);
}
static public class ColumnModel implements Serializable {
private static final long serialVersionUID = 1L;
private String header;
private String property;
public ColumnModel(String header, String property) {
this.header = header;
this.property = property;
}
public String getHeader() {
return header;
}
public String getProperty() {
return property;
}
}
}
Exception stacktrace:
java.lang.NullPointerException
at org.primefaces.component.datatable.feature.CellEditFeature.encode(CellEditFeature.java:54)
at org.primefaces.component.datatable.DataTableRenderer.encodeEnd(DataTableRenderer.java:76)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:924)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1863)
at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:559)
at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
at org.primefaces.component.api.UIData.visitTree(UIData.java:675)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
at javax.faces.component.UIForm.visitTree(UIForm.java:371)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:399)
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:319)
at org.primefaces.context.PrimePartialViewContext.processPartial(PrimePartialViewContext.java:57)
at javax.faces.component.UIViewRoot.encodeChildren(UIViewRoot.java:1004)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:417)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:131)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:745)