This question already has answers here:
How does the 'binding' attribute work in JSF? When and how should it be used?
(2 answers)
Closed 6 years ago.
I have some trouble dealing with PrimeFaces 5.0 and a panelGroup with "binding". I know, "binding" sucks, but I'm working with a legacy system and believe me, it's truly hard to make it the right way, without "binding".
So, when I click mi link, the getter of the "binding" property is called before the "action", then the action is called, and after all the "update". But, the component is not properly updated.
Even if I make it to run the "action" before the getter of the binding, and the getter is returning the right stuff, this particular component is not updated.
I have more components without binding which are updated properly, but not the one with "binding".
UPDATE:
Here is the rest of the code. flag starts with "false" value.
The page always print "READ ONLY".
Somepage.xhtml
<h:form id="frm">
<p:commandLink value="#{messages.Guardar}" id="bt_Guardar" action="#{myBean.flagFalse}" update="someid" />
<p:commandLink value="#{messages.Editar}" id="bt_Editar" action="#{myBean.flagTrue}" update="someid" />
<h:panelGroup id="someid" layout="block">
<h:panelGroup id="panelCamposTD" layout="block" binding="#{myBean.someStuff}" ></h:panelGroup>
</h:panelGroup>
</h:form>
And my myBean, wich is a SessionScoped bean (because it comes from a legacy system)
#ManagedBean(name="myBean")
#SessionScoped
public class MyBean implements Serializable{
private static final long serialVersionUID = 7628440827777833854L;
private boolean flag = false;
public void flagFalse(){
flag = false;
}
public void flagTrue(){
flag = true;
}
public HtmlPanelGroup getSomeStuff(){
HtmlPanelGroup pg = new HtmlPanelGroup();
HtmlOutputText t = new HtmlOutputText();
if (flag){
t.setValue("EDITED");
}else{
t.setValue("READ ONLY");
}
pg.getChildren().add(t);
return pg;
}
public void setSomeStuff(HtmlPanelGroup pg){
return;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
You should avoid bindings to managed beans with session scope. This can cause a lot of touble:
JSF 2.0 Spec
3.1.5 Component Bindings
[...]
Component bindings are often used in conjunction with JavaBeans that are dynamically instantiated via the Managed Bean Creation facility (see Section 5.8.1 “VariableResolver and the Default VariableResolver”). It is strongly recommend that application developers place managed beans that are pointed at by component binding expressions in “request” scope. This is because placing it in session or application scope would require thread-safety, since UIComponent instances depends on running inside of a single thread. There are also potentially negative impacts on memory management when placing a component binding in “session” scope.
There are two solutions which could avoid the binding:
In this solution, the output text is always visible and just get a different text from the backed bean:
<h:panelGroup id="someid" layout="block">
<h:panelGroup id="panelCamposTD" layout="block">
<h:outputText value="#{myBean.flaggedText}"></h:outputText>
</h:panelGroup>
</h:panelGroup>
Add the getter to the backed bean:
public String getFlaggedText(){
if (flag){
return "EDITED";
}else{
return "READ ONLY";
};
}
The other option is to put both output texts in the front end and hide one of them:
<h:panelGroup id="someid" layout="block">
<h:panelGroup id="panelCamposTD" layout="block">
<h:outputText value="EDITED" rendered="#{myBean.flag}"></h:outputText>
<h:outputText value="READ ONLY" rendered="#{not myBean.flag}"></h:outputText>
</h:panelGroup>
</h:panelGroup>
Related
I have a simple CDI bean. The problem is that when I invoke method removeCustomer from the JSF page, method getCustomers() is executed before removeCustomer instead the opposite way. First I thought that producer itself is the problem but I'm getting same behavior if I put the list inside the controller. When I get response page the row in the table is not removed ether it is removed from the database. When I refresh page once more I don't see the deleted row.
#Named
#RequestScoped
public class CustomersController {
#Inject
private CustomerService customerService;
#Produces()
#RequestScoped
#Named("customers")
public List<Customer> getCustomers() {
return customerService.findCustomers();
}
/**
* #param customerId
*/
public String removeCustomer(Long customerId) {
customerService.deleteCustomer(customerId);
return null;
}
}
<h:form id="customers-form">
<h:dataTable value="#{customers}" var="customer"
styleClass="table">
<h:column>
<f:facet name="header">#{msg['customer.name']}</f:facet>
#{customer.name}
</h:column>
<h:column>
<f:facet name="header">#{msg['customer.vat']}</f:facet>
#{customer.vat}
</h:column>
<h:column>
#{msg['global.edit']}
</h:column>
<h:column>
<h:commandButton styleClass="btn btn-danger"
action="#{customersController.removeCustomer(customer.id)}"
value="#{msg['global.delete']}" aria-hidden="true">
<f:ajax render="#form"></f:ajax>
</h:commandButton>
</h:column>
</h:dataTable>
I'm using JBoss WildFly beta 1 but I'm guessing that it uses stable JSF 2.x version.
Check this answer
The method getCostumers is called before because the EL expresion (#customers) is being evaluated several times while processing the request (more details in the link).
The solution is to make your getters and setters "dumb". Just make them change a variable of your class. Avoid logic with side effects (like customerService.findCustomers();). If you need initialization, you can perform it in a #PostConstruct method or in a method fired by a JSF event).
Also, take into account that with a #RequestScoped bean, a new instance of the bean will be created for every request (either to render the page, or to save changes to the values).
Actually it is pretty easy to achieve what I want.
Here is updated CDI bean.
#Named
#RequestScoped
public class CustomersController {
#Inject
private CustomerService customerService;
#Inject
private List<Customer> customers;
#Produces()
#RequestScoped
#Named("customers")
public List<Customer> getCustomers() {
return customerService.findCustomers();
}
/**
* #param customerId
*/
public void removeCustomer(Customer customer) {
customerService.deleteCustomer(customer.getId());
customers.remove(customer);
}
}
As you can see. On ajax request to remove the customer producer is invoked before method removeCustomer. Logically because in restore view phase jsf is restoring view and evaluating EL expressions and this is happening before event processing phase. So we have list of customers in request scope and we need to inject it in controller bean so we can remove from the list selected customer in the removeCustomer method. I hope I was clear enough.
I have written a piece of code to put two pairs of radio button in a *.xhtml page of a JSF project built in JSF2.0, Jboss As 7.1.1 final and JDK 6.0, PrimeFaces 3.4.2.
<h:panelGrid columns="2">
<h:selectOneRadio value="#{calendarBean.radioFirst}">
<f:selectItem id="morning" itemValue="morning" itemLabel="Morning"/>
<f:selectItem id="afternoon" itemValue="afternoon" itemLabel="Afternoon"/>
</h:selectOneRadio>
<h:selectOneRadio style="padding-left: 170px" value="#{calendarBean.radioSecond}">
<f:selectItem id="noon" itemValue="noon" itemLabel="Noon"/>
<f:selectItem id="endofday" itemValue="endofday" itemLabel="End Of Day"/>
</h:selectOneRadio>
</h:panelGrid>
The Bean is:
private String radioFirst="morning";
private String radioSecond="endofday";
public String getRadioFirst() {
return radioFirst;
}
public void setRadioFirst(String radioFirst) {
this.radioFirst = radioFirst;
}
public String getRadioSecond() {
return radioSecond;
}
public void setRadioSecond(String radioSecond) {
this.radioSecond = radioSecond;
}
I have written a method to invoke on a Action Event where I want to have the values of the radio button selected. when the method invokes I get the value of the radio buttons as "morning" and "endofday" as these two are the initial values of the bean variables.
private String radioFirst="morning";
private String radioSecond="endofday";
But if I change the radio button options, I don't get the updated values, its the same old initial values. How can I get the changed values? Please help!
Three things
Don't use the session scope for this usage scenario. This will go completely wrong when your users open the same page in more than one tab. Use request scope or view scope.
Use a regular action instead of an action listener, and don't provide the radio button values as attributes. This makes no sense. The selectOneRadio components will set the updated values on your backing bean. When your action method is invoked, you will have access to those values via the instance fields of your bean.
Leave out the window.reload. If you need a full page refresh after the action, set ajax to false on the p:commandButton or use an h:commandButton.
You should modify your action button like this :
<p:commandButton actionListener="#{userLeaveBean.addAppliedLeave}" styleClass="apply_button" value="Create Leave">
<f:attribute name="userId" value="#{employee.name}"/>
</p:commandButton>
As #MikeBraun suggested, remove the window.reload(). If you want to refresh something, you could do it by using the update attribute from the p:commandButton.
And modify your bean according to this :
#ManagedBean
#SessionScoped // Could probably be moved to #RequestScoped or #ViewScoped
public class UserLeaveBean
{
#ManagedProperty(value="#{calendarBean}")
CalendarBean calendarBean;
public void setCalendarBean(CalendarBean calendarBean)
{
this.calendarBean = calendarBean;
}
public void addAppliedLeave(ActionEvent event)
{
System.out.println("Radio 1 : " + calendarBean.getRadioFirst());
System.out.println("Radio 2 : " + calendarBean.getRadioSecond());
}
}
The ManagedProperty will get the other bean accessible for you by injecting it inside the current one. Use the proper scope, probably you are using SessionScoped because you are using window.reload().
Important : the ManagedProperty bean must be at least scoped the same length as the other bean.
Read more :
Injecting Managed Beans in JSF 2.0
PrimeFaces Showcase - commandButton
I want to store the selected value of the first attribute managedabean "refCont" in a second attribute of the managed bean and then I'll post. but the value will show that after the second selection knowing that the value displayed is the one selected for the first time. I do not know what's sake
.xhtml
<h:outputText value="* Contact" />
<h:selectOneMenu id="contact" value="#{reclamationMB.refCont}" onchange="lazyload()">
<c:set value="#{reclamationMB.refCont}" target="#{contactMB}" property="refContt" />
<f:ajax listener="#{contactMB.affichReclContact}" />
<f:selectItem itemLabel="Select One" noSelectionOption="true" itemValue="---" />
<f:selectItems value="#{contactMB.contactsClient}" noSelectionValue="" var="contact" itemValue="#{contact.refCont}" itemLabel="#{contact.nomCont}" />
<f:selectItems itemLabel="Autre" itemValue=""></f:selectItems>
</h:selectOneMenu>
first ManagedBean:
#RequestScoped
public class ReclamationMB extends AbstractMB implements Serializable {
private int refCont;//ref contact recuperé pour la creation de la reclamation
.....
//getter and setter
Second Mnaged bean
#RequestScoped
public class ContactMB extends AbstractMB implements Serializable {
#ManagedProperty("#{reclamationMB.refCont}")
private int refContt;
.....
//getter an setter
public void affichReclContact(AjaxBehaviorEvent event){
contact=service.AffichContact(refContt);
setContact(contact);
}
The problem is that JSTL's <c:set> is a view build time tag handler, and not a view render time UI component, so your problem is a classical one: when you expect it to set the value, it is no longer there in the component tree. Moreover, it has run when there was no <h:selectOneMenu> tag at all.
Of course, if <h:selectOneMenu> implemented ActionSource, just like <h:commandButton>, you could have nested JSF <f:setPropertyActionListener> for the reason of setting the bean property with the needed values, as in <f:setPropertyActionListener target="#{contactMB.refContt}" value="#{reclamationMB.refCont}">. But you can't do that.
Basically you can do two things:
Use PrimeFaces <p:remoteCommand> tag, which can hold the abovementioned action listener to set the needed value, and call it in onchange method. There are a bunch of such examples here on Stack Overflow and in excellent PrimeFaces' documentation.
Make use of bean injection, instead of just bean property injection. In the former case, the expression reclamationMB.getRefCont() will always yield the right result in the holder bean, provided that reclamationMB is the variable to hold the injected bean by #ManagedProperty.
I am new to jsf 2.0 and therefore i am facing a serious issue grouping the prime faces datagrid and radio buttons. I am using JSF 2.0-Primefaces 3.3, java 1.6, and running my application on tomcat server on windows operating system.
The issue is grouping data grid and radio button. The following paragraph will describe about an example code:
#ManagedBean(name="quickBill")
#ViewScoped
public class QuickBill extends BaseManagedBean implements Serializable {
private List<String[]> insurerIDs=null;
#PostConstruct
public void init(){
loadInsurer();
}
private void loadInsurer(){
logger.info("Entered Load Insurer");
insurerIDs = new ArrayList<String[]>();
if (null != insurerList) {
insurerIDs.add(new String[] {"InsurerID1","Insurer_Name1") });
insurerIDs.add(new String[] {"InsurerID2","Insurer_Name2") });
insurerIDs.add(new String[] {"InsurerID3","Insurer_Name3") });
insurerIDs.add(new String[] {"InsurerID4","Insurer_Name4") });
}
}
public List<String[]> getinsurerIDs() {
return insurerIDs;
}
}
The jsf code is as follows:
<p:dataGrid var="quickBillVar" value="#{quickBill.insurerIDs}" columns="4" >
<p:column selectionMode="single">
<p:selectOneRadio id="options" value="#{quickBill.selectedOption}">
<f:selectItem itemLabel="#{quickBillVar[1]}" itemValue="#{quickBillVar[1]}"/>
</p:selectOneRadio>
<p:commandLink id="insurerid" title="Isurer">
<p:graphicImage value="../../images/insurers1/#{quickBillVar[0]}.jpg"/>
</p:commandLink>
</p:column>
</p:dataGrid>
The above code when executed results in the display of the radio button, which allows user to select multiple radio button at the same time. However the requirement is to restrict user to select only one button rather than allowing user to select multiple button at a time. I have also used selectionMode="single" to produce the expected output but it didn't help much.
Kindly help me on this or provide me with examples which will overcome this issue.
I believe the custom layout in this example will help.
https://www.primefaces.org/showcase/ui/input/oneRadio.xhtml
You can try the below code using Map.
Backing bean code
String selectedInsurer;
Map<String,String> insurerIDs = new LinkedHashMap<String,String>();
insurerIDs.put("InsurerID1", "Insurer_Name1"); //(label, value)
insurerIDs.put("InsurerID2", "Insurer_Name2");
insurerIDs.put("InsurerID3", "Insurer_Name3");
insurerIDs.put("InsurerID4", "Insurer_Name4");
//-- getter & setter
At front end
<h:selectOneRadio value="#{quickBill.selectedInsurer}">
<f:selectItems value="#{quickBill.insurerIDs}" />
</h:selectOneRadio>
Some days ago I've changed mojarra to myfaces to solve this problem, now I'm having an strange problem rendering my composite components, they simply aren't rendered the second time I open one popup (the popup is a composite component too).
The first time, as you can see in fieldset, all is rendered ok:
then I click in "CANCELAR" (cancel) button, and the second time, none of my composite components, except the dialog, is rendered:
when I've looked at log, I found these messages:
[#|2012-04-10T15:22:00.681-0300|SEVERE|glassfish3.1.1|org.apache.myfaces.renderkit.html.HtmlCompositeComponentRenderer|_ThreadID=19;_ThreadName=Thread-2;|facet UIComponent.COMPOSITE_FACET_NAME not found when rendering composite component prevenda:popupPreVenda:j_id_2uz|#]
[#|2012-04-10T15:22:00.684-0300|SEVERE|glassfish3.1.1|org.apache.myfaces.renderkit.html.HtmlCompositeComponentRenderer|_ThreadID=19;_ThreadName=Thread-2;|facet UIComponent.COMPOSITE_FACET_NAME not found when rendering composite component prevenda:popupPreVenda:inputRazaoSocial|#]
[#|2012-04-10T15:22:00.685-0300|SEVERE|glassfish3.1.1|org.apache.myfaces.renderkit.html.HtmlCompositeComponentRenderer|_ThreadID=19;_ThreadName=Thread-2;|facet UIComponent.COMPOSITE_FACET_NAME not found when rendering composite component prevenda:popupPreVenda:j_id_2vi|#]
[#|2012-04-10T15:22:00.685-0300|SEVERE|glassfish3.1.1|org.apache.myfaces.renderkit.html.HtmlCompositeComponentRenderer|_ThreadID=19;_ThreadName=Thread-2;|facet UIComponent.COMPOSITE_FACET_NAME not found when rendering composite component prevenda:popupPreVenda:j_id_2vn|#]
[#|2012-04-10T15:22:00.686-0300|SEVERE|glassfish3.1.1|org.apache.myfaces.renderkit.html.HtmlCompositeComponentRenderer|_ThreadID=19;_ThreadName=Thread-2;|facet UIComponent.COMPOSITE_FACET_NAME not found when rendering composite component prevenda:popupPreVenda:j_id_2vs|#]
as you can see, the problem is that myfaces can't find a facet in composite component...
The only composite component that uses facets is hrgi:popup:
<c:interface>
<c:attribute name="titulo" default="sem titulo" required="false"/>
<c:attribute name="renderizar" default="false" required="false"/>
<c:attribute name="modal" default="true" required="false"/>
<c:attribute name="bordaConteudo" default="true" required="false"/>
<c:facet name="cabecalho" required="false"/>
<c:facet name="conteudo" required="true"/>
<c:facet name="botoes" required="true"/>
</c:interface>
<c:implementation>
<h:outputStylesheet library="css" name="hrgiPopup.css" target="head"/>
<h:outputStylesheet library="css" name="clearfix.css" target="head"/>
<h:outputScript library="js" name="hrgiPopup.js" target="head"/>
<h:panelGroup layout="block" rendered="#{cc.attrs.renderizar}"
class="hrgi-dialog-panel clearfix">
<h:panelGroup layout="block" class="hrgi-dialog-overlay clearfix" rendered="#{cc.attrs.modal}"></h:panelGroup>
<h:panelGroup id="popup" layout="block" class="hrgi-dialog-box clearfix">
<h:panelGroup layout="block" class="hrgi-dialog-title clearfix">
<h:outputText style="float:left" value="#{cc.attrs.titulo}"/>
</h:panelGroup>
<h:panelGroup layout="block" class="hrgi-dialog-content clearfix">
<c:renderFacet name="cabecalho" required="false"/>
<h:panelGroup layout="block" class="hrgi-dialog-background clearfix"
rendered="#{cc.attrs.bordaConteudo}">
<c:renderFacet name="conteudo" required="true"/>
</h:panelGroup>
<h:panelGroup layout="block" class="clearfix" rendered="#{not cc.attrs.bordaConteudo}">
<c:renderFacet name="conteudo" required="true"/>
</h:panelGroup>
<c:renderFacet name="botoes" required="true"/>
<script type="text/javascript">
cercarEventoTab("#{cc.clientId}:popup");
</script>
</h:panelGroup>
</h:panelGroup>
</h:panelGroup>
</c:implementation>
Is this a bug of MyFaces?? Mojarra doesn't show any problem like this!
UPDATED
The problem just happens when user clicks "CANCELAR" button... The action call this code to clear the fields and close the dialog:
public void cancelar(ActionEvent evento){
fechar();
UIComponent componente=evento.getComponent().getParent().getParent().getParent();
componente.getFacet("conteudo").getChildren().clear();
}
this code was adapted from the approaches you can see here. In this case, only components inside facet conteudo are recreated. Works fine, except with the my composite components.
The code in MyFaces is ok. The log suggest org.apache.myfaces.renderkit.html.HtmlCompositeComponentRenderer cannot find the c:implementation entry in your composite component file, Since 2.1.6, some changes were done to prevent use '/' characters inside libraryName (see MYFACES-3454 for details). A web config param (org.apache.myfaces.STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME) was added to enable the backward behavior, but note the new behavior is mentioned in a explicit way inside the spec.
If that doesn't work, please try create a simple demo app reproducing the bug and create an issue in MyFaces Issue Tracker. In that way, there are better chances that it could get solved in a future release.
UPDATE
I tried to reproduce it with the information provided without success. The problem is the call to
componente.getFacet("conteudo").getChildren().clear();
That code remove all components inside the facet, and MyFaces is smart enought to remember the components that were removed. When the view is restored, MyFaces algorithm builds the view as in the first request and then remove the components to restore the state correctly. This behavior is expected, so there is no bug in MyFaces code. Instead, I suppose the previous behavior described is a bug in Mojarra, and you should change your code to reset your input components in other way, maybe clearing the values inside the bean, or creating a method expression attribute in your composite component that can be called when cancel operation occur and clear the required input fields. There is a lot of ways to do it.
I don't know why, but after I've created some class to handle exceptions, this problem disappeared...
public class HRGIExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
public HRGIExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return wrapped;
}
#Override
public void handle() throws FacesException {
Iterator i = getUnhandledExceptionQueuedEvents().iterator();
while (i.hasNext()) {
ExceptionQueuedEvent event = (ExceptionQueuedEvent) i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext)event.getSource();
Throwable t = context.getException();
try{
t.printStackTrace();
}finally{
i.remove();
}
}
getWrapped().handle();
}
}
and
public class HRGIExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public HRGIExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getExceptionHandler() {
ExceptionHandler result = new HRGIExceptionHandler(parent.getExceptionHandler());
return result;
}
}
finally I've added this to faces.config:
<factory>
<exception-handler-factory>com.hrgi.web.erp.HRGIExceptionHandlerFactory</exception-handler-factory>
</factory>