I have a JSF view with a Primefaces data table and a command button insite it, as fallows:
<p:messages id="statusMessages" showDetail="true" />
<h:form id="listForm">
<p:panel header="Wellsite List">
<br />
<h:outputLabel value="Welcome, #{wellsiteController.loggedUser.login}" />
<br />
<br />
<p:dataTable id="dataTable" var="wellsite" value="#{wellsiteController.wellsiteDataTableModel}"
paginator="true" rows="10" selection="#{wellsiteController.wellsite}">
<p:column selectionMode="single" style="width:18px" id="radioSelect" />
<p:column sortBy="#{wellsite.reference}" headerText="Wellsite ID">
<h:outputText value="#{wellsite.reference}" />
</p:column>
<p:column headerText="Allowed Groups">
<h:outputText value="#{wellsite.allowedGroups.toString()}" />
</p:column>
<f:facet name="footer">
<h:panelGrid columns="3">
<p:commandButton id="addWellsite" value="Add New Wellsite" icon="ui-icon-flag" ajax="false" action="#{wellsiteController.showAddWellsite}"/>
<p:commandButton id="editWellsite" value="Edit Selected Wellsite" icon="ui-icon-wrench" ajax="false" action="#{wellsiteController.showEditWellsite}"/>
<p:commandButton id="deleteWellsiteButton" value="Remove Selected Wellsite" icon="ui-icon-trash" onclick="confirmation.show()" type="button"/>
</h:panelGrid>
</f:facet>
</p:dataTable>
<p:spacer height="20" />
</p:panel>
<p:confirmDialog id="confirmDialog" message="Are you sure you want to remove the selected Wellsite along with all it's data?" header="Confirmation" severity="alert" widgetVar="confirmation">
<p:commandButton id="confirm" value="Yes" ajax="false" oncomplete="confirmation.hide()" action="#{wellsiteController.deleteWellsite}" />
<p:commandButton id="decline" value="Cancel" onclick="confirmation.hide()" type="button" />
</p:confirmDialog>
</h:form>
And here's the controller:
#ManagedBean(name = "wellsiteController")
#RequestScoped
public class WellsiteController implements Serializable {
private static final long serialVersionUID = 1L;
#ManagedProperty("#{wellsiteDao}")
private WellsiteDao wellsiteDao;
#ManagedProperty("#{userDao}")
private UserDao userDao;
#ManagedProperty("#{groupDao}")
private GroupDao groupDao;
#ManagedProperty("#{userController.loggedUser}")
private UserEnt loggedUser;
private WellsiteEnt wellsite;
private List<WellsiteEnt> wellsiteList;
DualListModel<GroupEnt> pickGroupsModel;
public WellsiteController(){
}
#PostConstruct
public void build(){
wellsite = new WellsiteEnt();
wellsite.setAllowedGroups(new ArrayList<GroupEnt>());
}
/*some getters & setters*/
public WellsiteDataTableModel getWellsiteDataTableModel(){
return new WellsiteDataTableModel(getWellsiteList());
}
public void setPickGroupsModel(DualListModel<GroupEnt> model){
pickGroupsModel = model;
}
public DualListModel<GroupEnt> getPickGroupsModel() {
if(pickGroupsModel == null){
List<GroupEnt> allGroups = groupDao.getAll();
List<GroupEnt> currentGroups = wellsite.getAllowedGroups();
for(GroupEnt g : currentGroups){
allGroups.remove(g);
}
pickGroupsModel = new DualListModel<GroupEnt>(allGroups, currentGroups);
}
return pickGroupsModel;
}
public String listWellsites(){
getWellsiteList();
return "listWellsites";
}
public String showAddWellsite(){
FacesContext context = FacesContext.getCurrentInstance();
setWellsite(new WellsiteEnt());
wellsite.setAllowedGroups(new ArrayList<GroupEnt>());
pickGroupsModel = null;
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,
"Fields annotated with a ' * ' are mandatory",""));
return "addWellsite";
}
public String addWellsite(){
FacesContext context = FacesContext.getCurrentInstance();
wellsite.setDate(new Date());
wellsite.setLastUpdate(wellsite.getDate());
try {
wellsiteDao.addWell(wellsite);
for(GroupEnt g : pickGroupsModel.getTarget()){
GroupEnt group = groupDao.getOne(g.getGroupId());
group.getGroupWellsites().add(wellsite);
groupDao.update(group);
}
return listWellsites();
} catch (Exception ex) {
Logger.getLogger(WellsiteController.class.getName()).log(Level.SEVERE, null, ex);
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
ex.getMessage(),""));
return null;
}
}
}
This view gets rendered correctly. The datatable and buttons looks fine. The problem is that when first i click the "addWellsite" commandButton, nothing happens. The page just seems to refresh. If i click it again, as exception happens:
java.lang.NumberFormatException: For input string: "null"
Using a debugger, i found out that the "addWellsite"'s action is NOT called the first time, and so, not outcome is generated (thus, the page refresh).
The exception is probably comming from the lack of initialization in the current or the target views (since both view are displayed from action methods that were not called in the page refresh)
The question is: WHY the action method is not called the first time?
As from this answer:
Whenever an UICommand component fails to invoke the associated action, verify the following:
UICommand components must be placed inside an UIForm component (e.g. h:form).
I do have a h:form
You cannot nest multiple UIForm components in each other (watch out with include files!).
There's only one.
No validation/conversion error should have been occurred (use h:messages to get them all).
I have a h:messages that does not display any error.
If UICommand components are placed inside an UIData component, ensure that exactly the same DataModel (the object behind the UIData's value attribute) is preserved.
The commandButton is inside the dataTable, but the target view does not need the dataModel. As my controller code shows, the object is built as the view tries to retrive it. The next request is not using this dataTable, to i do not handle it anymore.
The rendered and disabled attributes of the component and all of the parent components should not evaluate to false during apply request values phase.
There's no rendered or disbled attributes.
Be sure that no PhaseListener or any EventListener in the request-response chain has changed the JSF lifecycle to skip the invoke action phase.
No phaseListener is defined.
Be sure that no Filter or Servlet in the same request-response chain has blocked the request fo the FacesServlet somehow.
No other Servlet is defined. I don't even know what a Filter is.
WHY the action method is not called the first time?
This can happen when a parent of this <h:form> was been rendered by an ajax request initiated by another <h:form> beforehand. The rendered/updated <h:form> would then lose its view state. This is caused by a bug in the JavaScript API as described in JSF issue 790 which is already fixed for the shortly upcoming JSF 2.2.
In the meanwhile, with JSF 2.0/2.1, you need to explicltly specify the client ID of the <h:form> in the render (or for PrimeFaces, the update) attribute of the action in the other form.
E.g.
<h:commandButton ...>
<f:ajax ... render=":listForm" />
</h:commandButton>
or
<p:commandButton ... update=":listForm" />
Or just make it a normal (non-ajax) request instead.
See also:
Communication in JSF 2 - Ajax rendering of content which contains another form
Related
<f:setPropertyActionListener> does not call the setter method of target object when I have the <h:inputText> in the JSF page but it triggered properly when I interchange h:inputText with <h:outputText> and the values are loading properly. I am amused with the behavior of <h:inputText>. Here is the code
index.xhtml
<p:row>
<p:column><p:outputLabel value="ID"/></p:column>
<p:column ><h:inputText value="#{bean.obj.innerObj.id}"/></p:column>
</p:row>
<p:commandButton update=":form:detail"
oncomplete="PF('dialog').show()"
icon="ui-icon-search"
title="View">
<f:setPropertyActionListener value="#{detail}"
target="#{bean.obj}" />
</p:commandButton>
Bean.java
public Obj getObj() {
return obj;
}
public void setObj(Obj pObj) {
this.obj = pObj;
}
I don't believe there is an issue with Bean.java as the setter method was called when I have <h:outputText> in index.xhtml.
i have a problem about partial processing. (I use primefaces 3.4.1.) I have a dataGrid listing services and each row of the dataGrid has a commandButton, "remove service", to remove the service.
I remove the specified service from my service list in my backing bean and update the datagrid while "remove service" button is clicked.
Also, i have two buttons in the form;
First button, "add service", works to add new service; I add a new object to my service list in my backing bean and update the datagrid while "add service" is clicked.
The second, "save", button works to save all services. When "save button" is clicked, service list is inserted to database.
Btw, i need form validation since there are mandatory fields in my service object. So, the form should be validated while "add service" and "save" are clicked. But, the form
shouldn't be validated while "remove service" button is clicked. If i use 'process="#form"' for "remove service" button, everything is ok. I can remove the specified service
from my service list in the backing bean and the datagrid is updated properly. But as u know, since all form is processed, all input components are validated. If i use 'process="#this"' or 'immediate="true"' for "remove service" button, i can not reach the last entry. I mean, i cannot reach the last service. I guess it hasn't been posted yet when i click the "remove service" button.
So, what should i do? Is there any suggestion about this situation? Thanks in advance...
My code is as below;
<h:form id="formServicesTab">
<h:panelGrid id="pnlSvcTab" columns="1" styleClass="valignTop" width="100%">
<p:dataGrid id="dgServices" var="service"
value="#{subMerchantOperations.subMerchantServices}"
columns="1" width="100%" styleClass="valignTop"
emptyMessage="#{messagebundle.submerc_grdlabel_no_service}" transient="true"
rowIndexVar="index" >
<p:toolbar>
<p:toolbarGroup align="left">
<p:commandButton id="btnRemoveSvc" onclick="loading.show();"
oncomplete="loading.hide();"
value="#{messagebundle.submerc_btn_delete_service}"
actionListener="#{subMerchantOperations.removeService}"
process="#form" update="#form">
<f:setPropertyActionListener target="#{subMerchantOperations.serviceRowIndex}" value="#{index}" />
</p:commandButton>
</p:toolbarGroup>
</p:toolbar>
<h:panelGrid columns="3" styleClass="valignTop" width="100%" bgcolor="F0F0F0">
<p:panel style="background:#F0F0F0;">
<h:panelGrid id="pnlDgSvc" columns="1" width="100%" style="height:100%">
<h:outputText value="#{messagebundle.submerc_label_svc_shortName}" />
<h:panelGrid columns="2">
<p:inputText id="txtSvcName" value="#{service.serviceName}"
required="true" transient="true"
requiredMessage="#{messagebundle.submerc_validation_msg_required}"
converter="UpperCaseConverter" />
<p:message for="txtSvcName" display="text" />
</h:panelGrid>
<h:outputText value="#{messagebundle.submerc_label_svc_website}" />
<p:inputText value="#{service.serviceUrl}" transient="true"/>
</h:panelGrid>
</p:panel>
<p:panel style="background:#F0F0F0;">
<h:panelGrid columns="1" width="100%">
..........
</h:panelGrid>
</p:panel>
<p:panel style="background:#F0F0F0;">
<h:panelGrid id="pgSvcFiles" columns="1" width="100%">
...........
</h:panelGrid>
</p:panel>
</h:panelGrid>
</p:dataGrid>
</h:panelGrid>
<p:toolbar style="background:white">
<p:toolbarGroup align="left">
<p:commandButton id="btnAddNewSvc"
value="#{messagebundle.submerc_btn_addSvc}"
actionListener="#{subMerchantOperations.addNewService}"
process="#form" update="#form" />
<p:commandButton id="btnSaveSubM"
value="#{messagebundle.submerc_btn_sendApproval}"
action="#{subMerchantOperations.saveServices}"
process="#form" update="#form" />
</p:toolbarGroup>
</p:toolbar>
</h:form>
#ManagedBean
#ViewScoped
public class SubMerchantOperations implements Serializable {
private static final long serialVersionUID = 8556103952857187080L;
private List<Service> subMerchantServices = new ArrayList<Service>();
private int serviceRowIndex;
// add new empty service to the service list
public void addNewService() {
try {
Service svc = new Service();
svc.setStartDate(new Date());
getSubMerchantServices().add(svc);
}
catch(Exception ex) {
if (logger.isEnabledFor(Level.ERROR)) {
...
}
}
}
// Remove the specified service using index parameter got from the datagrid
public void removeService() {
try {
getSubMerchantServices().remove(serviceRowIndex);
}
catch(Exception ex) {
...
}
}
// DB Operations
public String saveSubMerchant() {
...
}
public List<Service> getSubMerchantServices() {
return subMerchantServices;
}
public void setSubMerchantServices(List<Service> subMerchantServices) {
this.subMerchantServices = subMerchantServices;
}
public int getServiceRowIndex() {
return serviceRowIndex;
}
public void setServiceRowIndex(int serviceRowIndex) {
this.serviceRowIndex = serviceRowIndex;
}
}
I have no idea what that transient attribute is doing on your <p:dataGrid>. I have reviewed the latest published version of the Primefaces PDF documentation and this is not listed as a valid attribute.
If you are finding that Facelet-based component validation is causing your empty form to fire validation errors on submit of one of your buttons, you might consider creating a method in your backing bean which is called by your submit method, and validates the state of elements on the bean. This is a popular approach with Primefaces, because unlike Seam we are not provided with form-level validation.
However, do check your understanding of immediate=true.
Alternatively you might consider annotating your POJO's properties with JSR303 annotations.
Since you're using Collections, ensure that your Service POJO overrides equals and hashcode to rule out that something screwy isn't going on with your collection and causing your problem.
My valuechangelistener method is not being called as I expected. I have a h:selectOneRadio component (below) and I'm expecting the valueChangeListener to be called when I click the add button, but only the add method is getting invoked, the setSelectedItem method is completely ignored. What am I missing? Note the javascript dataTableSelectOneRadio is executing fine.
Here is the .xhtml
h:panelGrid
Select Client to Associate with Appointment
<h:dataTable id="addClient" styleClass="dataTable"
value="#{AddEntryMB.clientValues}" var="c" binding="#{AddEntryMB.dataTable}" >
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectOneRadio valueChangeListener="#{AddEntryMB.setSelectedItem}"
immediate="true" onchange="dataTableSelectOneRadio(this);">
<f:selectItem itemValue="null" />
</h:selectOneRadio>
</h:column>
<h:column>
<f:facet name="header">Last Name</f:facet>
#{c.lastName}
</h:column>
</h:dataTable>
</h:panelGrid>
<h:panelGroup>
<h:commandButton action="#{AddEntryMB.add}" value="add" />
<h:commandButton action="cancel" value="cancel" immediate="true"/>
</h:panelGroup>
And here is the backing bean.
#ManagedBean(name="AddEntryMB")
#ViewScoped
public class AddEntryMB implements Serializable {
private int rowIndex;
private int idValue;
private transient HtmlDataTable dataTable;
public void setSelectedItem(ValueChangeEvent event) {
rowIndex = dataTable.getRowIndex();
}
public void add()
{
DefaultScheduleEntry entry = new DefaultScheduleEntry();
entry.setId(RandomStringUtils.randomNumeric(32));
entry.setStartTime(from);
entry.setEndTime(until);
entry.setTitle(title);
entry.setSubtitle(location);
entry.setDescription(comments);
}
The view scoped bean will be recreated during every RESTORE_VIEW phase when you bind a component by binding to a property of the view scoped bean. This is known as JSF issue 1492 and fixed for the upcoming JSF 2.2.
In this particular case, you'd better remove binding="#{AddEntryMB.dataTable}" and grab the current var="c" from the EL context.
public void setSelectedItem(ValueChangeEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
DefaultScheduleEntry c = context.getApplication().evaluateExpressionGet(context, "#{c}", DefaultScheduleEntry.class);
// ...
}
Im trying to implement the modification of an entity in JSF using Primefaces.
My main view, which lists the users is the following:
<p:growl id="growlEditUnit" showDetail="true" life="12000" />
<p:dialog id="dialogEditUnit" header="Edit Unit" widgetVar="editUnitDialog" showEffect="fade" hideEffect="fade" resizable="false" >
<ui:include src="editUnit.xhtml" />
</p:dialog>
<h:form id="form2">
<p:dataTable id="units" var="unit" value="#{unitController.unitsOfLoggedInUser}" >
<f:facet name="header">
Click Edit or Delete after selecting a unit to modify or remove it
</f:facet>
<p:column headerText="Code">
#{unit.unitCode}
</p:column>
<p:column headerText="Name">
#{unit.unitName}
</p:column>
<p:column headerText="Semester" >
#{unit.semester}
</p:column>
<p:column headerText="Academic Year">
#{unit.academicYear}
</p:column>
<p:column headerText="Twitter Username">
#{unit.twitterUsername}
</p:column>
<p:column headerText="Actions">
<p:commandButton id="editButton" value="Edit" action="#{unitController.setCurrent(unit)}" update=":dialogEditUnit" oncomplete"editUnitDialog.show()" />
</p:column>
</p:dataTable>
</h:form>
This view lists all the data correctly. However, when I press the current, my aim is to set the current attribute of the managed bean (code listed below) with the unit based on the button clicked. After this I try to update the edit dialog, so it will be filled with the values of that unit, and then make it visible using the oncomplete attribute. However, it seems that the managed been method setCurrent(unit) is never called when clicking the edit button. Subsequently the dialog is shown empty. Can someone help me with what am I doing wrong?
I am posting the managed bean code too.
#ManagedBean(name = "unitController")
#ViewScoped
public class UnitController implements Serializable {
private Unit current;
private List<Unit> unitsOfLoggedInUser;
#ManagedProperty(value="#{loginController.checkedUser}")
private Lecturer lecturer;
#EJB
private web.effectinet.ejb.UnitFacade ejbFacade;
#EJB
private web.effectinet.ejb.LecturerFacade lecturerFacade;
public UnitController() {
}
#PostConstruct
public void init(){
if (lecturer.getLecturerId() == null)
unitsOfLoggedInUser = null;
else
unitsOfLoggedInUser = (List<Unit>) lecturer.getUnitCollection();
}
public List<Unit> getUnitsOfLoggedInUser() {
return unitsOfLoggedInUser;
}
public void setCurrent(Unit current) {
this.current = current;
}
public Lecturer getLecturer() {
return lecturer;
}
public void setLecturer(Lecturer lecturer) {
this.lecturer = lecturer;
}
The action attribute of the commandButton is rendered without information on the value of the unit variable.
To pass the unit to the action method of your managed bean, then you need to pass the ID of unit in an <f:param> child tag of commandButton.
<p:commandButton action="#{managedBean.actionMethod}" ........>
<f:param name="unitid" value="#{unit.id}" />
</p:commandButton>
From your action method you can get the request parameter by the name from the ExternalContext and this will give you the ID of the unit that the commandButton was pressed for in your dataTable.
I want to handle all <p:panel> components inside the <h:panelGroup> in the backing bean. I have bound the <h:panelGroup> to a backing bean property. But during the #PostConstruct method the property is null. During the setter of the property setPanel(), the component has no children. If I submit something using a button, then I can get the panel's children.
What do I have to do to get the component's children during the #PostConstruct method?
View:
<h:panelGroup binding="#{wiz.panel}">
<p:panel header="one">
<h:outputText value="primeiro" />
</p:panel>
<p:panel header="two">
<h:outputText value="segundo" />
<h:inputText />
</p:panel>
<p:panel header="three">
<h:outputText value="terceiro" />
</p:panel>
<p:panel header="four">
<h:outputText value="quarto" />
</p:panel>
</h:panelGroup>
Backing bean
#PostConstruct
public void init() {
if (getPanel() != null) {
for (UIComponent comp : getPanel().getChildren()) {
if (comp instanceof Panel) {
System.out.println(((Panel) comp).getHeader());
}
System.out.println("not a panel child");
}
} else {
System.out.println("panel is null");
}
}
That is not possible. The #PostConstruct can be invoked long before the component tree is finished building. Rather hook on the preRenderView event. At that point the view is guaranteed to be already built. You can attach an event listener method by <f:event> in the view as follows:
<f:event type="preRenderView" listener="#{bean.init}" />
Don't forget to remove the #PostConstruct annotation from the init() method or to rename/split the method if necessary.
Needless to say that this approach is fishy. I suggest to rethink your approach and if necessary ask a new question wherein you state the functional requirement in detail. Perhaps the ultimate answer would be just using plain EL and/or rendered attribute.