This week's book giveaway is in the Servlets forum.
We're giving away four copies of Murach's Java Servlets and JSP and have Joel Murach on-line!
See this thread for details.
The moose likes JSF and the fly likes Passing a Parameter via Ajax Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Murach's Java Servlets and JSP this week in the Servlets forum!
JavaRanch » Java Forums » Java » JSF
Bookmark "Passing a Parameter via Ajax" Watch "Passing a Parameter via Ajax" New topic
Author

Passing a Parameter via Ajax

Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
I'm using a ui:repeat to repeat through a List of Questions. Each Question has several values and I'm trying to pass a single question object in an Ajax call to reset/save a value on the question when a value is changed. See the jsf code snippet below. I have commented out the <f:param> value as when this is integrated, then the other ajax functionalities stop working (the render of the 2nd column created in the panelGrid). I need the reset() method on GQBackingBean to have access to Question object (q) to reset/save the addnInfo value on question based on if the answer value is 1 or 0. Basically, I need the text field and row to clear when user clicks "No" and then I need the row to show when user clicks "Yes" with the value blanked out. Currently, the hide and show of the 2nd row is working with the ajax event on the Radio Button and the render on the last 3 columns (making a 2nd row), but the field clearing isn't happening since I can't pick up the question value to manipulating it on the Backing Bean. I'm not sure how clear this question has come across, so please let me know if you need additional info.

<ui:repeat var="q" value="#{gQBackingBean.questions.questions}">
<h:panelGrid columns="3" class="customtable"
columnClasses="columnhd, customtddata, customtdrevdata">
<span><span class="required">*</span> #{q.question} </span>
<p:selectOneRadio value="#{q.answer}">
<f:selectItem itemValue="1" itemLabel="Yes"/>
<f:selectItem itemValue="0" itemLabel="No"/>
<f:ajax event="valueChange" render="@form moreInfo revCode"
listener="#{gQBackingBean.reset}"
update="moreInfo revCode">
<!-- f:param value="#{q}" name="question"/-->
</f:ajax>
</p:selectOneRadio>
<h:panelGrid id="revCode"
columns="2" columnClasses="columntdrev, columntdrevd" sytle="display:inline"
rendered="#{gQBackingBean.showRevCode}">
<span><span class="required">*</span> Rev. Code:</span>
<h:selectOneMenu id="selectRev" value="#{q.revisionCode}">
<f:selectItem itemLable="Select One" itemValue=""/>
<f:selectItem itemLabel="Test" itemValue="test"/>
</h:selectOneMenu>
</h:panelGrid>

<h:panelGroup rendered="#{q.answer eq '1'}">
<div class="subquestion1"><span class="required">*</span> #{txt.gqMoreInfo}:</div>
</h:panelGroup>
<p:inputText id="moreInfo"
value="#{q.addnInfo}"
rendered="#{q.answer eq '1'}"/>
<h:panelGrid columns="2" columnClasses="columntdrev, columntdrevd" sytle="display:none"
rendered="#{q.answer eq '1'}"/>
</h:panelGrid>
</ui:repeat>
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 15950
    
  19

Welcome to the Ranch, Jessie!

I don't really recommend using raw HTML such as div and span on JSF view definitions. A span is generated in a more JSF-friendly manner from the h:outputText element. And actually, the panelGrid has a heading facet, that would be better for containing the question.

But that's just details. The ui:repeat element tends to get overused. Seems people just feel that they absolutely must make JSF views be programmable a la JSTL when in point of fact, as a relatively pure implementation of MVC JSF should properly be keeping the logic in the java code. From what I can see, however, your task would be a lot easier if you were to make each question and its related controls be contained in a panelGrid nested in a dataTable instead of a ui:repeat. That way, AJAX wouldn't have to worry about parameters, because the dataTable's dataModel would automatically track which question was being interacted with.

By the way, you can make code and XML samples more readable if you use the "Code" button in our editor to wrap formatting tags around them.


Customer surveys are for companies who didn't pay proper attention to begin with.
Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
The div's and span's are used for layout issues. This was the layout design I was told to work with... There are some constraints I have on how to make these questions look from the business side. The spans are used to make the * a certain class, while the question is another and still keep them as a single component on the panelGrid. If there is a better way to do this, I'm open to suggestions as I'm new to jsf.

I have used your suggestion of changing to a dataTable instead of panelGrid and this has in-fact resolved an issue I was having with the addnInfo field clearing when the ajax was called on another question. However, I'm still not sure how to get the backing bean reset method to know which question we are on. Is there a way to pick up the index on this method?

Also, this has created a gap between my quesitons, I set the border to 0 on the dataTable, but this didn't fix it.

I'm looking into how to resolve these 2 issues, but if anyone has a quick answer, that would be greatly appreciated!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 15950
    
  19

Check the docs, because I could be wrong, but I think you can dispense with the HTML div/span stuff like so:


You may very well be able to simplify that even further by setting a bullet style on the question text, for that matter. There's also a headerClass attribute that covers the entire header row on a panelGrid. As mentioned previously, the outputText generates a span for you.

Whenever an action method is fired, you can obtain the model row in question by invoking the model's getRowData or getRowIndex method. The only issue is that the backing bean for a dataModel cannot be in request scope, since that would cause the original model to be destroyed and replaced with one that wasn't properly initialized. So Session scope or higher for JSF1 and View scope or higher for JSF2.
Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
I feel so silly... I resolved the spacing issue by changing the cellpadding and cellspacing to 0... it was so easy, I missed it! lol

As for the spanning issue, I have tried many of the things you suggest, the header makes the question go above the answer and revcode and they need to be on the same line. and by using an outputText, it creates the * and question value as 2 components and they come out as different columns in the panelGrid and shift everything on the grid.

I'm still not sure how to get the reset function to grab the rowIndex. Here is what I have and notes of what I'm trying to do:

xhtml:


backing bean method:


...and thank you for the tip on the code tags, I'm using it above!
Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
I found the solution!

I changed the listener from an ajax listener to a valueChangeListener and was able to retrieve the row that way:

xhtml:


backing bean method:



Thank you for your help! You have guided me in the right direction!
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 15950
    
  19

Ah, I thought you WANTED the question above the answers!

One gripe people have about JSF is that there are no rownspan/colspan attributes for the core table elements. However, you can get the reverse effect by using the h:panelGroup element.

I've never gotten around to trying out the new ajax tags since I use RichFaces and they have their own AJAX support (which seems to me to be simpler). But presumably, you'd retrieve the row in question the same way you would in an ordinary action method or listener. Which is to obtain the table's dataModel object and invoke the getRowData() method on it to obtain the model row corresponding to the table row element that contains the item being monitored.
Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
I, apparently, am not done with this issue. I was able to retrieve the rowIndex correctly, but it seems when I applied the logic, all the fields on each row are cleared out:



output:
Question: Does applicant own, operate or lease aircraft or watercraft?
Answer: 1
AddnInfo(before):
AddnInfo(after):

The before value should have been "abc" I'm guessing that the form needs to be saved prior to executing this. How do I do this?
Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
lol... No, I want the Question, answer, and revCode on 1 line and then a 2nd row for addnInfo, which has it's own "question" and inputbox, this is the input I'm trying to clear when user selects "No"
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 15950
    
  19

Jessie Mohr wrote:
The before value should have been "abc" I'm guessing that the form needs to be saved prior to executing this. How do I do this?


First of all, don't walk the UIComponent tree yourself. I believe that we've had people do this in the past and had similar problems. Just access the table dataModel object that's already in your backing bean. As a rule the more javax.faces classes references - other than the model classes - the more likely you're not doing it right. JSF was designed to make things as POJO as possible.

Secondly, it is critical that the bean containing the datamodel must NOT be in request scope, since that will definitely empty things out - the original copy of the model is detroyed and a brand new one is created.

JSF will handle saving and restoring the form. The only thing you need to do is cache the "before" value if you need to, since the model will have been updated based on the latest values on the form.
Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
My Bean is already in Session Scope

How do I access the dataModel from the bean?
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 15950
    
  19

The dataModel should be a property within the bean. You create an instance of it, wrap your data collection in it, and use that both as the property referenced in the "value=" attribute of the h:dataTable tag and as the object of the getRowData() method in your action processors.

You cannot pass a collection directly to a dataTable tag, because in addition to the actual user data, the tag needs to maintain some context, and in particular, the context that tells the action method which row was the origin of the action request. In JSF 2 I've gotten the suspicion that if you do set the dataTable tag to directly reference a collection, JSF2 may take the initiative and silently build the dataModel for you, but I don't know of any clean and easy way to get hold of that object, so I recommend that you build and initialize the dataModel yourself.
Brendan Healey
Ranch Hand

Joined: May 12, 2009
Posts: 218
Howdy

> You cannot pass a collection directly to a dataTable tag

I thought that Jessie was using JSF 2 from the presence of the f:ajax tags in the code sample. As such he can
pass an array or List (amongst other things) to the h:dataTable value= tag attribute, so I think!

What's the present status on this? To be honest I didn't quite follow the original question but I'm fairly sure
there a simple answer.
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 15950
    
  19

Brendan Healey wrote:Howdy

> You cannot pass a collection directly to a dataTable tag

I thought that Jessie was using JSF 2 from the presence of the f:ajax tags in the code sample. As such he can
pass an array or List (amongst other things) to the h:dataTable value= tag attribute, so I think!

What's the present status on this? To be honest I didn't quite follow the original question but I'm fairly sure
there a simple answer.


That was my impression, but I cannot find any documentation to back it up.

However, one of the things that the TableModel wrapper classes adds is the cursoring ability and its support methods getDataRow() and getRowIndex(). The raw collections have no built-in cursors, so it's impossible to tell which row is under consideration. That's why I continue to use tablemodels even in JSF2. The alternative would be to hack it out via some sort of parameterized AJAX call, which was where we came in. For myself, I prefer to avoid logic-style parameters on my Views, since they can muddy the MVC separation of concerns.
Brendan Healey
Ranch Hand

Joined: May 12, 2009
Posts: 218
Hi Tim, I always use a List<T> unless I'm using lazy loading, when I go the dataModel route. I think that you're
able to pass arrays, lists, objects (scalar!), java.sql.ResultSet, javax.servlet.jsp.jstl.sql.Result BUT you can't pass a
Collection! This is certainly one of those little JSF areas where I found a way to make it work, and stuck to it
(similarly to f:selectItems).

I'm not entirely sure if there is still no Set support.
Brendan Healey
Ranch Hand

Joined: May 12, 2009
Posts: 218
-duplicate- had server error
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 15950
    
  19

I used the term "collection" because I was thinking of other things besides Lists (arrays, for example). But yes, the collection does have to be an ordered collection. List is, Set is not and neither are the Maps. The inherent ordering requirement is because the data sequence has to be repeatable and predictable.

Jessie Mohr
Greenhorn

Joined: Oct 24, 2011
Posts: 9
Brendan Healey wrote:
What's the present status on this? To be honest I didn't quite follow the original question but I'm fairly sure
there a simple answer.


Sorry for not checking back on this. I made a LOT of progress since this post and was able to retrieve the index value of the question using the component id. You can see how I did this on my other post: http://www.coderanch.com/t/560095/JSF/java/Ajax-Listener-event-valueChange-seems

I'm having a whole new set of issues now.
 
 
subject: Passing a Parameter via Ajax
 
Similar Threads
ajax events not triggered in dynamically included pages
Ajax Listener event valueChange seems to be firing onClick instead of onChange
Input field rendered by Ajax cannot be used to input values?
Default selection of Radio Button when added to h:panelGrid
JSF - Missing source code using templating and composition