I've found some sources for jsf/primefaces/ee-api/glassfish etc. to debug the behavior, so here's the answer:
In short
If a component
:
- Triggers an action (
controller.method
) that causes redirect - And is placed in a
datatable
- And the
datatable
generates its rows based on a@ViewScoped
bean
Then:
- After the
controller.method
invocation the@ViewScoped
bean that thedatatable
depends on will be regenerated (with all its dependencies of course)
Tested: In version 2.1.7
of JSF.
Looked in sources of 2.1.19
, and I'd expect same behavior there.
Details
For those who cry out loud in lonely summer nights asking: "Why?"
The chain of "Events" that lead to this behavior (with references to sources):
- The user clicks a button inside a table row.
- Data is
POST
ed to the server - Phases 1-4 go as planned
APPLICATION_INVOCATION
- The click event is received by JSF. Important: The click event that references the button is wrapped in an event that contains information about the table & the row number the click happened on. For simplicity:
rowEvent & clickEvent
- The event is "broadcasted" in the tree of the components @
UIViewRoot:794
javax.faces.UIData
The grandparent oforg.primefaces.component.datatable.DataTable
backingp:datatable
starts processing the event @UIData.broadcast(FacesEvent)
- The
broadcast
method first saves the index of the last selected row - Then it selects the one specified by the
rowEvent
- Dispatches the
clickEvent
on the childUIComponent
, in our case on theButton
- Everything is well & fine, and the event starts getting processed by
ActionListener.processAction(ActionEvent)
- This in turn invokes
controller.method
which returns a redirectString
and things begin to go downhill - At the end of the method the
redirectString
is processed by aNavigationHandler
- This one seeing that we're about to redirect quickly clears the
ViewMap
removing all@ViewScoped
beans from it at line179
. Which if we think about it is kind of logical, since we're on our way out.
- This one seeing that we're about to redirect quickly clears the
- This in turn invokes
- Everything is well & fine, and the event starts getting processed by
- On arriving back in
UIData.broadcast
which- having broadcasted the inner event,
- not knowing that some inner event caused a redirect and everything it does will be thrown to the garbage (because of
302
) - as a last action, tries to select the row whose index it saved at step
4.3.1
- And of course to select a row, it needs to know the data for it, and this is where the
@ViewScoped
bean(s) needed by the table get regenerated.
- The
- The click event is received by JSF. Important: The click event that references the button is wrapped in an event that contains information about the table & the row number the click happened on. For simplicity:
THE END
Notice
Though I haven't tested I'd expect the same behavior h:datatable
, p:accordionPanel
, p:carousel
, p:galleria
, p:dataGrid
etc. In short every component that subclasses UIData
and doesn't provide a redirect
- aware broadcast
method.