update component in another xhtml form from Custom layout p:selectOneRadio not working - jsf-2

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.

Related

#viewScoped data bean survives after navigation to another pages (views) with Spring Web Flow and JSF-2

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.

why action in <h:commandLink> doesn't work inside a composite component

I have a very simple JSF 2/Facelets page that looks like this:
<ui:repeat value="#{myBean.names}" var="_name">
<h:commandLink value="#{_name}" action="#{myBean.sayHello(_name)}">
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</ui:repeat>
The backing bean provides a java.util.List<String> with names and the action-method just prints a "hello <name>" message to standard output.
This works fine. I get a list of names in the browser and a click fires the action-method that says hello to the specified name.
The Problem arises, when I want to put this code in a composite component that does the iteration and renders the actual link via a facet:
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:cc="http://xmlns.jcp.org/jsf/composite">
<cc:interface>
<cc:attribute name="value" type="java.util.List" required="true" />
<cc:facet name="content" />
</cc:interface>
<cc:implementation>
<ui:repeat value="#{cc.attrs.value}" var="_name">
<cc:renderFacet name="content"/>
</ui:repeat>
</cc:implementation>
</ui:component>
I use the composite component like this:
<my:myComp value="#{bean.names}">
<f:facet name="content">
<h:commandLink value="#{_name}" action="#{bean.sayHello(_name)}">
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</f:facet>
</my:myComp>
In the browser I get a list of names that looks exactly like before. But clicking a link now renders a "hello null" message. So _name is resolved correctly in the value attribute of <h:commandLink> but not in the action attribute.
I also tried using actionListener instead of action or the listener attribute from the <f:ajax> tag with no difference.
Could anybody shade some light on this issue?
My environment:
WildFly 8.1 with
JSF 2.2.6 (Mojarra)
The issue has to do with the scope of the variable in this case _name which is evaluated once when the <ui:repeat/> is being processed. In my case, I ran your code and it produced Hello John even though their were other names in my list. To get around this, I introduced a <f:param/> that would contain the value of the _name, and modified your code as follows:
<h:form>
<my:myComp value="#{bean.names}">
<f:facet name="content">
<h:commandLink value="#{_name}" action="#{bean.sayHello()}">
<f:param name="name_" value="#{_name}"/>
<f:ajax execute="#this"/>
</h:commandLink>
<br/>
</f:facet>
</my:myComp>
</h:form>
I also modified the sayHello() method as follows for a #RequestScoped bean:
#ManagedProperty(value = "#{facesContext}")
private FacesContext facesContext;
public void setFacesContext(FacesContext facesContext) {
this.facesContext = facesContext;
}
public void sayHello() {
Map<String, String> params = facesContext.getExternalContext()
.getRequestParameterMap();
String name = params.get("name_");
System.out.println("Hello " + name);
}
You could change this to something shorter in a #ViewScoped bean to:
public void sayHello() {
Map<String, String> params = FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap();
String name = params.get("name_");
System.out.println("Hello " + name);
}
The final result is that it prints out the names correctly.

Getting selected value of a SelectOneMenu

I'm testing the component "SelectOneMenu" on a jsf page. I'm populating this component dinamically though my ManageBean (that will get all Animals from database).
I would like to know if is possible to see the user selected item of that "SelectOneMenu" (combobox), I'm trying with value="#{animalsManage.animalSelect}" but it is only called on the beginning of the page. Also, I'm using an inputText to see the value of the selected intem of the "SelectOneMenu".
What I'm doing wrong?
JSF:
<body>
<ui:component>
<h:form>
<h:outputText value="Select one Mets File" />
<h:selectOneMenu id="combo" value="#{animalsManage.animalSelected}">
<f:selectItem itemLabel="Select..." noSelectionOption="true"/>
<f:selectItems value="#{animalsManage.allAnimals}" />
</h:selectOneMenu>
<h:inputText id="textbox" value="#{animalsManage.animalSelected }" />
</h:form>
</ui:component>
</body>
ManageBean:
#ManagedBean
#ViewScoped
public class AnimalsManage implements Serializable {
#EJB
private AnimalsFacadeREST animalsFacadeREST;
private String animalSelected;
private List< SelectItem> selectAnimals;
public List<SelectItem> getAllAnimals() {
List<Animals> al = animalsFacadeREST.findAll();
selectAnimals = new ArrayList< SelectItem>();
int i = 0;
for (Animals animal: al) {
selectAnimals.add(new SelectItem(i, animal.getName()));
i++;
}
return selectAnimals;
}
public String getAnimalSelected() {
return animalSelected;
}
public void setAnimalSelected(String animalSelected) {
this.animalSelected = animalSelected;
}
}
There are many solutions to the presented problem. I present here two basic ideas.
Server-side solution. Simply attach <f:ajax> tag inside your <h:selectOneMenu> to update selected values and rerender user's choice, like in
<h:selectOneMenu id="combo" value="#{animalsManage.animalSelected}">
<f:selectItem itemLabel="Select..." noSelectionOption="true"/>
<f:selectItems value="#{animalsManage.allAnimals}" />
<f:ajax execute="combo" render="textbox" />
</h:selectOneMenu>
<h:inputText id="textbox" value="#{animalsManage.animalSelected }" />
If you like, you may also do some custom logic with selected element in ajax listener by specifying listener="#{animalsManage.performCustomAjaxLogic}" of <f:ajax> tag.
Client-side solution. Simply update element with id="textbox" on basic change event. So, if you use jQuery the solution will be
$('#combo').change(function() {
$('#textbox').val($('#combo').val());
});
Thought the client-side solution will bind only text value of your input component.

dynamic adding radio and InputText in jsf

i am getting problem in editing in textBox which are generated dynamically.
see my code.
<h:form>
<h:panelGroup>
<h:panelGrid columns="2">
<h:panelGrid columns="1">
<h:selectOneRadio id="radio1" value="#{dynamicBean.radiovalue}" layout="pageDirection" >
<f:selectItems value="#{dynamicBean.objectList}" var="k1" itemValue="#{k1.value}" itemLabel="" />
</h:selectOneRadio>
</h:panelGrid>
<h:panelGrid columns="1" rowClasses="raw1">
<c:forEach items="#{dynamicBean.objectList}" var="k3">
<p:inputText value="#{k3.textvalue}" valueChangeListener="#{dynamicBean.ajaxEvent}" >
</p:inputText>
</c:forEach>
</h:panelGrid>
<h:commandButton value="add new" action="#{dynamicBean.addNew}"/>
</h:panelGrid>
</h:panelGroup>
</h:form>
and this is my bean.
#ManagedBean
#ViewScoped
public class DynamicBean implements Serializable{
private String radiovalue;
private List<Pojo> objectList=new ArrayList<Pojo>();
int i=0;
private Pojo single=new Pojo();
public DynamicBean() {
System.out.println("In Cons");
if(objectList.isEmpty())
{
Pojo p1=new Pojo();
p1.setName("Name-"+i);
p1.setValue("Value-"+i);
p1.setTextvalue("Text-"+i);
objectList.add(p1);
i++;
setRadiovalue(p1.getValue());
}
}
public void addNew()
{
Pojo p1=new Pojo();
p1.setName("Name-"+i);
p1.setValue("Value-"+i);
p1.setTextvalue("Text-"+i);
objectList.add(p1);
i++;
setRadiovalue(p1.getValue());
}
public void ajaxEvent(ValueChangeEvent e)
{
System.out.println("New:"+e.getNewValue());
System.out.print("Old:"+e.getOldValue());
}
following are three variable in Pojo with getter and setter
private String name;
private String value;
private String textvalue;
biggest confusion is i can change first object value on in text box but i cant change the value of new generated objects.
Thanks.
Your concrete problem is caused because you're using JSTL <c:forEach> tag which runs during view build time while you're using a view scoped bean. View scoped beans are stored in the view state. When you submit the form to the server, the view state will be restored, but the original view scoped bean isn't available yet at that moment and thus a new one will be created (thus, with all properties set to default!). After restoring the view, the original view scoped bean will be put back in scope, overriding the temporary one.
You need a fullworthy JSF UI component instead of a JSTL tag. For this particular purpose, you need the <h:dataTable>.
Replace
<h:panelGrid columns="1" rowClasses="raw1">
<c:forEach items="#{dynamicBean.objectList}" var="k3">
<p:inputText value="#{k3.textvalue}" valueChangeListener="#{dynamicBean.ajaxEvent}" >
</p:inputText>
</c:forEach>
</h:panelGrid>
by
<h:dataTable value="#{dynamicBean.objectList}" var="k3" rowClasses="raw1">
<h:column>
<p:inputText value="#{k3.textvalue}" valueChangeListener="#{dynamicBean.ajaxEvent}" />
</h:column>
</h:dataTable>
See also:
JSTL in JSF2 Facelets... makes sense?
Unrelated to the concrete problem, you've some code duplication there in the constructor. Just call addNew() method in there. I'd also remove the instance variable i as that makes no sense. Just use a local one instead which get initialized with List#size().
public DynamicBean() {
addNew();
}
public void addNew() {
Pojo p1 = new Pojo();
int i = objectList.size();
p1.setName("Name-" + i);
p1.setValue("Value-" + i);
p1.setTextvalue("Text-" + i);
objectList.add(p1);
setRadiovalue(p1.getValue());
}

Can not use hidden commandButton with #RequestScoped backing-bean

I have following sample code.
Initially, only commandButton Two is visible. When I click this button, commandButton One is also visible. But when I click One, the backing-bean method click1 does not get fired.
Following is my code:
xhtml
<h:form id="form1">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}" />
</h:form>
<h:form id="form2">
<h:inputHidden id="show" value="#{bean.show1}" />
<h:commandButton id="button2" value="Two" action="#{bean.click2}" />
</h:form>
backing-bean
#RequestScoped
#Named("bean")
public class JsfTrial implements Serializable {
private static final long serialVersionUID = 2784462583813130092L;
private boolean show1; // + getter, setter
public String click1() {
System.out.println("Click1()");
return null;
}
public String click2() {
System.out.println("Click2()");
setShow1(true);
return null;
}
}
I found a very informative answer by BalusC.
h:commandLink / h:commandButton is not being invoked
If I understand it correctly, my problem is due to point 5 of this answer.
Does that also mean we can not use hidden commandButton with #RequestScoped backing-bean?
You can use the request scope, you should only pass the condition as a request parameter to the subsequent requests by <f:param> instead of by a JSF hidden input field <h:inputHidden>. The value of the hidden input field is only set in the model during Update Model Values phase, while the condition of the rendered attribute is already evaluated during Apply Request Values phase which is earlier.
So, use <f:param> instead of <h:inputHidden>:
<h:form id="form1">
<h:commandButton id="button1" value="One" action="#{bean.click1}"
rendered="#{bean.show1}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
<h:form id="form2">
<h:commandButton id="button2" value="Two" action="#{bean.click2}">
<f:param name="show1" value="#{bean.show1}" />
</h:commandButton>
</h:form>
This way you can extract them as request parameter in bean's (post)constructor.
public JsfTrial() {
String show1 = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("show1");
this.show1 = (show1 != null) && Boolean.valueOf(show1);
}
Ugly, but CDI doesn't offer a builtin annotation which substituties JSF's #ManagedProperty("#{param.show1}"). You could however homegrow such an annotation.

Resources