inputHidden not being updated on immediate ajax action - ajax

I'm trying to code a JSF page with a pop-up dialog that allows users to enter data. I've used PrimeFaces before, but for various reasons I've decided not to use it for this project. After all, I just need a simple pop-up dialog. How hard could it be to code it myself?
The ways it's suppose to work is:
The user clicks the Add New Record button and the dialog is shown.
The user enters data and clicks the Save button.
If there are validation errors, the messages are displayed in the dialog and the dialog remains visible.
If there are no errors, a success message is shown on the main page and the dialog disappears.
The user can press the Cancel button to close the dialog without saving.
My problem is that when the user presses Save and gets validation errors and then presses Cancel, the dialog does not disappear. I can see that under this scenario, the value for the inputHidden field does not get updated to false. The isShowDialog() method on the backing bean does not get called.
Here is my facelet:
<?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:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<h:outputScript library="js" name="jquery-1.11.0.js" />
</h:head>
<h:body>
<h:messages id="pageMessages" errorStyle="color: red;" infoStyle="color: green;" />
<script type="text/javascript">
function showHideDialog(data) {
if (data.status == "success") {
if ($("#dialogForm\\:showDialog").val() == "true") {
$("#dialogDiv").show();
$("#pageMessages").hide();
} else {
$("#dialogDiv").hide();
$("#pageMessages").show();
}
}
}
</script>
<div id="dialogDiv" style="border: thick solid black; display: none;">
<h:form id="dialogForm">
<h:messages id="dialogMessages" errorStyle="color: red;" infoStyle="color: green;" />
<h:inputHidden id="showDialog" value="#{backingBean.showDialog}" />
<h:panelGrid columns="2">
<h:outputLabel for="dialogField" value="Some Field:" />
<h:inputText id="dialogField" value="#{backingBean.dialogField}" label="Some field" required="true" />
</h:panelGrid>
<h:commandButton value="Save" action="#{backingBean.save}">
<f:ajax event="action" execute=":dialogForm" render=":dialogForm :pageMessages" onevent="showHideDialog" />
</h:commandButton>
<h:commandButton value="Cancel" action="#{backingBean.cancel}" immediate="true">
<f:ajax event="action" render=":dialogForm :pageMessages" onevent="showHideDialog" />
</h:commandButton>
</h:form>
</div>
<h:form id="pageForm">
<p>Normal page content. Blah, blah, blah.</p>
<h:commandButton value="Add New Record" action="#{backingBean.addNew}">
<f:ajax event="action" render=":dialogForm :pageMessages" onevent="showHideDialog" />
</h:commandButton>
</h:form>
</h:body>
</html>
Here is my backing bean:
package com.mycompany.example;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class BackingBean {
private String dialogField;
private boolean showDialog = false;
public String addNew() {
dialogField = null;
showDialog = true;
return null;
}
public String save() {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "New row successfully saved.", null));
showDialog = false;
return null;
}
public String cancel() {
showDialog = false;
return null;
}
public String getDialogField() { return dialogField; }
public void setDialogField(String dialogField) { this.dialogField = dialogField; }
public boolean isShowDialog() { return showDialog; }
public void setShowDialog(boolean showDialog) { this.showDialog = showDialog; }
}
If it matters, I'm using Mojarra 2.2.5 and Tomcat 7.0.42.
Any suggestions or insights would be appreciated.

I found an answer. I changed
<h:inputHidden id="showDialog" value="#{backingBean.showDialog}" />
to
<input id="dialogForm:showDialog" type="hidden" value="#{backingBean.showDialog}" />

I found the answer reading the following BalusC article: Okay, when should I use the immediate attribute?
In short, use
immediate="true" in <h:inputHidden id="showDialog" too,
and
execute="showDialog" in <f:ajax inside <h:commandButton value="Cancel".
P.S.: Your example code was very useful.

Related

Required fields within a p:tabView

I have a jsf/primefaces page, with a tabView, that does not work as I would expect it to.
If I make a selection on tab 1, then move to tab 2 and save, it works fine. If I stay on tab 2 and save a second time, it displays the required message for the required field on the first tab.
If I return to the first tab, it is filled in, as it should be. If I press Save again now, on the first tab, it works again.
Why does jsf/primefaces think this field is not filled in?
Page:
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head />
<h:body>
<div style="height: 50px;">
<p:messages id="messages" autoUpdate="true" showDetail="true" closable="true" />
</div>
<h:form id="form">
<p:tabView id="tabs" dynamic="true">
<p:tab id="tab1" title="Tab 1">
<h:outputLabel value="Field 1" />
<h:selectOneMenu required="true" requiredMessage="Must fill in field 1 selection">
<f:selectItem itemLabel="Select..." itemValue="" noSelectionOption="true" />
<f:selectItem itemLabel="1" itemValue="1" />
</h:selectOneMenu>
<br/>
</p:tab>
<p:tab id="tab2" title="Tab 2">
<h:outputLabel value="Field 2" />
<p:inputText required="false" />
<br/>
</p:tab>
</p:tabView>
<p:commandButton value="Save" ajax="true" action="#{tabBean.action}" update="form"
oncomplete="setTimeout(function(){$('[id$=messages]').fadeOut()},'500')" />
</h:form>
</h:body>
</html>
Bean:
#ViewScoped
#ManagedBean(name = "tabBean")
public class TabBean implements Serializable {
private static final long serialVersionUID = 1L;
public TabBean() { }
#PostConstruct
public void init(){ }
public void action() {
// do the save, if validation doesn't fail first
addJsfMessage(FacesMessage.SEVERITY_INFO, "OK", "no prob");
}
private void addJsfMessage(Severity severity, String summary, String detail) {
FacesMessage msg = new FacesMessage(severity, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
While creating this sample, I found that either of the following changes got rid of the error, but neither is desirable:
turning off dynamic loading (the primefaces documentation says in dynamic mode, only the active tab contents
are rendered and when an inactive tab header is selected, content is loaded with ajax, which I assumed to mean on the first load but it seems to not just hide already-loaded tabs, but not render them at all)
replacing the <h:selectOneMenu ... /> with: <h:inputText required="true" requiredMessage="Must fill in field 1 text" />
It's caused by the combination of <p:tabView dynamic="true"> and <p:commandButton update="form">.
When (re)rendered, a dynamic tabview only contains the currently active tab, not others. Only when you change the tab, then it will be loaded into the HTML DOM tree asynchronously. Now, you're on submit updating the entire form, including the dynamic tabview, so it re-renders with only the currently active tab reloaded. In your particular case, the first tab doesn't exist in the HTML DOM tree at all and therefore nothing can be sent to the server side.
Try being more specific in <p:commandButton update>. Specify only the components which really need to be updated, not the entire form with the dynamic tabview.

PF SetPropertyActionListener breaks after first time

i've a problem that's driving me crazy! I have a component which renders a panel that have links on a footer. This links have, among other things, a title and HTML content I want to show. The idea is basically that when a link is clicked, the content of the page is changed via ajax and the html content of the link is shown (The footer remains on the page). Debbuging I've noticed that the first time a link is clicked the content does change, but then it doesn't. The setPropertyActionListener of the p.commandlinks in my composite component aren't executed any more after first time. I'have debbuged the project and this seems to be the problem, but I can't figure out why
Part of my composite component. Tryed setting an onstart and oncomplete js functions on the command link, and both are executed but the actionListeners aren't.
<cc:interface>
<cc:attribute name="compId"/>
<cc:attribute name="title"/>
<cc:attribute name="bean"/>
...
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<p:panel id="#{cc.attrs.compId}" header="#{cc.attrs.title}"
widgetVar="#{cc.attrs.compId}"
styleClass="#{cc.attrs.styleClass}">
<h:form>
<p:dataList id="datalist-#{cc.attrs.compId}" value="#{cc.attrs.bean.currentList}"
type="definition" widgetVar="datalist-#{cc.attrs.compId}"
var="items" >
<p:commandLink action="#{navBean.active}" update="#([id$=content])" styleClass="link"
value="#{items.descrInLang}" rendered="#{items.typeUrl eq 0}">
<f:setPropertyActionListener value="infoPage" target="#{navBean.selection}"/>
<f:setPropertyActionListener value="#{items.descrInLang}" target="#{infoPage.title}" />
<f:setPropertyActionListener value="#{items.HTMLContent}" target="#{infoPage.HTMLContent}"/>
</p:commandLink>
</p:dataList>
</h:form>
</p:panel>
</cc:implementation>
Part of my Navigation Bean
public void active() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException {
setAlbums(selection.equals("albums"));
setBand(selection.equals("band"));
setConcerts(selection.equals("concerts"));
setContacts(selection.equals("contacts"));
setHome(selection.equals("home"));
setSiteMap(selection.equals("siteMap"));
setInfoPage(selection.equals("infoPage"));
}
public void setSelection(String selection) {
this.selection = selection;
}
public boolean isInfoPage() {
return infoPage;
}
public void setInfoPage(boolean infoPage) {
this.infoPage = infoPage;
}
The bean which stores the title & HTML Content to show. After the first click on the link, the getters of Title & HTML content are executed, but the setters aren't (as declared in the setPropertyActionListeners)
#ManagedBean
#SessionScoped
public class InfoPage implements Serializable {
private String title;
private String HTMLContent;
public InfoPage() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getHTMLContent() {
return HTMLContent;
}
public void setHTMLContent(String HTMLContent) {
this.HTMLContent = HTMLContent;
}
}
The content part of the template which indicates what content should be shown according to the selected page on the NavBean. I pass the HTML content and the title of the infoPage as a parameter
<h:head>
</h:head>
<h:body>
<!-- the all containing file -->
<ui:composition template="template.xhtml">
<ui:define name="content">
<h:panelGroup layout="block" id="content">
<h:panelGroup layout="block" rendered="#{navBean.home}">
<ui:include src="homecontent.xhtml"/>
</h:panelGroup>
....
<h:panelGroup layout="block" rendered="#{navBean.infoPage}">
<ui:include src="infoPage.xhtml">
<ui:param name="title" value="#{infoPage.title}"/>
<ui:param name="infoContent" value="#{infoPage.HTMLContent}"/>
</ui:include>
</h:panelGroup>
</h:panelGroup>
</ui:define>
</ui:composition>
</h:body>
The info Page.
<h:form id="infoPageForm">
<p:panel id="infoPagePanel" header="#{title}">
<h:panelGrid columns="1" cellpadding="10">
<h:outputText escape="false" value="#{infoContent}" />
</h:panelGrid>
</p:panel>
</h:form>
Thanks for your time!

Conditionally render h:selectOneMenu and h:inputText by ajax depending on h:selectOneRadio selection

I have a form with 2 radio buttons: "type1" and "type2". If "type1" is chosen, then a dropdown must be displayed. If "type2" is chosen, then a textfield must be displayed.
Here's the view and the controller:
test.xtml
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j">
<h:form>
<h:selectOneRadio
id="type"
label="Type"
value="#{testBean.type}">
<f:selectItem itemLabel="Type1" itemValue="type1" />
<f:selectItem itemLabel="Type2" itemValue="type2" />
<f:ajax execute="#all" render="selectBox inputBox"/>
</h:selectOneRadio>
<h:selectOneMenu
id="selectBox"
label="Service"
value="#{testBean.service}"
rendered="#{testBean.isType1}"
style="width:285px">
<f:selectItem itemLabel="Medium" itemValue="medium" />
<f:selectItem itemLabel="Basic" itemValue="basic" />
<f:selectItem itemLabel="Premium" itemValue="premium" />
</h:selectOneMenu>
<h:inputText
id="inputBox"
size="50"
value="#{testBean.custom}"
rendered="#{!testBean.isType1}" />
</h:form>
</ui:composition>
TestBean.java
package com.test.backing;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean(name = "testBean")
#SessionScoped
public class TestBean implements Serializable
{
private static final long serialVersionUID = -4337084623546767911L;
private String type = "type1";
private String service;
private String custom;
public Boolean getIsType1()
{
if(type.equals("type1"))
{
System.out.println(type+":true");
return true;
}
else
{
System.out.println(type+":false");
return false;
}
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
public String getService()
{
return service;
}
public void setService(String service)
{
this.service = service;
}
public String getCustom()
{
return custom;
}
public void setCustom(String custom)
{
this.custom = custom;
}
}
When I start my application, I have the following in my stdout:
type1:true
type1:true
type1:true
type1:true
type1:true
type1:true
However, nothing happens in the UI when I choose another type. How is this caused and how can I solve it?
Try to replace xhtml code with the following code
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j">
<h:head>
</h:head>
<h:form prependId="false">
<h:selectOneRadio
id="type"
label="Type"
value="#{testBean.type}">
<f:selectItem itemLabel="Type1" itemValue="type1" />
<f:selectItem itemLabel="Type2" itemValue="type2" />
<f:ajax execute="#all" render="selectInputPanel"/>
</h:selectOneRadio>
<h:panelGroup id="selectInputPanel">
<h:selectOneMenu
id="selectBox"
label="Service"
value="#{testBean.service}"
rendered="#{testBean.isType1}"
style="width:285px">
<f:selectItem itemLabel="Medium" itemValue="medium" />
<f:selectItem itemLabel="Basic" itemValue="basic" />
<f:selectItem itemLabel="Premium" itemValue="premium" />
</h:selectOneMenu>
<h:inputText
id="inputBox"
size="50"
value="#{testBean.custom}"
rendered="#{!testBean.isType1}" />
</h:panelGroup>
</h:form></ui:composition>
Main problem in your code is,
Missing h:head to import jsf.js which is required for jsf ajax.
Please wrap your component into a panelGroup as suggested by #BaluC because once the component not rendered (not available on page) then the ajax on it will not work with its id.
And regarding number of time getIsType1() method calling is due to the rendered attribute, for more information check #Baluc's answer here
JSF generates HTML. JS/Ajax works on HTML. JS/Ajax updates HTML elements by finding it in HTML DOM tree by document.getElementById() and replacing its contents based on Ajax response. However, if a JSF component is instructed to not render HTML, then JS/Ajax cannot find it in the HTML DOM tree and thus can't replace anything.
You can only ajax-update the HTML representation of a JSF component which is always rendered. So, wrap them in e.g. a <h:panelGroup>.
<h:selectOneRadio ...>
<f:ajax ... render="selectAndInputBox" />
</h:selectOneRadio>
<h:panelGroup id="selectAndInputBox">
<h:selectOneMenu ... rendered="..." />
<h:inputText ... rendered="..." />
</h:panelGroup>
See also:
Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
Unrelated to the concrete problem, that getIsType1() method is clumsy. Just do the comparison directly in the view so that you can get rid of it.
<h:selectOneMenu ... rendered="#{testBean.type == 'type1'}" />
<h:inputText ... rendered="#{testBean.type != 'type1'}" />
or perhaps, more matching your initial question,
<h:selectOneMenu ... rendered="#{testBean.type == 'type1'}" />
<h:inputText ... rendered="#{testBean.type == 'type2'}" />

The f:ajax listener method in h:selectOneMenu is not executed

The page is generated correctly with appropriate values in managed bean, but ajax events in these two h:selectOneMenus don't works. Listener is not called. An error has to be somewhere within tags, but I don't see it.
<f:view>
<h:form>
<h:messages />
<h:panelGrid columns="3">
<h:outputLabel value="Choose your faculty: *" for="faculties" />
<h:selectOneMenu id="faculties" value="#{registrateStudent.selectedFaculty}" >
<f:ajax event="change" listener="#{registrateStudent.genSpecializations}" execute="faculties" render="specializations" />
<f:selectItems value="#{registrateStudent.listFaculty}" var="curFac" itemLabel="#{curFac.name}" itemValue="#{curFac}" />
</h:selectOneMenu>
<h:message id="message_faculties" for="faculties" />
<h:outputLabel value="Choose your specialization: *" for="specializations" />
<h:selectOneMenu id="specializations" value="#{registrateStudent.selectedSpecialization}" >
<f:selectItems value="#{registrateStudent.listSpecialization}" var="curSpec" itemLabel="#{curSpec.name}" itemValue="#{curSpec}"/>
</h:selectOneMenu>
<h:message id="message_specializations" for="specializations" />
Managed Bean:
#ManagedBean(name = "registrateStudent")
#ViewScoped
public class RegistrateStudent {
private Faculty selectedFaculty;
private List<Faculty> listFaculty;
private Specialization selectedSpecialization;
private List<Specialization> listSpecialization;
private boolean showSpecialization = false;
/** Creates a new instance of RegistrateStudent */
public RegistrateStudent() {
users = new Users();
System.out.println("poaposd1");
student = new Student();
}
#PostConstruct
public void init() {
listFaculty = ff.findAll();
if (listFaculty != null) {
selectedFaculty = listFaculty.get(0);
listSpecialization = sf.findByFaculty(selectedFaculty.getIdFaculty());
if (listSpecialization != null) {
selectedSpecialization = listSpecialization.get(0);
}
else {}
} else {}
}
public void genSpecializations(AjaxBehaviorEvent event) {
if (sf.findByFaculty(selectedFaculty.getIdFaculty()) != null) {
this.showSpecialization = true;
} else {
JsfUtil.addSuccessMessage("faculties", "We don't have specializations for such faculty");
}
}
}
UPDATE:
I've found out a few interesting things:
<f:ajax> tag doesn't work at <h:link>, <h:selectOneMenu>, <h:button>, <h:commandButton>. In this cases incorrect values in render attribute is not noticed, but incorrect value of event attribute generate an error.
<h:outputLabel>, <h:inputText> work with <f:ajax> properly
The <f:ajax> requires jsf.js file being included in the HTML <head>. It contains all JS functions for doing the JSF ajax magic.
To achieve this, ensure that you're using <h:head> instead of <head> in the master template. JSF will then automatically include the necessary <script> element there pointing to jsf.js.
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Look, with h:head</title>
</h:head>
<h:body>
Put your content here.
</h:body>
</html>
Note that in a bit decent webbrowser with a bit decent webdeveloper toolset like Firefox's Web Developer Toolbar and/or Firebug you should immediately have noticed JS errors like jsf is undefined when the ajax request is to be executed. That should at least have given something to think about.
Update: as per your update
I've found out a few interesting things:
<f:ajax> tag doesn't work at <h:link>, <h:selectOneMenu>, <h:button>, <h:commandButton>. In this cases incorrect values in render attribute is not noticed, but incorrect value of event attribute generate an error.
<h:outputLabel>, <h:inputText> work with <f:ajax> properly.
The <h:link> and <h:button> are intented for GET requests only, not POST requests. It should however work just fine on <h:selectOneMenu> and <h:commandButton>. Don't you have more code into the complete picture which you omitted from the question for simplicity? Which JSF impl/version are you using? Are you using the right libraries in classpath? It look like that you must really have messed up something.
To convince you (and myself) I just created the following copy'n'paste'n'runnable testcase
<!DOCTYPE html>
<html lang="en"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<h:head>
<title>SO question 6089924</title>
</h:head>
<h:body>
<h:form>
<h:selectOneMenu value="#{bean.selected}">
<f:selectItem itemValue="#{null}" itemLabel="Select..." />
<f:selectItem itemValue="one" />
<f:selectItem itemValue="two" />
<f:selectItem itemValue="three" />
<f:ajax listener="#{bean.listener}" render="result" />
</h:selectOneMenu>
<h:commandButton value="commandButton" action="#{bean.submit}">
<f:ajax listener="#{bean.listener}" render="result" />
</h:commandButton>
<h:outputText id="result" value="#{bean.selected} #{bean.result}" />
<h:messages />
</h:form>
</h:body>
</html>
with this bean
package com.example;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.AjaxBehaviorEvent;
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
private String selected;
private String result;
public void submit() {
System.out.println("submit");
}
public void listener(AjaxBehaviorEvent event) {
System.out.println("listener");
result = "called by " + event.getComponent().getClass().getName();
}
public String getSelected() {
return selected;
}
public void setSelected(String selected) {
this.selected = selected;
}
public String getResult() {
return result;
}
}
It runs fine with Mojarra 2.1.1 on Tomcat 7.0.12.
INFO: Starting Servlet Engine: Apache Tomcat/7.0.12
INFO: Initializing Mojarra 2.1.1 (FCS 20110408) for context '/playground'
Be careful if you have f:metadata and f:viewparam tags since the setters of the parameters will be called with every ajax request.
Can you provide the error log if there is any error/exception that is being generated when you call the ajax call?

JSF 2.0 AJAX: jsf.ajax.request to call method not only rerender an area of page

I'm looking for a soultion of the following problem:
_The is a list of links with different types of cars.
_The user can click on each car in the list and a ajax request should be sent.
_The response of the ajax request should be dependent on the id (of each car) and displayed in an panelGroup.
So what I need is a possibility to call a method on the backing-bean. Additionally, this method should be called with a car id as its parameter.
My code so far looks like:
...
function showDetails(detailsFor){
jsf.ajax.request(this, event, {render: 'form1:carDetails'});
}
...
<ul>
<ui:repeat value="#{carTree.getCars)}" var="car">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<li onclick="showDetails(#{car.id});">#{car.name}</li>
</ui:repeat>
</ul>
...
<h:panelGroup id="carDetails" layout="block" style="float:left;">
// need the details of each 'selected /clicked' car here
</h:panelGroup>
...
And the method in the backing bean should look like:
public class CarTree {
...
public String getCarDetails(int carid){
return "The car details for the car id "+carid+" are......";
}
...
}
I've no idea how to call a method by using the new JSF 2.0 AJAX functionality. Please, help me...
Use f:setPropertyActionListener to pass a object from JSF page to your backend. This tag is especially useful when you are using repeatable components like datatable
No need to use raw JavaScript, you can use <f:ajax />. Plus instead of worrying about Car id and all, just send it completely to backing bean.
Here is a sample example:
The Car class:
public class Car {
int id;
String brand;
String color;
public Car(int id, String brand, String color) {
this.id = id;
this.brand = brand;
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
The CarTree class:
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean(name = "CarTree")
#RequestScoped
public class CarTree {
List<Car> carList;
Car selectedCar;
public Car getSelectedCar() {
return selectedCar;
}
public void setSelectedCar(Car selectedCar) {
this.selectedCar = selectedCar;
}
public List<Car> getCars() {
return carList;
}
public void setCars(List<Car> carList) {
this.carList = carList;
}
public CarTree() {
carList = new ArrayList<Car>();
carList.add(new Car(1, "jaguar", "grey"));
carList.add(new Car(2, "ferari", "red"));
carList.add(new Car(3, "camri", "steel"));
}
}
The JSF page:
<?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"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body id="mainBody">
<h:form id="carForm">
<h:dataTable value="#{CarTree.cars}" var="car">
<h:column>
<h:outputText value="#{car.id}"/>
</h:column>
<h:column>
<h:outputText value="#{car.brand}"/>
</h:column>
<h:column>
<h:outputText value="#{car.color}"/>
</h:column>
<h:column>
<h:commandButton value="Show Car Detail" >
<f:setPropertyActionListener target="#{CarTree.selectedCar}" value="#{car}"/>
<f:ajax render=":carForm:carDetails" />
</h:commandButton>
</h:column>
</h:dataTable>
<h:panelGroup id="carDetails" layout="block" style="float:left;">
<h:outputText value="#{CarTree.selectedCar.id}" />
<h:outputText value="#{CarTree.selectedCar.brand}" />
<h:outputText value="#{CarTree.selectedCar.color}" />
</h:panelGroup>
</h:form>
</h:body>
</html>
Hope this helps.
I haven't tested myself, but I'd suggest you try something like this (assuming your class CarTree is #Named and, therefore, can be referred to inside the JSF page using the name carTree):
<ul>
<ui:repeat value="#{carTree.getCars)}" var="car">
<li><h:commandLink action="#{carTree.getCarDetails(car.id)}" value="#{car.name}">
<f:ajax render="carDetails" />
</h:commandLink></li>
</ui:repeat>
</ul>
...
<h:panelGroup id="carDetails" layout="block" style="float:left;">
// need the details of each 'selected /clicked' car here
</h:panelGroup>
I think the contents of the action property in the <h:commandLink /> can also be coded as the listener property in the <f:ajax /> tag. Don't know if there's any difference...
If you don't want to use <h:commandLink /> you could replace it with <h:outputText /> and add the property event="click" to the <f:ajax /> tag. I think that would work as well. In this case, the method call would have to be in the listener property of the <f:ajax /> tag.

Resources