I was playing around with primefaces components, when I stumbled upon this issue...
I have a sample page which contains the following,
<h:form>
<p:panel id="p1">
<p:commandButton value="display" actionListener="#{myTestBean.display}" update="p2"> </p:commandButton>
</p:panel>
<p:panel id="p2" rendered="#{myTestBean.show}">
<p:inputText value="#{myTestBean.val1}" id="t1"></p:inputText>
<p:inputText value="#{myTestBean.val2}" id="t2"></p:inputText>
<p:commandButton value="click me" update="#form" action="#{myTestBean.disply}"></p:commandButton>
</p:panel>
</h:form>
My backing bean contains the following,
public class Testbean implements Serializable {
private String val1;
private String val2;
private boolean show;
//getters and setters for the above...
public void display(){
if(show == false){
setShow(true);
}
}
public void disply(){
System.out.println("I am here");
val1 = "hi";
val2 = "hello";
if(show == false){
setShow(true);
}
}
}
Now, the catch here is, when I click my first button(display) the below panel(p2) gets rendered correctly. But when I click on the second button(click me) the panel gets hidden and also on click of second button it never goes to the backing bean method 'disply()'.
I have used action listener in the second button but again the same behavior. Also, I have given the update = "p2" in the second button, even that didn't yield what I wanted.
Could anyone help me out with this and let me know where and what I am doing wrong?
Thank you.
The bean needs to be placed in the view scope in order to remember all previous (ajax) actions on the same view.
#ManagedBean
#ViewScoped
public class TestBean {
// ...
}
When the bean is placed in the request scope, then the boolean show would be reinitialized to default false and thus the rendered attribute would evaluate false while JSF is looking for the action method to be invoked and hence the action method would be never found and invoked.
See also:
How to choose the right bean scope?
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
When using actionListener="#{myTestBean.display}", the backinbean method should be
public void display(**ActionEvent event**){
if(show == false){
setShow(true);
}
}
and an ActionEvent parameter must be declared.
I surrounded the button as shown below
<h:form>
<h:commandButton styleClass="toggle_btn" action="#{buttonNavBean.show()}" style=" font-family: sans-serif; color: black;" value="Sign In/Sign Up"></h:commandButton>
</h:form>
I faced the same challenge too. I tried to search online but all in vain. But after several trials, I came to figure out that since I was using the commandButton to set the value of the rendered panel, it was not possible to work out of UIForm, Therefore, I surrounded the commandButton with UIForm and it worked correctly as I wanted.
Related
I have a following situation. I am trying to pass a parameter from one bean (e.g. fooBean) to another bean (e.g. barBean) when a page to a page navigation occurrs.
Both beans are ViewScoped.
The fooBean is created when a foo.xhtml page is loaded. From there I pass a parameter "id" to bar.xhtml page.
This is foo.xhtml page:
<h:form>
<p:dataTable var="item" value="#{fooBean.items}" ....>
<p:column ..>
<h:commandLink action="#{fooBean.next}">
<h:outputText value="#{item.name}"/>
<f:setPropertyActionListener value="#{item.id}" target="#{barBean.newId}" />
</h:commandLink>
</p:column>
</p:dataTable>
</h:form>
This is fooBean.next() method:
public String next() { return "bar.xhtml?faces-redirect=true" }
When I click h:commandLink (record or row) from foo.xhtml page, it is redirected to "bar.xhtml".
This is barBean:
#ManagedBean
#ViewScoped
public class BarBean implements ... {
private String newId;
#PostConstruct
public void init() { ... }
// getter and setter for newId
}
I noticed that #PostConstruct init() method of barBean is triggered twice. With first call/trigger, I was expecting that this.newId variable of the barBean would be set to passed value (e.g. id=12345) which it did by checking log statements (setter is called and this.newId = 12345).
Then init() method is triggered again but I thought this.newId would retain the passed value but it becomes null.
Can someone explain to me what is happening?
My xhtml is split in to Menu area (defaultMenu.xhtml) and Content area (defaultContent.xhtml).
The code for defaultMenu.xhtml is:
<h:form id="defaultmenuform">
<p:outputPanel id="menupanel" class="contain auto-fixed-center">
<p:panel id="pmenu" visible="#{phController.user.menuVisible}">
<table id="stutable">
<tr>
<td width="15%">
<p:outputLabel id="stuname" value="#{phController.phBean.studentName}" />
</td>
<td>
<p:tabMenu activeIndex="#{param.selectedtab}">
<p:menuitem value="Home" outcome="phome" icon="ui-icon-star">
<f:param name="selectedtab" value="0" />
</p:menuitem>
<p:menuitem value="Bank" outcome="bhome" icon="ui-icon-person">
<f:param name="selectedtab" value="1" />
</p:menuitem>
</p:tabMenu>
</td>
</tr>
</table>
</p:panel>
</p:outputPanel>
</h:form>
The defaultContent.xhtml actually displays the ph.xhtml content (as part of functional navigation) and the code is:
<ui:define name="content">
<f:event listener="#{phController.readPeople}" type="preRenderView">
</f:event>
<h:form id="form">
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
<p:ajax update=":defaultmenuform:parentmenupanel :defaultmenuform:stuname" listener="#{phController.onChangePerson}"/>
<f:selectItems value="#{phController.selectStudents}" />
</p:selectOneRadio>
<div style="width: 300px; float:left;">
<p:dataGrid var="studentlist" value="#{phController.listStudent}" columns="1" rowIndexVar="stuindex">
<p:panel header="" style="text-align:left">
<h:panelGrid columns="1" style="width:100%">
<h:outputText value="#{studentlist.studentName}" />
<p:radioButton for=":form:selstud" itemIndex="#{stuindex}"/> Select
</h:panelGrid>
</p:panel>
</p:dataGrid>
</div>
</h:form>
</ui:define>
The code for backing bean is:
Map<String, Object> studentparam = new HashMap<>();
studentparam.put("studentSeq", phBean.getSsSeq());
lS = getBaseDAOService().readStudent("readStudent", studentparam);
phBean.setStudentName(lS.get(0).getStudentFirstName() + " " + lS.get(0).getStudentLastName());
As you can see, I am calling the onChangeStu method to display the selected Student Name in defaultMenu.xhtml. I am using Custom Layout p:selectOneRadio in ph.xhtml and onClick trying to update a p:outputLabel in defaultMenu.xhtml.
The backing bean method gets invoked successfully and the value is also set in variable phController.phBean.studentName, but the update is not working. I also checked using view source and the id is “:defaultmenuform:stuname”, I also tried updating the menu panel ":defaultmenuform:menupanel”, but none of this works.
Not sure how to resolve this. Please suggest.
Including the structure of all .xhtmls
<h:body id="entirePageBody">
<div id="page">
<ui:insert name="header" >
<ui:include src="/template/defaultHeader.xhtml" />
</ui:insert>
<ui:insert name="menu" >
<ui:include src="/template/defaultMenu.xhtml" />
</ui:insert>
<div id="content_div" class="auto-fixed-center">
<div id="content_div_padding" class="content-block">
<ui:insert name="content" >
<ui:include src="/template/defaultContent.xhtml" />
<ui:debug hotkey="z" />
</ui:insert>
</div>
</div>
<ui:insert name="footer" >
<ui:include src="/template/defaultFooter.xhtml" />
</ui:insert>
</div>
</h:body>
PhController.java:
public class PhController extends BaseController implements Serializable {
private List<Stud> listStudent;
private List selectStudents;
SelectItem option;
private PhBean phBean;
private Boolean menuVisible;
int counter = 0;
public PhController() {
phBean = new PhBean();
}
public void readPeople() {
listStudent = new ArrayList<Stud>();
listStudent.add(new Stud(1, "John Miller"));
listStudent.add(new Stud(2, "Scott Jackson"));
selectStudents = new ArrayList();
option = new SelectItem(listStudent.get(0).getStudentSeq(), "Select");
selectStudents.add(option);
option = new SelectItem(listStudent.get(1).getStudentSeq(), "Select");
selectStudents.add(option);
phBean.setSsSeq(String.valueOf(1));
phBean.setSelectedName(listStudent.get(0).getStudentName());
menuVisible = true;
}
public void onChangePerson() {
phBean.setSelectedName(listStudent.get(1).getStudentName());
}
// Getters and Setters
}
PhBean.java:
public class PhBean implements Serializable {
private String ssSeq;
private String studName; // Used to display the name in the Menu bar.
private String selectedName;
public PhBean() {
}
// Getters and Setters
}
I'd say that in the p:ajax in defaultContent.xhtml the list of components to be updated should be separated with spaces only, no commas - so try changing this:
update=":defaultmenuform:menupanel, :defaultmenuform:stuname"
to this:
update=":defaultmenuform:menupanel :defaultmenuform:stuname"
UPDATE
I played with this a bit more and may have found a clue - please add the following code to defaultmenuform:
<p:messages autoUpdate="true" showDetail="true" />
This should help us tracking the reason for failed validation (in case failing validation is the root cause for you - as I said, I have rather limited possibility to reproduce this issue).
Anyway, when I selected some item in p:selectOneRadio, an error message like this appeared:
Conversion Error setting value 'test001.Student#4110c95c' for 'null Converter'.
And the root cause was on this row:
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
p:selectOneRadio expects only String to be passed as a value - and ssSeq is very likely of a different type. Try to change the way value is populated to ensure it is always String - maybe a different attribute of the phBean or simply a brand new String one.
NOTE: if this doesn't help, maybe you could update your question with very simplified example of how phController and phBean could look like if we are to test it.
UPDATE #2
So you have explained there is a problem that you want to call phController.readPeople every time the page is loaded/refreshed, but instead it gets loaded with each and every Ajax request, thus overwriting the values.
In your PhController (it is a bean, right? session scoped?) you could add something like this (omitted null checks for the sake of readability):
public void readPeopleOnGet() {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
HttpServletRequest req = (HttpServletRequest) ec.getRequest();
String reqMethod = req.getMethod();
if ("GET".equals(reqMethod)) {
readPeople();
}
}
With the above method you could keep this part of your defaultContext.xhtml in place, provided it is actually called (I assume so), just with the listener method changed:
<f:event listener="#{phController.readPeopleOnGet}" type="preRenderView">
</f:event>
The method readPeopleOnGet will still be called with every request to the page, but since Ajax requests are POST, it will only call readPeople when the page is loaded or refreshed as whole.
This may not be a "perfectly clean" solution, but seemed to work properly when I tested it.
UPDATE #3
Well, since you use PrimeFaces, it would be possible to identify Ajax call also this way:
public void readPeopleOnGet() {
RequestContext rc = RequestContext.getCurrentInstance();
if (!rc.isAjaxRequest()) {
readPeople();
}
}
But if I got your point from latest comments, you want to only run readPeople when the page is loaded for the very first time - so the following part could be even better for that.
You didn't answer if PhController is actually a bean, but I assume it is (there were no annotations visible from the code you posted). You may try making it #SessionScoped:
#ManagedBean
#SessionScoped
public class PhController extends BaseController implements Serializable {
// the rest of the PhController goes here...
Then you could add something like this to the PhController:
#PostConstruct
private void init() {
readPeople();
}
The annotation #PostConstruct ensures the method init is called exactly once after the bean was created. You can continue calling the method readPeople from other places as necessary, while removing both the <f:event ... /> and readPeopleOnGet as these will no longer be needed.
I'm using Spring Web Flow and JSF-2.
The fact is strange and incomprehensible to me.
I have a main page, a typical index.
And I have and actionView with some filters (filtering will be made with an ajax calling) and a table for displaying the results according to filter info.
The back bean for this action is definied as #ViewScoped.
If, at first, I navigate to index after filtering some data and then, later, I return to the action view I don't expect to found the last search displayed, I expect an empty view, but the filters are not empty and data result is filtered.
Why? If I'm defining a #ViewScope is due to I expect the back bean information has to be deleted when I change the view (and index is another view in my case) but I must be making some mistake.
Here is my code:
My parent-flow (simpifying):
<view-state id="index" view="../index.xhtml" redirect="true" popup="true"/>
<view-state id="action1Flow" view="flowRedirect:action1-flow" />
<global-transitions>
<transition on="home" to="index" />
<transition on="action1" to="action1Flow" />
</global-transitions>
action1-flow: (start-state="action1View")
<view-state id="action1View" view="../views/action1View.xhtml" redirect="true" popup="true"/>
action1View.xhtml: (Simplifying, one filter -> table results)
...
<p:panel id="filter" header="Filter example">
<h:panelGrid columns="2">
<h:outputLabel for="dataFilter" value="Filter"/>
<p:inputText id="dataFilter" value="#{action1View.dataValue}"/>
<p:ajax event="keyup" listener="#{action1View.filterData()}" update="table"/>
</p:inputText>
</h:panelGrid>
</p:panel>
<p:panel id="display" header="Data filtered">
<p:dataTable id="table" var="data" value="#{action1View.resultData"
selection="#{action1View.selectedData}" rowKey="#{data.dataValue}">
<p:column headerText="#{msg.id}" sortBy="#{data.dataValue}">
<h:outputText value="#{data.dataValue}" />
</p:column>
</p:dataTable>
</p:panel>
...
<p:commandButton value="Go to index" action="home"/>
...
and action1View.java, the back bean:
#ManagedBean
#ViewScoped
#Component("action1View")
public class Action1View implements Serializable {
static final long serialVersionUID = 42L;
List<ExampleBean> resultData = null;
ExampleBean selectedData = null;
Integer dataValue;
public ExampleBean getSelectedData() {
return selectedData;
}
public void setSelectedData(ExampleBean selectedData) {
this.selectedData = selectedData;
}
public Integer getDataValue() {
return dataValue;
}
public void setDataValue(Integer dataValue) {
this.dataValue = dataValue;
}
public void filterData() {
// Some logic
resultData = xxxxx;
}
}
index.xhtml:
...
<p:commandButton value="Go to index" action="action1"/>
...
Sorry for my english and ...
Greetings!
I have found the GAP!
My bean, apart from #ViewScoped annotation, is also defining (as you can see in the code) with this annotation: #Component("action1View") without apparent reason.
The fact is by removing this annotation I got everything to work properly.
I think that is because the Component behaviur overlays ViewScoped one doing that only one bean for each defined class can be created, and, becauso of this, information stays in time (until the session ends or the application was closed).
But it would be great if someone give more and richer information about it.
Here is the situation:
I have a component, whose value is a property of my ViewScoped bean, which gets a FacesMessage generated for it do to some error when the form is first submitted
On a subsequent user action, a h:commandlink is clicked, which uses f:ajax, and its actionlistener sets the component to no longer be rendered
Upon receiving the reponse, the component is no longer rendered (as expected), but the FacesMessage for it still remains and is displayed
I am using Mojarra JSF 2.1, and IceFaces 3.0 - although this example and the problematic behavior are seen using only standard JSF components. Here is a small example that demonstrates the behavior:
test.xhtml
<h:form id="testform">
<h:message for="tv"/>
<h:inputText id="tv" value="#{testBean.testVal}" rendered="#{testBean.testValRendered}" required="true" />
<h:commandButton value="Submit"/>
<h:commandLink actionListener="#{testBean.toggleTestVal}" value="Toggle input">
<f:ajax execute="#this" render="#form"/>
</h:commandLink>
</h:form>
Bean code
#ViewScoped
#ManagedBean(name = "testBean")
public class TestBean {
private String testVal;
private boolean testValRendered;
public TestBean() {
testValRendered = true;
}
public String getTestVal() {
return testVal;
}
public void setTestVal(String testVal) {
this.testVal = testVal;
}
public boolean isTestValRendered() {
return testValRendered;
}
public void setTestValRendered(boolean testValRendered) {
this.testValRendered = testValRendered;
}
public void toggleTestVal(ActionEvent ae) {
testValRendered = !testValRendered;
}
}
If I change the h:commandLink so that it does not use f:ajax, and set immediate='true' on it instead, then the message is removed properly. While this may work in some cases, I would like to understand why using f:ajax to render things as I have in my example does not work. Thanks.
The input component is submitted, processed and validated before its rendering condition has been toggled. The <h:message> isn't aware that the associated input component is not rendered. You'd like to check the very same rendering condition on the <h:message> as well:
<h:message for="tv" rendered="#{testBean.testValRendered}" />
<h:inputText id="tv" ... rendered="#{testBean.testValRendered}" />
I have a loop with a bunch of h:selectBooleanCheckboxs inside a ui:repeat and I want to be able to check / uncheck all of them with one click. Following this this is how I try to do it:
<h:selectBooleanCheckbox value="#{bean.selectAll}" valueChangeListener="#{bean.selectAllCustomers}">
<f:ajax execute="#form" render="entireLoop"/>
</h:selectBooleanCheckbox>
The loop:
<h:panelGroup id="entireLoop">
<ui:repeat var="customer" varStatus="status" value="#{bean.customers}">
<tr>
<td>
<h:selectBooleanCheckbox value="#{bean.customersMap[customer]}"/>
</td>
<td><h:commandLink value="#{customer.userID}" action="#{navBean.gotoEditCustomer(customer)}"/></td>
<td>#{bean.getContact(user)}</td>
<td>#{customer.companyName}</td>
<td>#{customer.email}</td>
<td>#{customer.phone}</td>
</tr>
</ui:repeat>
</h:panelGroup>
The code in the bean:
public void selectAllCustomers() {
Iterator<User> keys = customersMap.keySet().iterator();
while(keys.hasNext()) {
User user = keys.next();
customersMap.put(user, selectAll);
}
}
When I click to check the "select all" checkbox, I have two problems:
Nothing happens. Only when I click on somewhere else in the page the method is called. How can I get it to go directly?
The entire loop disappears from the page. Why? (I tried to get the ajax to render only the selectBooleanCheckbox by giving it an id, or a name, but I'm getting an error saying this is not a naming component.)
Instead of using valueChangeListener, your method will work if you use the listener attribute of the <f:ajax> tag. It would be something like this:
<h:selectBooleanCheckbox value="#{bean.selectAll}">
<f:ajax listener="#{bean.selectAllCustomers}" render="entireLoop"/>
</h:selectBooleanCheckbox>
Besides, you don't need to submit the whole form with execute="#form". You only need to submit selectAll property and re-render the entireLoop.
If you really want to use valueChangeListener, you must note that your method will be triggered before the new value is applied in the selectAll property. As a result, your entireLoop will not be updated until the next request. To use valueChangeListener, your listener method should look like this:
public void selectAllCustomers(ValueChangeEvent e) {
boolean newSelectAll = (Boolean) e.getNewValue();
Iterator<Users> keys = customersMap.keySet().iterator();
while(keys.hasNext()) {
Users user = keys.next();
customersMap.put(user, newSelectAll);
}
}