I use the google map tool from primefaces.
I want my user to be able to place just one marker on a map.
The values of the coordinates should be stored in a managed bean variables.
How can I do that?
See what I did so far:
I created the map:
<f:view contentType="text/html">
<p:gmap id="gmap" center="36.890257,30.707417" zoom="13" type="HYBRID"
style="width:600px;height:400px"
model="#{mapBean.emptyModel}"
onPointClick="handlePointClick(event);"
widgetVar="map" /> </f:view>
<p:dialog widgetVar="dlg" effect="FADE" effectDuration="0.5" close="false" fixedCenter="true">
<h:form prependId="false">
<h:panelGrid columns="2">
<h:outputLabel for="title" value="Title:" />
<p:inputText id="title" value="#{mapBean.title}" />
<f:facet name="footer">
<p:commandButton value="Add"
actionListener="#{mapBean.addMarker}"
update="messages"
oncomplete="markerAddComplete()"/>
<p:commandButton value="Cancel" onclick="return cancel()"/>
</f:facet>
</h:panelGrid>
<h:inputHidden id="lat" value="#{newOfferSupportController.mapLocationX}" />
<h:inputHidden id="lng" value="#{newOfferSupportController.mapLocationY}" />
</h:form>
</p:dialog>
<script type="text/javascript">
var currentMarker = null;
function handlePointClick(event) {
if(currentMarker == null) {
document.getElementById('lat').value = event.latLng.lat();
document.getElementById('lng').value = event.latLng.lng();
currentMarker = new google.maps.Marker({
position:new google.maps.LatLng(event.latLng.lat(), event.latLng.lng())
});
map.addOverlay(currentMarker);
dlg.show();
}
}
function markerAddComplete() {
var title = document.getElementById('title');
currentMarker.setTitle(title.value);
title.value = "";
currentMarker = null;
dlg.hide();
}
function cancel() {
dlg.hide();
currentMarker.setMap(null);
currentMarker = null;
return false;
}
</script>
I also greated the variables that will hold the coordinates:
#ManagedBean
#RequestScoped
public class NewOfferSupportController {
private float mapLocationX;
private float mapLocationY;
//Get & set methods
It all works as in the primefaces page but I have 2 problems:
Problem 1: Once the marker is placed, it cannot be placed again.
Problem 2: In the same form where the map is there are some other elements such as text fields. I noticed that validation does not occur when I click on the submit button located in the form where the map is, Actually the form does not get submitted at all(This didn't occur before i added the map), why is the map disrupting the validation?
Problem 1: Once the marker is placed, it cannot be placed again.
This problem is caused by the following things:
You've bound latitude and longitude to a different bean (NewOfferSupportController) than the bean which holds the map model (MapBean). You should be using MapBean example in the PrimeFaces showcase as design starting point for NewOfferSupportController bean. It namely stores the markers set. The hidden inputs must point to that bean because in the addMarker() method those values will be added. From the showcase example you just have to rename the MapBean class name and rename #{mapBean} references in the view by #{newOfferSupportController}.
Your NewOfferSupportController bean is request scoped while it should be view scoped.
#ManagedBean
#ViewScoped
public class NewOfferSupportController implements Serializable {}
View scoped beans live as long as you're interacting with the same form by Ajax. But request scoped beans get recreated on every request (and thus also on every Ajax request!), hereby trashing the markers which are placed before in the map and inputs which are entered before adding markers.
Problem 2: In the same form where the map is there are some other elements such as text fields. Actually the form does not get submitted at all(This didn't occur before i added the map), why is the map disrupting the validation?
This works for me. This is likely caused because your NewOfferSupportController is been placed in the request scope instead of the view scope.
To recap, here is the code which I used in my test:
view:
<p:growl id="messages" showDetail="true" />
<h:form>
<p:gmap id="gmap" center="36.890257,30.707417" zoom="13" type="HYBRID" style="width:600px;height:400px"
model="#{mapBean.mapModel}" onPointClick="handlePointClick(event);" widgetVar="map" />
<h:inputText value="#{mapBean.input}" required="true" />
<p:commandButton value="submit" action="#{mapBean.submit}" update="messages" />
</h:form>
<p:dialog widgetVar="dlg" effect="FADE" effectDuration="0.5" close="false" fixedCenter="true">
<h:form prependId="false">
<h:panelGrid columns="2">
<h:outputLabel for="title" value="Title:" />
<p:inputText id="title" value="#{mapBean.title}" />
<f:facet name="footer">
<p:commandButton value="Add" actionListener="#{mapBean.addMarker}"
update="messages" oncomplete="markerAddComplete()"/>
<p:commandButton value="Cancel" onclick="return cancel()"/>
</f:facet>
</h:panelGrid>
<h:inputHidden id="lat" value="#{mapBean.lat}" />
<h:inputHidden id="lng" value="#{mapBean.lng}" />
</h:form>
</p:dialog>
(I didn't change the <script> code in the showcase example, just add it unchanged)
Bean:
#ManagedBean
#ViewScoped
public class MapBean implements Serializable {
private MapModel mapModel;
private String title;
private double lat;
private double lng;
private String input;
public MapBean() {
mapModel = new DefaultMapModel();
}
public void addMarker(ActionEvent actionEvent) {
mapModel.addOverlay(new Marker(new LatLng(lat, lng), title));
addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO, "Marker Added", "Lat:" + lat + ", Lng:" + lng));
}
public void submit() {
addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO, "Form submitted", "Amount markers: " + mapModel.getMarkers().size() + ", Input: " + input));
}
public void addMessage(FacesMessage message) {
FacesContext.getCurrentInstance().addMessage(null, message);
}
// Getters+setters.
}
Related
I am developing a system that the user choose some checkboxes and each checkbox have a currency value, and I want to change a outputText according to selected checkboxes.
Example: I have three checkboxes, each one have $10.00. If the user choose just two checkboxes, the outputText must show $20.00. If the user choose the last one, the outputText must show $30.00. And all this without reloading the page.
My code so far:
<h:form id="form">
<p:fieldset legend="Tipo base do site:" style="margin-bottom:20px">
<h:panelGrid columns="2" cellpadding="5">
<p:dataList value="#{tiposIndexView.tipos}" var="tipo"
type="ordered" >
R$
<h:outputText value="#{tipo.preco}" >
<f:convertNumber pattern="#0.00" />
</h:outputText>
</p:dataList>
<p:selectOneRadio id="tipo" value="#{tiposIndexView.tipos}"
layout="grid" columns="1">
<f:selectItems value="#{tiposIndexView.tipos}" var="tipo"
itemLabel="#{tipo.nome}" itemValue="#{tipo.nome}" />
</p:selectOneRadio>
</h:panelGrid>
</p:fieldset>
<p:fieldset legend="Sistemas adicionais:" style="margin-bottom:20px">
<h:panelGrid columns="2" cellpadding="5">
<p:dataList value="#{sistemasIndexView.sistemas}" var="sistema"
type="ordered" >
R$
<h:outputText value="#{sistema.preco}" >
<f:convertNumber pattern="#0.00" />
</h:outputText>
</p:dataList>
<p:selectManyCheckbox style="margin-top:0px;!important" id="grid" value="#{sistemasIndexView.sistemas}" layout="grid" columns="1">
<f:selectItems value="#{sistemasIndexView.sistemas}" var="sistema" itemLabel="#{sistema.nome}" itemValue="#{sistema}" />
</p:selectManyCheckbox>
</h:panelGrid>
</p:fieldset>
<p:fieldset legend="Valor total:">
<h:outputText>
R$
<f:convertNumber pattern="#0.00" />
</h:outputText>
</p:fieldset>
</h:form>
The last outputText is where I want to show the total value.
And one more question, every time that I open my site, all checkboxes starts checked and I want to all start unchecked. The code of checkboxes:
#ManagedBean
#ViewScoped
public class SistemasIndexView implements Serializable{
private static final long serialVersionUID = -2697991732915561009L;
private List<Sistema> sistemas;
#PostConstruct
public void init(){
sistemas = new ArrayList<>();
sistemas.add(new Sistema("Teste", 200));
sistemas.add(new Sistema("Exemplo", 300));
sistemas.add(new Sistema("gsdfaf", 50));
}
public List<Sistema> getSistemas() {
return sistemas;
}
public void setSistemas(List<Sistema> sistemas) {
this.sistemas = sistemas;
}
}
You need 2 lists, one for storing all checkboxes and second for selected ones. That's why you have all checkboxes selected because your selected values are the same as all possible.
Css mistake: style="margin-top:0px;!important" should be style="margin-top:0px !important;"
Things you need in your backing bean (with getters/setters):
List for selected items private List<Sistema> selectedSistemas;
Field for calculated sum private int sum
Method for calculating output public void calculateSum()
Code
<p:selectManyCheckbox style="margin-top:0px !important;" id="grid" value="#{sistemasIndexView.selectedSistemas}" layout="grid" columns="1">
<f:selectItems value="#{sistemasIndexView.sistemas}" var="sistema" itemLabel="#{sistema.nome}" itemValue="#{sistema}" />
<p:ajax process="#this" listener="#{test.calculateSum}" update="sum"/>
</p:selectManyCheckbox>
<h:outputText id="sum" value="#{test.sum}">
<f:convertNumber pattern="#0.00" />
</h:outputText>
And calculating method, you can change it but from what you have written it does the job
public void calculateSum() {
sum = selectedSistemas.size() * 10;
}
I tried apply rendered in order to check for if else conditional in JSF. (ref: Conditionally displaying JSF components)
This part is my JSF index.html
<p:commandButton value="Update Hidden Label" action="#{carForm.updateBool}" />
<h:outputText value="Text A" />
<h:outputText value=" Text B" rendered="#{carForm.booleanValue}" />
This is my java class
private boolean booleanValue;
public boolean isBooleanValue() {
return booleanValue;
}
public void setBooleanValue(final boolean booleanValue) {
this.booleanValue = booleanValue;
}
public void updateBool() {
booleanValue = true;
}
when I tried click on "Update Hidden Label", it would update the booleanValue in java class to true, however in index.html page "Text B" is still not appear yet.
Also you need to update page fragment with <h:outputText value=" Text B" rendered="#{carForm.booleanValue}" />
You can use <p:panel id="textPanel"></p:panel> and put your code there.
And add update parameter to p:commandButton with value textPanel, like this update="textPanel".
<p:panel id="textPanel">
<p:commandButton value="Update Hidden Label" action="#{carForm.updateBool}" update="textPanel" />
<h:outputText value="Text A" />
<h:outputText value=" Text B" rendered="#{carForm.booleanValue}" />
</p:panel>
i have a p:selectOneMenu filled with objects from database.When page is looaded first,
the default item of selectOneMenu must be "please select one" in string type( other items of selectOneMenu are in object type.) When page is loaded first, one data in object type from database is visible as default.I don't want this.how to set default item like "Please select one" in p:selectOneMenu with object from database?
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
template="layout.xhtml">
<ui:define name="pageContent">
<h:form id="silYapilandirmaForm" prependId="true">
<p:panel id="silYapilandirmaPanel" header="#{etiketler.silYapilandirma}" collapsed="true" toggleable="true" >
<p:tabView id="tabView" dynamic="false" activeIndex="0">
<p:tab title="#{etiketler.silIslemleri}" id="silIslemleriTab">
<p:outputPanel id="silIslemleriPanel">
<h:panelGrid id="eshsSecPanelGrid" columns="3" >
<h:outputText value="#{etiketler.eshsSertifika}:*" />
<p:selectOneMenu id="silYapilandirmaSelect" value="#{silYapilandirmaView.seciliEshsSertifika}" effect="fade" effectDuration="100" style="width: 205px" converter="#{entityConverter}">
<f:selectItem itemLabel="Please select one" itemValue="#{null}"/>
<f:selectItems value="#{silYapilandirmaView.eshsSertifikaList}" var="eshsSertifika"
itemLabel="#{eshsSertifika.sertifikaKodAdi}" itemValue="#{eshsSertifika}" />
<p:ajax process="#this" event="change" update="eshsSecPanelGrid,silIslemleriPanel" listener="#{silYapilandirmaView.addListener()}" />
</p:selectOneMenu>
<h:outputText value=""/>
<h:outputText value="#{etiketler.eshsSertifika}:" />
<h:inputText value="#{silYapilandirmaView.seciliEshsSertifika.sertifikaKodAdi}" disabled="true" size="30"/>
<h:outputText value=""/>
<h:outputText value="#{etiketler.silUretimBaslangicSaati}:* " />
<p:inputMask id="silUretimBaslangicSaatiInputMask" value="#{silYapilandirmaView.seciliEshsSertifika.silUretimBaslangicSaati}" mask="99:99:99" size="30" />
<p:message for="silUretimBaslangicSaatiInputMask" display="text" />
<h:outputText value="#{etiketler.silUretimBaslangicPeriyodu}:* " />
<p:inputMask maxlength="4" mask="99?99" id="periyotInputMask" size="30" value="#{silYapilandirmaView.seciliEshsSertifika.silUretimPeriyodu}" >
</p:inputMask>
<p:message for="periyotInputMask" display="text" />
</h:panelGrid>
</p:outputPanel>
</p:tab>
</p:tabView>
</p:panel>
</h:form>
</ui:define>
</ui:composition>
i have addListener method in p:ajax in p:selectOneMenu.This method is working in this way;
you are selecting one object from selectOneMenu and this object is put in the last index of the list filling selectOneMenu ,the object in the last index of the list is put in the index of selected object.I mean you are selecting an object and the index of this object in the list is 2.In total there are five objects in the list ,let we say.selected object whose index is 2 is put in index 4 of the list.The object in index 4 of the list is put in index 2 of the list.I am sorry for my english.The aim of this addListener method,selectOneMenu displays the last item of the list as selected always.Whichever object you select,IT DISPLAYS THE LAST ITEM OF THE LIST.Therefore due to this method , the object i selected is put in the last of the list and it displays as selected to user.The method works fine.
But when page is loaded first , the last item of the list is shown as selected in p:selectOneMenu ,i want "please select" to be shown as default."
My bean class is SilYapilandirmaView.java
#ManagedBean
#ViewScoped
public class SilYapilandirmaView extends BaseView implements Serializable {
#ManagedProperty("#{commonService}")
private CommonService commonService;
private EshsSertifika seciliEshsSertifika;
private List<EshsSertifika> eshsSertifikaList;
#PostConstruct
public void init() {
seciliEshsSertifika=new EshsSertifika();
eshsSertifikaList = (List) commonService.hepsiniGetir(EshsSertifika.class);
seciliEshsSertifika = eshsSertifikaList.get(eshsSertifikaList.size() - 1);
}
public CommonService getCommonService() {
return commonService;
}
public void setCommonService(CommonService commonService) {
this.commonService = commonService;
}
public void addListener() {
if(seciliEshsSertifika ==null)
{
return;
}
int i = 0;
for (EshsSertifika eshsSert : eshsSertifikaList) {
if (seciliEshsSertifika.getId() == eshsSert.getId()) {
eshsSert = eshsSertifikaList.get(eshsSertifikaList.size() - 1);
eshsSertifikaList.set(i, eshsSert);
eshsSertifikaList.set(eshsSertifikaList.size() - 1, seciliEshsSertifika);
break;
}
i++;
}
}
public EshsSertifika getSeciliEshsSertifika() {
return seciliEshsSertifika;
}
public void setSeciliEshsSertifika(EshsSertifika seciliEshsSertifika) {
this.seciliEshsSertifika = seciliEshsSertifika;
}
public List<EshsSertifika> getEshsSertifikaList() {
return eshsSertifikaList;
}
public void setEshsSertifikaList(List<EshsSertifika> EshsSertifikaList) {
this.eshsSertifikaList = EshsSertifikaList;
}
}
When i added following code in init() method ,my code is working perfectly!
seciliEshsSertifika=null;
public void init() {
seciliEshsSertifika=new EshsSertifika();
eshsSertifikaList = (List) commonService.hepsiniGetir(EshsSertifika.class);
seciliEshsSertifika = eshsSertifikaList.get(eshsSertifikaList.size() - 1);
seciliEshsSertifika=null;
}
when page is loaded, "please select one" is visible in p:selectOneMenu,but i don't want to make "seciliEshsSertifika" object equal to null.,is there anybody else coming up with another solution?
Try this. get the object you want to set the first item. (this example i get the first object in the list)
bean.java
EshsSertifika firstItem = eshsSertifikaList.get(0);
xhtml
<f:selectItem itemLabel="#{eshsSertifika.firstItem.sertifikaKodAdi}" itemValue="#{eshsSertifika.firstItem}" />
Would really appreciate some guidance concerning patterns for a Web App using JSF 2.0, PrimeFaces and Ajax. Our current system uses JSPs with standard submits and we have 1 JSP for each functional page in our app. Each JSP calls an action class for logic, navigation and EJB calls. Migrating to JSF using the same set up would be relatively simple i.e. 1 xhtml page and associated backing bean and navigation/logic done via a do method. However we want to submit via Ajax and this causes puzzles in my head. If I load abc1.xhtml and submit with ajax then I stay on abc1.xhtml although I might need to go to abc2.xhtml. I thought about having the associated forms on 1 xhtml page and using the rendered property to decide what to display. This works but I'm not comfortable having lots of forms in 1 page. Ideally I want to keep each page separate but don't know how this is possible with Ajax. Any ideas would be much appreciated.
Edit - This was original Solution but now has been refined in my answer below. This works but there seems to be issues with the Params. When I click on the AAA or BBB link I need to pass a param so that the ViewController bean knows what was clicked and where to set the Destination page. However if I click the Submit button in AAA.xhtml the content doesn't change unless I also add <f:param name="tranID" value="AAA"/> to the command button. I'd thought I'd handled a null Param in the ViewController constructor but obviously I'm missing something important. My only thoughts were to do with the URL. When I click the menu link it adds the param on to the url ?tranID=AAA. If I then don't add the param onto the subsequent submit is this effectively changing the url and causing some sort of mismatch?
viewController.xhtml
<h:body>
<h:panelGroup layout="block" id="transactionControl">
<h4>
<h:outputLink id="mfa" value="#{facesContext.externalContext.requestContextPath}/xhtml/viewController.xhtml" styleClass="menuLink">
<h:outputText value="AAA"></h:outputText>
<f:param name="tranID" value="AAA"/>
</h:outputLink>
</h4>
<h4>
<h:outputLink id="inq" value="#{facesContext.externalContext.requestContextPath}/xhtml/viewController.xhtml" styleClass="menuLink">
<h:outputText value="BBB"></h:outputText>
<f:param name="tranID" value="BBB"/>
</h:outputLink>
</h4>
</h:panelGroup>
<h:panelGroup layout="block" id="content" style="border-style: solid;">
<ui:include src="#{viewController.destinationPage}.xhtml"></ui:include>
</h:panelGroup>
</h:body>
AAA.xhtml
<h:body>
<h:form prependId="false">
<h:outputText value="Click the button to go to AAB"></h:outputText>
<p>
<p:commandButton id="submitButton" value="Go" ajax="true" actionListener="#{viewController.doAAAtoAAB}"
process="#form"
update="content">
<f:param name="tranID" value="AAA"/>
</p:commandButton>
</p>
</h:form>
</h:body>
AAB.xhtml
<h:body>
<h:panelGroup layout="block" id="subContent">
<h:outputText value="This is the AAB content"></h:outputText>
</h:panelGroup>
</h:body>
BBB.xhtml and BBC.xhtml as above
ViewController bean
package com.mcpplc.supportclient.webapp.managedBeans;
import javax.faces.context.FacesContext;
import java.io.Serializable;
#ManagedBean
#ViewScoped
public class ViewController implements Serializable
{
String destinationPage = "splash";
FacesContext context;
String callingTranID;
public ViewController ()
{
context = FacesContext.getCurrentInstance();
callingTranID = context.getExternalContext().getRequestParameterMap().get("tranID");
if (callingTranID != null )
{
destinationPage = callingTranID;
}
}
public void doAAAtoAAB()
{
destinationPage = "AAB";
}
public void doBBBtoBBC()
{
destinationPage = "BBC";
}
public String getDestinationPage()
{
return destinationPage;
}
public void setDestinationPage( String destinationPage )
{
this.destinationPage = destinationPage;
}
}
My recomendation for you is, do not use ajax unless you need it. If you need to actually call another page after a request, what is the point in using ajax?
Try to think in the application workflow and apply ajax where it is necessary. The cases you think that ajax will be confusing, is because you probably should not be using it
Cheers
Decided to answer my own question as I have now have a full working ajax only prototype app. This fits my initial requirements of having each view saved as one .xhtml page and each view having it's own backing bean. Instead of further polluting my question here is the simplified code. (Thanks to BalusC for his previous answer that really helped although its now disappeared).
viewController.xhtml
<h:body>
<h:panelGroup layout="block" id="transactionControl">
<h:form>
<h4>
<p:commandLink id="mfa" ajax="true" process="#form" update="content transactionBannerContent" styleClass="menuLink" oncomplete="showTransaction01Screens();">
<h:outputText value="MFA"></h:outputText>
<f:setPropertyActionListener target="#{viewController.destinationPage}" value="MFA01"/>
</p:commandLink>
</h4>
<h4>
<p:commandLink id="inq" ajax="true" process="#form" update="content transactionBannerContent" styleClass="menuLink" oncomplete="showTransaction01Screens();">
<h:outputText value="INQ"></h:outputText>
<f:setPropertyActionListener target="#{viewController.destinationPage}" value="INQ01"/>
</p:commandLink>
</h4>
</h:form>
</h:panelGroup>
<h:panelGroup layout="block" id="content" style="border-style: solid;">
<ui:include src="#{viewController.destinationPage}.xhtml"></ui:include>
</h:panelGroup>
</h:body>
mfa01.xhtml
<h:panelGroup layout="block" id="mfa01">
<h:form id="mfa01Form">
<p>
<span>
<h:outputLabel value="UCN" for="ucn"/>
<h:inputText id="ucn" value="#{mfa01BackingBean.mfa01FormVO.ucn}" size="20"/>
</span>
</p>
<p class="submitButton">
<p:commandButton id="submitButton" value="Go" ajax="true" actionListener="#{mfa01BackingBean.doMFA01}" process="#form"
update="content transactionBannerContent" oncomplete="ajaxFinished('MFA01')">
</p:commandButton>
</p>
</h:form>
</h:panelGroup>
mfa02.xhtml
<h:panelGroup layout="block" id="mfa02">
<h:form id="mfa02Form" prependId="true">
<p>
<span style="width:25%">
<h:outputLabel value="Vessel Name"/>
<h:outputText id="vesselName"
value="#{mfa02BackingBean.mfa02FormVO.vesselName}"/>
</span>
<span style="width:75%">
<h:outputLabel value="ETA"/>
<h:outputText id="eta"
value="#{mfa02BackingBean.mfa02FormVO.eta}"/>
</span>
</p>
<p>
<span>
<h:outputLabel value="Unit ID" for="unitID"/>
<h:inputText id="unitID"
value="#{mfa02BackingBean.mfa02FormVO.unitID}"
size="20"
required="true" validator="#{mfa02BackingBean.validateData}"/>
</span>
</p>
<p class="submitButton">
<p:commandButton id="submitButton" value="Go" ajax="true" action="#{mfa02BackingBean.doMFA02}"
process="#form" update="content transactionBannerContent" oncomplete="ajaxFinished('MFA02')">
<f:param name="randomString" value="AAA"/>
</p:commandButton>
</p>
</h:form>
</h:panelGroup>
ViewController.java
package com.mcpplc.supportclient.webapp.managedBeans;
import javax.faces.bean.*;
import java.io.Serializable;
#ManagedBean (name = "viewController")
#SessionScoped
public class ViewController implements Serializable
{
String destinationPage = "splash";
String transactionID;
public String getDestinationPage()
{
return destinationPage;
}
public void setDestinationPage( String destinationPage )
{
this.destinationPage = destinationPage;
transactionID = destinationPage.toUpperCase();
}
public String getTransactionID()
{
return transactionID;
}
public void setTransactionID( String transactionID )
{
this.transactionID = transactionID;
}
}
mfa01BackingBean.java
package com.mcpplc.supportclient.webapp.managedBeans;
import com.mcpplc.supportclient.webapp.Utils.JSFUtils;
import com.mcpplc.supportclient.webapp.valueObjects.*;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class Mfa01BackingBean
{
#ManagedProperty(value = "#{viewController}")
private ViewController viewController;
private ImportConsignmentVO importConsignmentVO;
private VoyageVO voyageVO;
private Mfa01FormVO mfa01FormVO;
private Mfa02FormVO mfa02FormVO;
#PostConstruct
public void init()
{
mfa01FormVO = new Mfa01FormVO();
}
public void doMFA01()
{
//pretend to get VOs
importConsignmentVO = new ImportConsignmentVO();
voyageVO = new VoyageVO();
//set some VO stuff
if ( mfa01FormVO.getUcn().equalsIgnoreCase( "123" ) )
{
importConsignmentVO.setUnitID("AJF1");
voyageVO.setVesselName("Ever Glade");
}
else {
importConsignmentVO.setUnitID("ZZZ1");
voyageVO.setVesselName("Ever Champion");
}
importConsignmentVO.setType("41G1");
importConsignmentVO.setWeight("25000");
voyageVO.setEta("01/01/2011");
constructMFA02Form();
viewController.setDestinationPage("mfa02");
}
private void constructMFA02Form()
{
mfa02FormVO = new Mfa02FormVO();
mfa02FormVO.setUnitID(importConsignmentVO.getUnitID());
mfa02FormVO.setType(importConsignmentVO.getType());
mfa02FormVO.setWeight(importConsignmentVO.getWeight());
mfa02FormVO.setMfaRef("12345");
mfa02FormVO.setVesselName(voyageVO.getVesselName());
mfa02FormVO.setEta(voyageVO.getEta());
JSFUtils.addObjectToRequest(Mfa02FormVO.class.getName(), mfa02FormVO);
}
.....getters&setters
}
mfa02BackingBean.java
package com.mcpplc.supportclient.webapp.managedBeans;
import com.mcpplc.supportclient.webapp.Utils.JSFUtils;
import com.mcpplc.supportclient.webapp.valueObjects.*;
import java.io.Serializable;
import java.util.*;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class Mfa02BackingBean implements Serializable
{
#ManagedProperty(value = "#{viewController}")
private ViewController viewController;
private Mfa02FormVO mfa02FormVO;
#PostConstruct
public void init()
{
mfa02FormVO = (Mfa02FormVO) JSFUtils.getObjectFromRequest(Mfa02FormVO.class.getName());
}
public String doMFA02()
{
viewController.setDestinationPage("mfa01");
return "viewController";
}
public void validateData ( FacesContext facesContext, UIComponent uiComponentToValidate, Object value)
{
String message = "";
String requestData = (String) value;
if ( !requestData.contains( "0" ) )
{
((UIInput )uiComponentToValidate).setValid( false );
message = "Incorrect format for Unit ID";
facesContext.addMessage( uiComponentToValidate.getClientId(facesContext), new FacesMessage(message) );
//JSFUtils.addObjectToRequest(Mfa02FormVO.class.getName(), mfa02FormVO);
}
}
.....getters&setters
}
Mfa01FormVO & Mfa02FormVO are just value objects with getters & setters
The main issue I encountered was saving object data between requests. When I go from mfa01 to mfa02 I do a lookup populate some vos and then pass a form vo into the Flash. Mfa02 screen is then constructed with the vo values. The big issue I had was when there was a validation error on mfa02. Initially I had the bean set to requestScoped however I lost the vo object after the validation failed. This was despite me adding the object back into the flash as soon as I got it from the Flash. The strange thing about this though was there was no guarantee when I would lose the object. If I clicked again sometimes I would lose the object straight away but sometimes it would remain for 1 more click and then i'd lose it.
I then changed mfa02backingbean to ViewScoped. However because it's completely ajax mfa02backingbean was not getting reinitialised on subsequent requests. This meant that whatever I set the values to on the first request would then be displayed forever more. I finally resolved this using ViewScoped and changing the Command Button to use an Action rather than ActionListener and in that method I return "viewController" String. I assume the lifecycle thinks I'm returning to a new view so it clears out anything left in the viewscope.
I hope this code helps anyone else looking for a similar solution.
I have two selectOneMenu. I need following:
On first menu change I need to populate second one and some form's fields with AJAX
On second menu change I need to populate only some form's fields wit AJAX.
Populating second menu and form's fields on step 1 is perfect and right after first menu change, but there is a problem with populating form on step 2.
Nothing happens when I change second menu's value. But when I returned second menu value to NoSelectionOption ajax listener invoked. It seems that AJAX listener ignores values that was added with AJAX.
Here's my code fragments:
JSF:
<h:panelGrid>
<h:panelGrid columns="1">
<h:panelGrid columns="1">
<h:panelGrid columns="2">
<h:outputText value="Получатель:" />
<h:selectOneMenu value="#{paymentOrder.curContractor}">
<f:selectItem itemLabel="Выберите контрагента .." noSelectionOption="true" />
<f:selectItems value="#{paymentOrder.userContractors}" var="contr"
itemValue="#{contr.idcontractor}"
itemLabel="#{contr.pnamecontractor}"/>
<a4j:ajax event="valueChange" listener="#{paymentOrder.valueContractorChanged}" render="idINNContractor, idKPPContractor, idNameContractor, idContractorAccounts" execute="#this"/>
</h:selectOneMenu>
</h:panelGrid>
<h:panelGrid columns="1">
<h:panelGrid columns="4">
<h:outputText value="ИНН"/>
<h:inputText id="idINNContractor" value="#{paymentOrder.chosenContractor.inncontractor}"/>
<h:outputText value="КПП"/>
<h:inputText id="idKPPContractor" value="#{paymentOrder.chosenContractor.kppcontractor}"/>
</h:panelGrid>
<h:panelGrid columns="2">
<h:outputText value="Получатель"/>
<h:inputTextarea id="idNameContractor" value="#{paymentOrder.chosenContractor.pnamecontractor}"/>
</h:panelGrid>
</h:panelGrid>
<h:panelGrid columns="1">
<h:panelGrid columns="2">
<h:outputText value="Счёт получателя:"/>
<h:selectOneMenu id="idContractorAccounts" value="#{paymentOrder.curContractorAccount}">
<f:selectItem itemLabel="Выберите счёт .." noSelectionOption="true" />
<f:selectItems value="#{paymentOrder.contractorAccounts}" var="acc"
itemValue="#{acc.naccountcontractor}"
itemLabel="#{acc.advName}"/>
<a4j:ajax event="valueChange" listener="#{paymentOrder.valueAccountChanged}" render="idContrAcc, idNameContrBank, idBikContrBank, idAccContrBank" execute="#this"/>
</h:selectOneMenu>
</h:panelGrid>
<h:panelGrid columns="2">
<h:outputText value="Сч.№"/>
<h:inputText id="idContrAcc" value="#{paymentOrder.curContractorAccount}"/>
</h:panelGrid>
</h:panelGrid>
</h:panelGrid>
</h:panelGrid>
<h:panelGrid columns="1">
<h:panelGrid columns="4">
<h:panelGrid columns="2">
<h:outputText value="Банк получателя"/>
<h:inputTextarea id="idNameContrBank" value="#{paymentOrder.chosenBank.namebank}" />
</h:panelGrid>
<h:panelGrid columns="2">
<h:outputText value="БИК"/>
<h:inputText id="idBikContrBank" value="#{paymentOrder.chosenBank.bikbank}"/>
<h:outputText value="Сч.№"/>
<h:inputText id="idAccContrBank" value="#{paymentOrder.chosenBank.bankkorshet}"/>
</h:panelGrid>
</h:panelGrid>
</h:panelGrid>
</h:panelGrid>
And my back bean fragment:
public class PaymentOrder {
#EJB(lookup="JpaBankBean/local")
private JpaBankBeanLocal jpaBean;
private Paymentdocument pDocument;
private Paymentorder pOrder;
private Klbankrf chosenBank;
private String curContractorAccount;
private String curContractorBank;
private String curContractor;
private String err;
private String chosenAccount;
private Contractor chosenContractor;
#SuppressWarnings("rawtypes")
private Set contractorAccounts;
#SuppressWarnings("rawtypes")
private List contractorBanks;
private String userName;
private Date nowDate;
public PaymentOrder() {
this.nowDate = Calendar.getInstance().getTime();
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
setUserName(((UserDetails)principal).getUsername());
} else {
setUserName(principal.toString());
}
pDocument = new Paymentdocument();
pOrder = new Paymentorder();
chosenContractor = new Contractor();
chosenBank = new Klbankrf();
}
public void valueContractorChanged()
{
chosenContractor = jpaBean.getContractor(Integer.valueOf(getCurContractor()));
setContractorAccounts(jpaBean.getContractorAccounts(Integer.valueOf(getCurContractor())));
pDocument.setReceiver(chosenContractor.getPnamecontractor());
}
public List getUserContractors()
{
return jpaBean.getUserContractors(userName);
}
public void valueAccountChanged()
{
chosenBank.setNamebank("SBER");
//TODO chosenBank = jpaBean.getContractorBank(getCurContractorAccount());
//TODO setChosenAccount("012345678901234567890");
}
}
So, "SBER" appear in idNameContrBank field only if I change second menu value to somethig and then change back to NoSelectOption. It is strange to me.
I use RichFaces 4, JBoss AS 6.
I've found some information at this forum, but there is no solution.
How to make second menu to change something? Or is there my mistake? Any ideas?
Thanks in advance!
I've made some experiments.
When I change second menu to some value populated with AJAX, page gets response like that:
<?xml version='1.0' encoding='UTF-8'?>
<partial-response>
<changes>
<update id="docForm:idContrAcc">
<![CDATA[<input id="docForm:idContrAcc" type="text" name="docForm:idContrAcc" style=" width : 100%;" />]]>
</update>
<update id="docForm:idNameContrBank">
<![CDATA[<textarea id="docForm:idNameContrBank" name="docForm:idNameContrBank" style=" width : 130px;"></textarea>]]>
</update>
<update id="docForm:idBikContrBank">
<![CDATA[<input id="docForm:idBikContrBank" type="text" name="docForm:idBikContrBank" style=" width : 140px;" />]]>
</update>
<update id="docForm:idAccContrBank">
<![CDATA[<input id="docForm:idAccContrBank" type="text" name="docForm:idAccContrBank" style=" width : 140px;" />]]>
</update>
<update id="javax.faces.ViewState"><![CDATA[3312710224811729695:3995303008700914422]]>
</update>
</changes>
</partial-response>
There are no values, but right IDs! How it can be? Why?
But if I return NoSelectionOption active then page gets response
<?xml version='1.0' encoding='UTF-8'?>
<partial-response>
<changes>
<update id="docForm:idContrAcc">
<![CDATA[<input id="docForm:idContrAcc" type="text" name="docForm:idContrAcc" value="" style=" width : 100%;" />]]>
</update>
<update id="docForm:idNameContrBank">
<![CDATA[<textarea id="docForm:idNameContrBank" name="docForm:idNameContrBank" style=" width : 130px;">SBER</textarea>]]>
</update>
<update id="docForm:idBikContrBank">
<![CDATA[<input id="docForm:idBikContrBank" type="text" name="docForm:idBikContrBank" style=" width : 140px;" />]]>
</update>
<update id="docForm:idAccContrBank">
<![CDATA[<input id="docForm:idAccContrBank" type="text" name="docForm:idAccContrBank" style=" width : 140px;" />]]>
</update>
<update id="javax.faces.ViewState">
<![CDATA[3312710224811729695:3995303008700914422]]>
</update>
</changes>
</partial-response>
It's seems OK - values are presented! But why does it happen when NoSelectionOption is active?!
I suspect JSF might be overwriting your attempts since it usually calls all the setters for all the properties exposed on the page and it normally does this after the valueChangeListeners are executed. I've never seen the <a4j:ajax /> tag but you could try the <a4j:support /> tag. Instead of this:
<a4j:ajax event="valueChange" listener="#{paymentOrder.valueContractorChanged}" render="idINNContractor, idKPPContractor, idNameContractor, idContractorAccounts" execute="#this"/>
Try this:
<a4j:support event="onchange" ajaxSingle="true" reRender="idINNContractor, idKPPContractor, idNameContractor, idContractorAccounts"/>
Since ajaxSingle="true" is included in the tag it means that only the setter for curContractor will be called (and not all the rest on the page like JSF normally does). Then you can call valueContractorChanged from the setter (or even just make it part of the setter).
If you replace all of the <a4j:ajax /> tags in this way you should get the desired result. Check out more info about the tag via the link above.