1

I have a page with a dataTable, which is populated based on the query parameters (e.g., username and pagenum). Each entry in the table has a delete commandButton

When the pagenum != 0 and we click delete, the list of records to display is generated during the "apply" phase. During this phase the view parameters have not been set, so the list of records is empty so nothing get's deleted (our delete method doesn't get called)

To work around this I've added a @PostConstruct method that retrieves the query parameters from the Servlet request and sets the values in the bean, so they are available when we get the list of away records, which allows my delete method to be called.

I'm certain that JSF has a better way of handling this scenario and the @PostConstruct work around is a hack.

What is the correct way to implement this scenario, without resorting to a View or Session scoped bean?

Surely there must be a way to just POST the form and delete the appropriate record without having to waste time regenerating the list of records.

Tiny
  • 27,221
  • 105
  • 339
  • 599
Tom Howard
  • 6,516
  • 35
  • 58
  • 2
    Why don't you want to use `@ViewScoped` for your bean? `@SessionScoped` is not the best fit for this scenario. – Luiggi Mendoza May 05 '14 at 14:48
  • 2
    Could you post your code piece? As @LuiggiMendoza says, the easiest is to go with ´@ViewScoped´, which will make you able to maintain the state of the table. – Aritz May 05 '14 at 16:01
  • It' counterintuitive to me to store state in the server (as in the `@ViewScoped` bean) in this scenario. If I wasn't using JSF, then it would be easy to implement a form on each record, that when submitted would call the appropriate code to delete the record, without having the maintain any session state on the server. Help me understand why using `@ViewScoped` bean is a good thing to do here. What happens if a user loads the page, goes of and has lunch, submits one of the delete forms, but the session has expired? – Tom Howard May 05 '14 at 21:16
  • Or at least help me understand why it makes sense for JSF not to set the query parameters when rebuilding the view prior to invoking the delete method. – Tom Howard May 05 '14 at 21:16
  • Not to mention that Spring doesn't have a View scope – Tom Howard May 07 '14 at 08:06
  • Is this a Spring managed bean? I've never done Spring, so I know nothing about it and usually ignore `[spring]` tagged questions. Before posting a potential answer: can you use CDI in a Spring managed bean? There's a CDI based solution readily available for this situation. On an unrelated note: why exactly are you using Spring instead of CDI to manage beans? – BalusC May 08 '14 at 05:27
  • @BalusC: If you have some time to spare, in case CDI doesn't work with Spring, I'd still love to read your solution with CDI :). – Mr.J4mes May 08 '14 at 06:14
  • @TomHoward: Your questions in the comment sounds like: `I am riding a bike, can you explain why pushing the pedals is a good thing to do`. If you read about JSF lifecycle, you'd have an idea about why `@ViewScoped` is the best one for your case. In general, you shouldn't decide to use JSF and then seek a *correct* solution that go against how JSF generally works (e.g. maintain state on the server, etc.). Is there a way to move the bike without pushing the pedals? Yes. Is it the *correct* way? Maybe not. Perhaps, you should consider if JSF is the correct framework for your application. – Mr.J4mes May 08 '14 at 06:52
  • @BalusC yes, it's a spring managed bean. NFI about CDI. It's unlikely we could switch to CDI as client I'm working for would probably be reluctant to change unless I could clearly articulate the reasons and if I had the knowledge to implement such a change, which I don't. – Tom Howard May 08 '14 at 07:42
  • @Mr.J4mes fair call. I'm new to JSF (I'm doing work for a client that uses JSF) and used to the stateless nature of http. I'll read up on the JSF lifecycle, however I find it strange that view scoped is considered the right way to go, when it didn't exist before JSF 2 and doesn't exist in spring (unless you roll your own). How were you all doing this sort of stuff before JSF 2 and how are the other people using spring doing it? – Tom Howard May 08 '14 at 07:50
  • 1
    In JSF 1.x, Tomahawk offered with `` a way to turn request scoped beans into view scoped beans without much effort. Spring people just jumped into all sorts of hacks or custom annotations and then blamed JSF instead of Spring for the fact that Spring isn't aware of the "view scope" concept. Well, your (theoretical) answer is a custom Spring annotation which immediately injects the HTTP request parameter instead of using ``. I only can't answer this in detail off top of head. – BalusC May 08 '14 at 07:53
  • @TomHoward: I believe the motivation for `@ViewScoped` in JSF 2 is exactly because of situations like yours :). Because of the way JSF works, `@RequestScoped` is too small to store a list of objects beyond the initial request for further logic. On the other end, `@SessionScoped` and above are just too big to be used for just one single page. So, `@ViewScoped` was invented. In brief, it's the right way because it was born for that purpose :P. BalusC covered the rest above. – Mr.J4mes May 08 '14 at 10:06
  • @BalusC I was just reading you post on [jsf communication](http://balusc.blogspot.com.au/2006/06/communication-in-jsf.html) and reading the bit there about `@PostConstruct`, it sounds as though the properties that are bound to the query parameters would normally by the time the `@PostConstruct` method is called. Is this a correct understanding? If so, I'm guessing they are not getting set prior to `@PostConstruct` because we're using spring managed beans. – Tom Howard May 08 '14 at 11:26
  • Only if you use `@ManagedProperty`, not ``. And indeed, `@ManagedProperty` is not available to Spring managed beans, only to JSF managed beans. CDI has also no equivalent for this, but JSF utility library OmniFaces has a CDI based `@Param` for the purpose. – BalusC May 08 '14 at 11:31
  • @BalusC This is making sense now. So in order for me to achieve similar behaviour to `@ManagedProperty` with spring managed beans, I **have** to set them from the request query parameters in the `@PostConstruct` method, or similar. This will allow the view to be constructed correctly, and the delete action will be called. Fair assessment? – Tom Howard May 08 '14 at 15:00
  • Indeed. See also point 4 of http://stackoverflow.com/questions/2118656/hcommandlink-hcommandbutton-is-not-being-invoked/2120183#2120183 – BalusC May 08 '14 at 19:48
  • @BalusC brilliant!. Post a brief answer along those lines and the bounty is yours. – Tom Howard May 08 '14 at 20:50

2 Answers2

3

What is the correct way to implement this scenario, without resorting to a View or Session scoped bean? Surely there must be a way to just POST the form and delete the appropriate record without having to waste time regenerating the list of records

Sorry, there's no way. At least not when using a standard <h:commandButton> inside a standard <h:dataTable>. This is the consequence of the stateful nature of JSF. JSF just wants to ensure that the view is exactly the same during processing the postback as it was during generating the HTML output.

This is part of JSF's safeguard against tampered requests wherein the enduser/hacker can manipulate the request parameters in such way that it could do hazardful things, e.g. changing the ID of entry to delete, or bypassing the check on rendered attribute, etc. All those things on which you would/should do additional pre-validation anyway if JSF didn't do that for you and are easily overlooked by starters (they would then blame JSF for being insecure instead of themselves). See also Why JSF saves the state of UI components on server? and commandButton/commandLink/ajax action/listener method not invoked or input value not updated.

In case of <h:commandButton> inside <h:dataTable>, JSF simply needs to have the data model available during the apply request values phase, so that it can iterate over the <h:dataTable> in the component tree in order to find the pressed button and queue the action event. If there's no datamodel, then it can't find the pressed button and the action event won't be queued. Normally, this is to be solved by placing the managed bean in the JSF view scope. See also How to choose the right bean scope?

In case of request scoped beans, the <f:viewParam> is indeed not the right tool for the job of preserving the data model before apply request values phase takes place. You need to do the job in a @PostConstruct annotated method instead. The request parameters can in case of JSF managed beans be injected via @ManagedProperty. See also ViewParam vs @ManagedProperty(value = "#{param.id}"). In case of CDI or Spring managed beans, there's no standard annotation available to inject a HTTP request parameter as a bean property. For CDI, the JSF utility library OmniFaces has a @Param for the very purpose. See also OmniFaces @Param showcase. For Spring, you'd need to homegrow it yourself. I, as non-Spring-user have however no idea how to do that. Google also doesn't seem to reveal much.

Alternatively, you can also just put the bean in the view scope by @ViewScoped. It'll then live as long as you postback to the same view. JSF 2.2 has a CDI compatible annotation for that in javax.faces.view package. The one in javax.faces.bean package is the old JSF 2.0/2.1 annotation for @ManagedBean. Spring has no annotation out the box for this as that would otherwise put a dependency on JSF API. You'd need to homegrow it yourself. Google shows several examples.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
1

What is the correct way to implement this scenario

Before executing the any logic on the backing bean, JSF always have to rebuild the view in order to get information about what to execute. For displaying and updating purpose, the best (and correct) solution is certainly the @ViewScoped.

without resorting to a View or Session scoped bean?

If you insist on using @RequestScoped, I'd say there're no correct ways but work-arounds or hacks. One way is to initialise the list in a @PostConstruct method like you've mentioned. Another way may be to use a JavaScript function for the onclick attribute of your delete button. The JS function, for example, will make a call to the server using a URL to request a delete. Or else, you can also use PrimeFace's RemoteCommand for the JS function.

Mr.J4mes
  • 9,168
  • 9
  • 48
  • 90