passing datatable object var as parameter in EL expression: how to make it robust? - jsf

In Core JavaServer Faces we find the following example for deleting rows out of a DataTable, where tableData.names is a list of Name objects:
<h:dataTable value="#{tableData.names}" var="name" ... />
<h:commandLink value="Delete" action="#{tableData.deleteRow(name)}" />
</h:dataTable>
It comes with a warning that this may not delete the correct row if we use request scope and the list changes "between the rendering of the table and the decoding of the response".
CAUTION: If the value of the data table has request scope, be sure that the data does not change between the rendering of the table and the decoding of the response
(page 226 of the 3rd edition)
Can anyone explain that quote in terms of the JSF lifecycle? If "render response" is the final phase in the JSF lifecycle, where does "decoding the response" fit in? Do they mean the decoding of the following postback REQUEST (which sends a generated ID by which to identify the row and hence the name object)?
And: how can we implement this in a more robust fashion?

The method expression is not evaluated during the request of displaying the form (the encode step as the book is talking about), but during the request of processing the form submit (the decode step as the book is talking about). The inputs and actions of the datatable row are determined on basis of the table's row index. During processing of the form submit, JSF re-iterates over the data model in order to find the submitted values and the invoked actions.
So, if the <h:dataTable value> is tied to a request scoped bean and thus the data model is reinitialized on a per-request basis, then you'll risk the #{item} to actually reference the item at the wrong index during the processing the form submit, because between the requests of displaying the form and submitting the form, the DB might have retrieved a new item, or have removed another item, which would potentially move the desired item to a different index.
To avoid this, the bean needs to be placed in the view scope so that exactly the same datamodel which is initialized in (post)constructor of the initial request will be preserved across postbacks without the need to reinitialize it during the beginning of every single request and thus potentially containing different items or in a different order. The influence is bigger if loading of the datamodel is tied to a specific request parameter under full control of the enduser like a search query.
An alternative, which is actually a bad one in this particular non-idempotent "Delete this item" case, but a good one for the idempotent "Edit this item" case, would be to use a GET link instead. The desired item is immediately rendered as query string parameter of the <a> element.
<h:link value="Edit" outcome="edit">
<f:param name="id" value="#{item.id}" />
</h:link>

Related

JSF best practices : One form for the whole application or a few smaller forrms?

What is the best practice in JSF , to create one main <h:form> in template for example and put all content inside or to have a few forms for each parts, for example if we have two logical blocks of code on the page we should have wrapped these blocks in 2 separate forms ???
This is an overly broad question, and it as well has nothing to di with JSF. It is a question of HTML understanding and design.
If you abstract away all of the JSF details, you'll find out that the rendered component is a plain HTML <form>. To recall the specification, a form is:
a section of a document containing normal content, markup, special elements called controls (checkboxes, radio buttons, menus, etc.), and labels on those controls. Users generally "complete" a form by modifying its controls (entering text, selecting menu items, etc.), before submitting the form to an agent for processing (e.g., to a Web server, to a mail server, etc.)
So ultimately a whole form will be submitted to the server. In case of a component-based framework, like JSF, the bound values of components will populate the backing beans with the submitted data, in case there are no errors.
But what data will be submitted? All data that is within a form. So, if you have one global form that means that all unrelated pieces of information will be collected: like user credentials, product choices, selection of preferences, etc. And it will be done all at once. Most probably it won't yield the desired functionality, but will produce unexpected results.
The problem there was mixing of concerns: one form contained non-related data that was not designed to be submitted together. This leaves us with a one-form-per-user-interaction, like one form for user authentication/logout, another form for storing user preferences, yet another one for keeping product choices, etc. One global form also is likely to leave you with nesting forms problem, taking you some time to figure it out.
Regarding the choice of where to put that form, it is as well depending on the logical separation: if you have user login/logout on every page it could be placed in master template. Per-view forms are likely to be put in template client. Moreover, you'll find it convenient to sometimes put them side-by-side to distinguish the functionality.
Also, form may alter the expected behaviour. One final example: user choice of products. Consider two cases.
The first one:
<h:form>
<ui:repeat value="#{bean.purchasedProducts}" var="product">
<h:outputText value=#{product.name}/>
<h:inputText value=#{product.quantity}/>
<h:commandButton value="Change"/>
</h:dataTable>
</h:form>
The second one:
<ui:repeat value="#{bean.purchasedProducts}" var="product">
<h:form>
<h:outputText value=#{product.name}/>
<h:inputText value=#{product.quantity}/>
<h:commandButton value="Change"/>
</h:form>
</h:dataTable>
While the first example will update all products, the second one will update only the relevant product.
Finally, there is no 'best practice' or 'universal solution'. I think that it's best to depart from separation of concerns when designing an HTML document.
And, yes, I'm voting to close the question.

pass parameter from f:selectItems in h:selectOneListbox

I have a selectOneListbox that, when clicked, should pass an additional parameter (id) to the server. As it is now, the user sees a list of names and when they select one I can get the name. But, each name also has a unique id associated with it that I don't want the user to see - how can I pass the unique id of the selected name to the backing bean without the user ever seeing it? Is it possible? I was trying to figure out how to use the f:param but I don't see how that will work here.
<h:selectOneListbox id="listBox" value="#{ScheduleMB.clients}" size="5"
rendered="#{ScheduleMB.showClients}" >
<f:selectItems value="#{ScheduleMB.clientList}" var="c"
itemLabel="#{c.lastName} #{', '} #{c.firstName}" itemValue="#{c.lastName}" />
<f:ajax event="click" listener="#{ScheduleMB.clickListener}"
render="group" />
</h:selectOneListbox>
The <f:param> serves a different purpose. Even if the <f:param> was possible, it would still end up being visible in the generated HTML output. The enduser would just do rightclick and View Source and then see the IDs being definied as option values.
Your best bet is to retrieve the ID from the DB based on a different unique identifier, perhaps the unique combination of firstname+lastname.
It does by the way not make any sense to me why you would like to hide the ID from the output. It'd be so much easier if you used that as option value, even more if you used a converter so that you can just pass the whole #{c} as option value. The enduser can't spoof/change it in any way. JSF will revalidate the submitted value against the list of available options (which are definied in server side anyway).

#PostConstruct and commandButton/commandLink parameters case

I'm wondering if there is a common pattern for the following scenario.
Let's say I have one JSF page backed with one request scoped bean.
I want to fech all data rows from a database table when a user enters this page.
The same JSF page contains a form to provide query criteria. When the user provides query criteria and submits the form, I want to display the result on the same page, too.
The suitable place to fetch all rows at page entry is the #PostConstruct method. This is a nice place to do that since additional (injected) request parameters are already available here and can be used in the query. However, parameters submited from the form are not available yet. They can be accessed in the action method.
If the user queries the database table using the form criteria the database will be queried twice in this case. The request scoped bean will be recreated and #PostConstruct method fetching all rows will be called prior to the form action method fetching what the user wants.
Of course I could redirect the form result to another JSF page backed by a different bean with DB query only in the action method. But is there a way to accomplish fetching only what is needed with one JSF page and one managed bean?
What you should use is a combination of ViewScoped managed bean and ajax engine. You're right about using #PostConstruct method to fetch all initial rows of your data table. Any further requests from your query form will not trigger this method again. Instead, you only need to make an ajax call to sort out the entries in your data table and update it at the end of the call. It would be something like this:
<h:dataTable id="myTable">
...
</h:dataTable>
<h:form>
...
<h:commandButton actionListener="#{viewScopedBean.sortEntries}">
<f:ajax render="myTable" execute="myForm" />
</h:commandButton>
</h:form>

JSF Required=Yes not working inside a datatable?

I searched everywhere but could not find a solution to this. I am trying to used
required=yes to validate whether a value is present or not. I am using it inside inputtext.
The problem is it does not work inside a datatable. If I put the text box outside the datatable it works. I am using JSF 1.7 so I don't have the validateRequired tag from JSF 2.0.
I even used a validator class but it is still not working. Does anyone know why does required=yes or validator='validationClass' inside a inputtext inside a datatable is not working.
I appreciate the help.
Thanks.
First of all, the proper attribute values of the required attribute are the boolean values true or false, not a string value of Yes. It's an attribute which accepts a boolean expression.
The following are proper usage examples:
<h:inputText required="true" />
<h:inputText required="#{bean.booleanValue}" />
<h:inputText required="#{bean.stringValue == 'Yes'}" />
As to the problem that it doesn't work inside a <h:dataTable>, that can happen when the datamodel is not been preserved properly (the datamodel is whatever the table retrieves in its value attribute). That can in turn happen when the managed bean is request scoped and doesn't prepare the datamodel during its (post)construction which causes that the datamodel is null or empty while JSF is about to gather, convert and validate the submitted values.
You need to ensure that the datamodel is exactly the same during the apply request values phase of the form submit request as it was during the render response phase of the initial request to display the form with the table. An easy quick test is to put the bean in the session scope. If that fixes the problem, then you definitely need to rewrite the datamodel preserving logic. You could also use Tomahawk's <t:saveState> or <t:dataTable preserveDataModel="true"> to store the datamodel in the view scope (like as JSF2's new view scope is doing).
Finally, JSF 1.7 doesn't exist. Perhaps you mean JSF 1.2?

rendered attribute on inputText

I have a search form tied to a backing bean that contains 4 input text fields. The design i am working from indicates that the user should be able to see the search results, but they should not be editable. i decided to use the rendered attribute to show the inputs if the managed bean is empty, and to show an output text tag if it's not:
<t:inputText styleClass="inputText" id="name" rendered="#{not searchCriteria.fieldsEntered}"
value="#{searchCriteria.name}" autocomplete="off"></t:inputText>
<h:outputText value="#{searchCriteria.name}" rendered="#{searchCriteria.fieldsEntered}"></h:outputText>
The display part works correctly, but I am noticing that only the first field is stored in the managed bean when more than 1 search field is entered.
I removed a rendered attribute from an inputText, and sure enough that's causing my problems. I can infer what's going on here, but I don't understand why.
I believe in this situation I will just remove the outputText tags and change rendered to disabled. I am just curious why my initial plan is incorrect.
The rendered="false" will cause the input element not being rendered and thus its value will not be submitted to the server side. If you're using a request scoped bean, the initial value will not be set. You'd like to either put the bean in session scope or to add a h:inputHidden along the h:outputText which transfers the value to the subsequent request.
Since you're already using Tomahawk's t:inputText I'd suggest to rather use its displayValueOnly attribute instead of the rendered attribute and a complementary h:outputText.
In a nut:
<t:inputText displayValueOnly="#{searchCriteria.fieldsEntered}" ... />

Resources