How to access component's children during a #PostConstruct method? - jsf

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.

Related

setPropertyActionListener not fired the setter method of target

<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.

jsf datatable why commandLink clientid resolved before update model values phase

I have a datatable that iterates over a list, lets call it myList. I populate this myList based on some request parameters. Inside the datatable there are commandLinks. If i put a dummy entry into myList during apply request values phase, i can click on the first commandLink, and it works as it should (it is executed during invoke application phase, and by then the correct entries are in myList). If i dont do it, or i click on the second or later commandLink, nothing happens. So im guessing the clientId of the command button is resolved during apply request phase, even thought it is only used during the invoke application phase, which results in the broken commandLinks.
something like this:
<h:selectManyCheckbox styleClass="hidden"
value="#{cc.attrs.selectionList.selected}"
converter="#{cc.attrs.converter}" >
<f:selectItems value="#{cc.attrs.selectionList.all}"
var="item" itemValue="#{item}" itemLabel="" />
</h:selectManyCheckbox>
<h:dataTable value="#{cc.attrs.selectionList.selectedTest}" var="item">
<h:column>
<h:commandLink value="deselect" action="#{cc.attrs.selectionList.deSelect(item)}">
<f:ajax execute=":#{component.parent.parent.parent.clientId}"
render=":#{component.parent.parent.parent.clientId}" />
</h:commandLink>
</h:column>
</h:dataTable>
and the model:
public List<E> getSelected()
{
return myList;
}
public List<E> getSelectedTest()
{
if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.RESTORE_VIEW) && getSelectedList().isEmpty())
{
return Collections.singletonList(myList.get(0));
}
else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.APPLY_REQUEST_VALUES) && getSelectedList().isEmpty())
{
return Collections.nCopies(2, myList.get(0));
}
else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.PROCESS_VALIDATIONS) && getSelectedList().isEmpty())
{
return Collections.nCopies(3, myList.get(0));
}
else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.UPDATE_MODEL_VALUES) && getSelectedList().isEmpty())
{
return Collections.nCopies(4, myList.get(0));
}
return myList;
}
public void deSelect(E item)
{
myList.remove(item);
}
with this example, the top two commandLinks of the datatable works.
My question is why is this behaviour, and is there any way around without filling myList with dummy entries? I do not want to use any (viewscoped) backing bean to store the data.
During apply request values phase, JSF needs to iterate over the model in order to find the clicked command link. If the model changes incompatibly during the HTTP request wherein the form submit is processed (the postback) as compared to the initial HTTP request wherein the table with the command links is shown, then JSF may not be able to find the clicked command link and thus never queue the desired action, or the object representing the "current row" is not the same as the enduser intented.
If your bean is request scoped, then it should be written in such way that it initializes selectedTest in the constructor or #PostConstruct method based on some request parameter. At least, you should absolutely not perform business logic in getters.
You can pass the parameters necessary for reconstructing the selectedTest as <f:param> in the command link.
<h:commandLink ...>
<f:param name="some" value="#{bean.some}" />
</h:commandLink>
And prepare the model as follows:
#ManagedProperty
private String some;
#PostConstruct
public void init(){
selectedTest = populateItBasedOn(some);
}
// Don't change standard getters/setters!
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 4
I managed to get my way around by binding the selectManyCheckbox itself to my componentBindings HashMap, and using that for the dataTable (with immediate="true" on the selectManyCheckbox):
<h:selectManyCheckbox immediate="true" styleClass="hidden"
binding="#{componentBindings[cc.attrs.selectionList]}"
value="#{cc.attrs.selectionList.selected}"
converter="#{cc.attrs.converter}" >
<f:selectItems value="#{cc.attrs.selectionList.all}" var="item"
itemValue="#{item}" itemLabel="" />
</h:selectManyCheckbox>
<h:dataTable value="#{componentBindings[cc.attrs.selectionList].value}" var="item">
<h:column>
<h:commandLink value="deselect" action="#{cc.attrs.selectionList.deSelect(item)}">
<f:ajax execute=":#{component.parent.parent.parent.clientId}"
render=":#{component.parent.parent.parent.clientId}" />
</h:commandLink>
</h:column>
</h:dataTable>
in faces-config.xml:
<managed-bean>
<description>Holder of all component bindings.</description>
<managed-bean-name>componentBindings</managed-bean-name>
<managed-bean-class>java.util.HashMap</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>

ValueChangeListener not being invoked upon submit

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);
// ...
}

JSF Action Method not being called

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

Why is the getter called so many times by the rendered attribute?

Related to a previous example, i tried to monitor my get/set methods on the server (when they are called, and how often). So, my actual been look such :
#ManagedBean(name="selector")
#RequestScoped
public class Selector {
#ManagedProperty(value="#{param.profilePage}")
private String profilePage;
public String getProfilePage() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}
System.out.println("GET "+profilePage);
return profilePage;
}
public void setProfilePage(String profilePage) {
this.profilePage=profilePage;
System.out.println("SET "+profilePage);
}
}
and the only page who can call this method (it only calls the get method on rendered) is :
<!DOCTYPE html>
<ui:composition
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:panelGroup layout="block" id="profileContent">
<h:panelGroup rendered="#{selector.profilePage=='main'}">
// nothing at the moment
</h:panelGroup>
</h:panelGroup>
</ui:composition>
my stupor when i see the server log, and i see :
SET null
GET main
GET main
GET main
GET main
GET main
GET main
GET main
What? It call seven times the getProfilePage() method? (and also 1 time setProfilePage())
I would like to know why this behaviour :)
Thanks
ADDED AN EXAMPLE
Bean
#ManagedBean(name="selector")
#RequestScoped
public class Selector {
#ManagedProperty(value="#{param.profilePage}")
private String profilePage;
#PostConstruct
public void init() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}
}
public String getProfilePage() { return profilePage; }
public void setProfilePage(String profilePage) { this.profilePage=profilePage; }
}
profile.xhtml
<h:panelGroup layout="block" id="profileContent">
<h:panelGroup layout="block" styleClass="content_title">
Profilo Utente
</h:panelGroup>
<h:panelGroup rendered="#{selector.profilePage=='main'}">
<ui:include src="/profile/profile_main.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{selector.profilePage=='edit'}">
<ui:include src="/profile/profile_edit.xhtml" />
</h:panelGroup>
</h:panelGroup>
// profile_main.xhtml
<h:form id="formProfileMain" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="EDIT">
<f:setPropertyActionListener target="#{selector.profilePage}" value="edit" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>
// profile_edit.xhtml
<h:form id="formProfileEdit" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="Edit">
<f:setPropertyActionListener target="#{selector.profilePage}" value="editProfile" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
<h:commandButton value="Back">
<f:setPropertyActionListener target="#{selector.profilePage}" value="main" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>
In this example, i call the profile_main (as default); After (for example) I call profile_edit (by clicking on EDIT); After, I return to profile_main by clicking Back. Now, if i want to reload profile_edit (EDIT), i need to click many times on that command button. Why?
EL (Expression Language, those #{} things) won't cache the result of the calls or so. It just accesses the data straight in the bean. This does normally not harm if the getter just returns the data.
The setter call is done by #ManagedProperty. It basically does the following:
selector.setProfilePage(request.getParameter("profilePage"));
The getter calls are all done by rendered="#{selector.profilePage == 'some'}" during the render response phase. When it evaluates false the first time, in UIComponent#encodeAll(), then no more calls will be done. When it evaluates true, then it will be re-evaluated six more times in the following sequence:
UIComponent#encodeBegin() - Locates renderer for the begin of component.
Renderer#encodeBegin() - Renders begin of component.
UIComponent#encodeChildren() - Locates renderer for children of component.
Renderer#encodeChildren() - Renders children of component.
UIComponent#encodeEnd() - Locates renderer for end of component.
Renderer#encodeEnd() - Renders end of component.
The component and its renderer verifies during every step if it is allowed to render. During a form submit, if an input or command component or any of its parents has a rendered attribute, then it will also be evaluated during apply request values phase as part of safeguard against tampered/hacked requests.
True, this look like clumsy and inefficient. It was considered the achilles heal of JSF as per spec issue 941. It's been suggested to remove all those repeated checks and stick to the one done in UIComponent#encodeAll(), or to evaluate isRendered() on a per-phase basis. During EG discussion, it became clear the root of the problem is in EL, not in JSF, and that performance could be greatly improved with CDI. So there was no necessity to solve it from JSF spec side on.
If your concern is that the managed property should be checked only once after its setting if it's null or empty, then consider to move it into a method which is annotated with #PostConstruct. Such a method will be called directly after bean's construction and all dependency injection.
#PostConstruct
public void init() {
if (profilePage == null || profilePage.trim().isEmpty()) {
profilePage = "main";
}
}
See also:
Why JSF calls getters multiple times?
you can use CDI Producers methods.
It will be called many times, but the result of first call is cached in scope of the bean and is efficient for getters that are computing or initializing heavy objects!
See here, for more info.

Resources