tags:

views:

297

answers:

2

Hi,

I am creating a JSF application. I have some items (e.g. products) from database and I want to create a JSF page for editing particular items, that is:

  • it should display selected item properties and enable user to edit them,
  • I want to be able to view this item with some link,
  • I want JSF to somehow remember that I'm editing particular item (e.g. after editing its data it should display this item page)

I have a problem with storing/passing id of item being edited. I saw that in sample JSF CarDemo application they store item (car) being viewed in session. I don't want to do this because I want user to be able to edit different elements in separate browser tabs.

I tried several approaches:

  • using some (e.g. itemId) GET parameter in URL, but it makes it difficult to go back to item page after editing data (to-view-id field in faces-config.xml can only contain constants),
  • using some backing bean managed-property and passing its value in each hyperlink and in forms (by adding hidden field)

The problem I still can't eliminate is that if after editing some item properties I try to save them and validation (e.g. f:validateLength) fails the page is reloaded but id of item being edited is lost. I think it is quite standard task when creating web applications (e.g. user edition, store products edition) so there certainly should be some solution.

Thanks in advance.

+3  A: 

Related info:

See this answer for sample code that allows editing while preserving an ID (the information should still be applicable if you're using JSPs instead of Facelets). See this answer for a few more techniques you might like to make use of. See this answer for the options for preserving the parameter in the URL.


To solve your problem, create a request-scope bean to perform the actions:

//pseudo-stubs
//request scope: #{editor}
public class Editor {
  public Integer getId();
  public void setId(Integer id);
  public String save();
}

(You can add other properties as appropriate.)

Populate the id from the request parameter when you navigate to the page via injection in faces-config:

<managed-bean>
  <managed-bean-name>editor</managed-bean-name>
  <managed-bean-class>foo.Editor</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
    <property-name>id</property-name>
    <property-class>java.lang.Integer</property-class>
    <value>#{param.id}</value> 
  </managed-property>
 //etc

Use a hidden field to preserve the id:

<h:inputHidden value="#{editor.id}" />
<h:commandLink action="#{editor.save}" value="save" />

Any time the form is submitted, this value will be sent; if validation fails, the hidden field will still be rendered with the id value. If validation succeeds, the editor bean will be populated with the id prior to the invocation of save.

McDowell
Actually I tried this before, but it still does not store id when validation fails. Probably I'm doing something wrong, if you could explain why "if validation fails, the hidden field will still be rendered with the id value". From what I tried it looks like when validation fails the GET parameter is removed and lost.
pazabo
View the HTML source to verify that the hidden field is initially populated with the ID. Any time the page is submitted, the hidden control will submit this value. When validation fails, editable control renderers write out the last submitted value so the id should be preserved for the next submit. _(Anything else, say an `outputText` bound to `#{editor.id}` will appear empty because the bean will not be populated when validation fails; the validation phase stops junk getting into the model)._
McDowell
Oh, yes, you're right. Do you know how to catch these values? I mean previous/not validated values? I wanted to use them in other places too (I have a list of items available for edition on the left). Is it "normal" way to do this in JSF - I mean if experienced JSF developed would do it like this? Or are there any builtin functionalities for this? Thanks a lot!
pazabo
If you need a submitted value in the render phase, you might be able to use a `param` on a `commandLink` (e.g. `<f:param name="id" value="#{param.id}" />`). This would make the initial URL param available to use on an `outputText`, for example. Beyond that, (core) JSF does not do any "magic" that makes it different from any other HTTP request.
McDowell
Just to be clear: JSF validation stops junk getting into the model; if you need to use data some other way, don't use JSF validation; do validation in the action. I see no reason to tie other forms of validation to the JSF API, so there is no reason to build them into the JSF lifecycle.
McDowell
+3  A: 

Tomahawk's t:saveState does exactly what you want. Just have something like in your page:

<t:saveState value="#{bean.item.id}" />

or if you want to cover all "uncovered" item values:

<t:saveState value="#{bean.item}" />

If you don't want to add another component library for some unknown reasons (I do recommend Tomahawk though, it adds more flexible components on top of standard JSF implementation, e.g. t:dataList, t:dataTable preserveDataModel="true", t:selectOneRadio layout="spread", t:inputFileUpload, etcetera), then you could also use the standard <h:inputHidden> component to pass hidden parameters from request to request (it renders an <input type="hidden">). One caveat is that you will still lost the values when the validation phase fails. But this can be workarounded by using the component binding instead of the value.

private HtmlInputHidden itemId = new HtmlInputHidden();
private Item item = new Item();

public void editItem() { // Action method when selecting an item for edit.        
    itemId.setValue(item.getId());
}

public void saveItem() { // Action method when saving edited item.
    item.setId((Integer) itemId.getValue());
}

and in the JSF page have the following in the same form:

<h:inputHidden binding="#{bean.itemId}" />

Hope this helps.

BalusC
Thanks, that's exactly what I was looking for :)
pazabo