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.
Related
I have a submit button that submits to a database using the button below.
If this button return "error", I want to use an AJAX to remove the form and its content and add a text in place of the form.
Edit:
The reason of staying in the same page after clicking the submit button is because, the same CMS content above and below the form is used on both the current page and redirected page after submit. Thus, its easier to just remove form and display the error page. This will avoid the whole page to reload due to redirect.
</h:form id="myForm">
...
<h:commandButton action="#{testBean.submitForm}"
type="submit" value="Create Button">
</h:commandButton>
</h:form>
Remember to use <h:head> and <h:body> instead of <head> and <body>.
<h:panelGroup id="mypanelgroup">
<h:outputText value="#{testBean.errorMessage}" rendered="#{testBean.errorMessage ne null}" />
<h:form rendered="#{testBean.errorMessage eq null}">
<!-- -->
<h:commandButton value="submit" action="#{testBean.submitForm()}">
<f:ajax execute="#form" render=":mypanelgroup"></f:ajax>
</h:commandButton>
</h:form>
</h:panelGroup>
#ManagedBean
public class TestBean {
private String errorMessage = null;
public String getErrorMessage() {
return errorMessage;
}
public void submitForm(){
// .....
//if(error){
this.errorMessage = "My error Message";
//}
}
}
Initially, the <h:outputText> is not rendered because errorMessage by default was set to null.
f:setPropertyActionListener in p:commandLink
How can I bring the data from the setter of the target- Attribut to the getter of the surrounding onclick-attribute?
I want to build an hyperlink within a p:CommandLink (onclick = "window.open('http://www.somewebsite.de ...')" for each row of the datatable. So the Request-parameters of "somewebsite" change depending on the actual row.
I have a working p:commandLink, because I can generate an new browser-window but only with a (constant! [and that is the problem/challenge]) website.
I can also access the needed data from EACH row through the p:commandLink that means the belonging data which I need for the hyperlink-Request-parameters, but I cannot bring it together, because I get a Nullpointer-Exception.
Look the spot which is marked with // HERE SEEMS TO BE THE PROBLEM
<p:dataTable value="#{columnsViewZwei.dataList1}" var="row"
resizableColumns="true"
styleClass="foo"
tableStyleClass="maggtabl2"
tableStyle="maggtabl2"
headerClass="order-table-header"
rowClasses="order-table-odd-row,order-table-even-row">
<p:column headerText="Titel" sortBy="#{row.titel}" filterBy="#{row.titel}">
<h:outputText value="#{row.titel}" />
</p:column>
<p:column headerText="Typ" sortBy="#{row.typ}" filterBy="#{row.typ}">
<h:outputText value="#{row.typ}" />
</p:column>
<p:column headerText="Metadaten" style="width:64px;text-align: center">
<p:commandLink value="show" title="show metatdata" onclick="#{columnsViewZwei.testurl}" >
<f:setPropertyActionListener value="#{row}" target="#{columnsViewZwei.selectedSET}" />
</p:commandLink>
</p:column>
</p:dataTable>
package bbb;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.xml.stream.XMLStreamException;
import bbb.InspireDS;
#ManagedBean
#ViewScoped
public class ColumnsViewZwei implements Serializable {
private String testurl = "";
private InspireDS selectedDataset;
public InspireDS getSelectedDataset() {
System.out.println("getSelectedDataset() ");
return this.selectedDataset;
}
public String selectedDataset() {
selectedDataset = getSelectedDataset();
this.testurl = "window.open('..Info.do?fileIdentifier="+selectedDataset.metadaten+"')";
return testurl;
}
public void setTesturl(String testurl){
this.testurl = "window.open('http://...Info.do?fileIdentifier="+selectedDataset.metadaten+"')";
}
public String getTesturl() {
//selectedDataset = getSelectedDataset();
// HERE SEEMS TO BE THE PROBLEM: NullPointerException probably for selectedDataset
//this.testurl = "window.open('..?fileIdentifier="+selectedDataset.metadaten+"')";
this.testurl = "window.open(' ...')"; // works, but constant hyperlink
return testurl;
}
// This from f:setPropertyActionListener value="#{row}" target="#{columnsViewZwei.selectedSET}
// brings the data I need, but it should be accessable in getTesturl()
public void setSelectedSET(InspireDS inspirDS) {
this.selectedDataset = inspirDS;
this.testurl = "window.open('...do?fileIdentifier="+selectedDataset.metadaten+"')";
}
private List<InspireDS> dataList1;
#ManagedProperty("#{inspireTabelleBean}")
private InspireTabelleBean insTabBean;
#PostConstruct
public void init() {
try {
if(insTabBean==null){System.out.println("s e r v i ce = = n u ll");}
dataList1 = insTabBean.fillListbyparse();
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setService(InspireTabelleBean service) {
this.insTabBean = service;
}
public List<InspireDS> getDataList1() {
return dataList1;
}
public void setInsTabBean(InspireTabelleBean service) {
this.insTabBean = service;
}
}
I'm not able to reproduce your code, i did it a simplified version without datatable.
Instead of window.open function i simply used target="_blank" as explained in this this post.
The error you made is use onclick coupled with f:setPropertyActionListener. The reason why that doesn't function is explained in this post.
faces-redirect=true is needed for change url (read more on this post), and ajax must be false.
However, when you want call bean function through js, take a look to remote command.
xhtml Page:
<h:form id="form_open_window" target="_blank">
<p:commandLink value="show" title="show metatdata" action="#{columnsViewZwei.goToUrl()}" ajax="false" >
<f:setPropertyActionListener value="metadata_parameter" target="#{columnsViewZwei.metadata}" />
</p:commandLink>
</h:form>
Bean Code:
private String testurl = "index";
private String metadata;
public String getMetadata() {
return metadata;
}
public void setMetadata(String metadata) {
this.metadata = metadata;
}
public String goToUrl() {
System.out.println(metadata);
return testurl + "?faces-redirect=true&fileIdentifier=" + metadata;
}
PS: If You don't want deviate too much from your solution, evaluate use of oncomplete instead of onclick, but i didn't try it.
I hope this is useful.
and thank you for your fast answer. I used your suggestions, but what happend was not what i want.
(The table was distorted etc.)
I put in each Method (getter and setter) the System.out.println(...) as you did for goToUrl() {...},
but I did not see any printouts on the console. So it is mysterious which code was invoked.
When I clicked on the commandLink I got a new window with the same facelet "index.xhtml" as i started from. I guess this is the effect of testurl = "index"; My view-file (facelet) has the name "index.xhtml".
I think it is good that you mentioned the other posts so I can try to understand your arguments.
I suppose that I need the value="#{row}" in the f:setPropertyActionListener to access the current row/dataset because in this current row/dataset is the information which i need to build the url to call/access a remote website.
With kind regards,
Yours sincerely
tsitra
ooooooooooooooooo
date:22.12.2014
A possible solution without using a Primefaces component ( control ) was :
<p:dataTable value="#{columnsViewZwei.dataList1}" var="o9"
resizableColumns="true">
<p:column headerText="Titel" sortBy="#{o9.titel}" filterBy="#{o9.titel}">
<h:outputText value="#{o9.titel}" />
</p:column>
<p:column headerText="Typ" sortBy="#{o9.typ}" filterBy="#{o9.typ}">
<h:outputText value="#{o9.typ}" />
</p:column>
<p:column headerText="Metadaten">
show
</p:column>
</p:dataTable>
I have the following code in JSF
<h:outputLabel value="Date" for="rdate" />
<h:inputText id="rdate" autocomplete="off"
value="#{myMB.abstractProject.joinedDate}">
</h:inputText>
and in Entity class, I have declared as
private Date joinedDate;
public Date getJoinedDate() {
return joinedDate;
}
public void setJoinedDate(Date joinedDate) {
this.joinedDate= joinedDate;
}
The problem is, in ManagedBean I am getting null for the following
System.out.println("date in save method "
+ abstractRequest.getJoinedDate());
What could be the reason for this? h:inputText is infact in <h:form>. Scope of my bean is #ViewAccessScoped
You need to use f:convertDateTime. Like:
<h:outputLabel value="Date" for="rdate" />
<h:inputText id="rdate" autocomplete="off" value="#{myMB.abstractProject.joinedDate}" label="Date">
<f:convertDateTime pattern="dd-MM-yyyy" />
</h:inputText>
Here is an example.
Edit:
Here is what I have done:
The xhtml:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" template="/WEB-INF/templates/globalTemplate.xhtml">
<ui:define name="title">1532116</ui:define>
<ui:define name="content">
<h:form>
<h:outputLabel value="Date" for="date" />
<h:inputText id="date" value="#{so15321163.date}" label="Date" required="true">
<f:convertDateTime pattern="dd-MM-yyyy"/>
</h:inputText>
<h:message for="date" style="color:red" />
<h:commandButton value="Submit" actionListener="#{so15321163.listener}"/>
</h:form>
</ui:define>
</ui:composition>
The managed bean:
package app.so.dev.web.controller;
import java.io.Serializable;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
#ManagedBean(name="so15321163")
#ViewScoped // #SessionScoped
public class SO15321163 implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8012804893825661900L;
private Date date;
#PostConstruct
public void init() {
}
public void listener(ActionEvent event) {
System.out.println(date);
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
Not sure, but if you are missing the annotation #Named then jsf bean is not working properly.
Here is some more detailed explanation: https://cwiki.apache.org/confluence/display/EXTCDI/Conversations
I have resolved the issue, the problem was there was an Converter error in another form element which stopped the form submit. I have used a Converter class to resolve this issue.
Thanks
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.
}
How can I show/hide component with JSF?
I am currently trying so do the same with the help of javascript but not successfull.
I cannot use any third party libraries.
Thanks| Abhi
You can actually accomplish this without JavaScript, using only JSF's rendered attribute, by enclosing the elements to be shown/hidden in a component that can itself be re-rendered, such as a panelGroup, at least in JSF2. For example, the following JSF code shows or hides one or both of two dropdown lists depending on the value of a third. An AJAX event is used to update the display:
<h:selectOneMenu value="#{workflowProcEditBean.performedBy}">
<f:selectItem itemValue="O" itemLabel="Originator" />
<f:selectItem itemValue="R" itemLabel="Role" />
<f:selectItem itemValue="E" itemLabel="Employee" />
<f:ajax event="change" execute="#this" render="perfbyselection" />
</h:selectOneMenu>
<h:panelGroup id="perfbyselection">
<h:selectOneMenu id="performedbyroleid" value="#{workflowProcEditBean.performedByRoleID}"
rendered="#{workflowProcEditBean.performedBy eq 'R'}">
<f:selectItem itemLabel="- Choose One -" itemValue="" />
<f:selectItems value="#{workflowProcEditBean.roles}" />
</h:selectOneMenu>
<h:selectOneMenu id="performedbyempid" value="#{workflowProcEditBean.performedByEmpID}"
rendered="#{workflowProcEditBean.performedBy eq 'E'}">
<f:selectItem itemLabel="- Choose One -" itemValue="" />
<f:selectItems value="#{workflowProcEditBean.employees}" />
</h:selectOneMenu>
</h:panelGroup>
Generally, you need to get a handle to the control via its clientId. This example uses a JSF2 Facelets view with a request-scope binding to get a handle to the other control:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head><title>Show/Hide</title></h:head>
<h:body>
<h:form>
<h:button value="toggle"
onclick="toggle('#{requestScope.foo.clientId}'); return false;" />
<h:inputText binding="#{requestScope.foo}" id="x" style="display: block" />
</h:form>
<script type="text/javascript">
function toggle(id) {
var element = document.getElementById(id);
if(element.style.display == 'block') {
element.style.display = 'none';
} else {
element.style.display = 'block'
}
}
</script>
</h:body>
</html>
Exactly how you do this will depend on the version of JSF you're working on. See this blog post for older JSF versions: JSF: working with component identifiers.
Use the "rendered" attribute available on most if not all tags in the h-namespace.
<h:outputText value="Hi George" rendered="#{Person.name == 'George'}" />
You should use <h:panelGroup ...> tag with attribute rendered. If you set true to rendered, the content of <h:panelGroup ...> won't be shown. Your XHTML file should have something like this:
<h:panelGroup rendered="#{userBean.showPassword}">
<h:outputText id="password" value="#{userBean.password}"/>
</h:panelGroup>
UserBean.java:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class UserBean implements Serializable{
private boolean showPassword = false;
private String password = "";
public boolean isShowPassword(){
return showPassword;
}
public void setPassword(password){
this.password = password;
}
public String getPassword(){
return this.password;
}
}
One obvious solution would be to use javascript (which is not JSF). To implement this by JSF you should use AJAX. In this example, I use a radio button group to show and hide two set of components. In the back bean, I define a boolean switch.
private boolean switchComponents;
public boolean isSwitchComponents() {
return switchComponents;
}
public void setSwitchComponents(boolean switchComponents) {
this.switchComponents = switchComponents;
}
When the switch is true, one set of components will be shown and when it is false the other set will be shown.
<h:selectOneRadio value="#{backbean.switchValue}">
<f:selectItem itemLabel="showComponentSetOne" itemValue='true'/>
<f:selectItem itemLabel="showComponentSetTwo" itemValue='false'/>
<f:ajax event="change" execute="#this" render="componentsRoot"/>
</h:selectOneRadio>
<H:panelGroup id="componentsRoot">
<h:panelGroup rendered="#{backbean.switchValue}">
<!--switchValue to be shown on switch value == true-->
</h:panelGroup>
<h:panelGroup rendered="#{!backbean.switchValue}">
<!--switchValue to be shown on switch value == false-->
</h:panelGroup>
</H:panelGroup>
Note: on the ajax event we render components root. because components which are not rendered in the first place can't be re-rendered on the ajax event.
Also, note that if the "componentsRoot" and radio buttons are under different component hierarchy. you should reference it from the root (form root).
check this below code.
this is for dropdown menu. In this if we select others then the text box will show otherwise text box will hide.
function show_txt(arg,arg1)
{
if(document.getElementById(arg).value=='other')
{
document.getElementById(arg1).style.display="block";
document.getElementById(arg).style.display="none";
}
else
{
document.getElementById(arg).style.display="block";
document.getElementById(arg1).style.display="none";
}
}
The HTML code here :
<select id="arg" onChange="show_txt('arg','arg1');">
<option>yes</option>
<option>No</option>
<option>Other</option>
</select>
<input type="text" id="arg1" style="display:none;">
or you can check this link
click here