Hi
It is probably that myfaces implementation of UIData do something different
that RI. Myfaces uses a map to hold dataModel instances to handle better
the use case when nested datatables occur.
The problems described on the last email are different to the one I'm trying
to
solve. My only intention is make note that it is possible to solve the
problem
described on the original proposal, only do very small changes on the spec
(in other words the scope of the proposal is not that huge, the dataModel
handling is different from the component state handling, but both problems
are related).
Really I would like to see a way to call markInitialState like the one
described.
The current way to call it is a chaos. I had to put this call on different
tag handlers
and the order how this method is called is the inverse one than someone
could
expect. Without this change, if someone wants to do something similar needs
to
call clearInitialState, save and then call again to markInitialState on the
code
that saves the state.
The other change described, a param to indicate when the current view is
using
partial state saving and all calls to markInitialState should occur, to
diferentiate
between the call when the view is built and the call when a row is set is
document
an implementation detail that both myfaces and mojarra contains (I suppose,
otherwise how ComponentHandlerDelegate implementation could know when
do not call this method if the current view is not using PSS ). Note the
documentation
of ComponentHandler just says this method is called but does not says
anything
about the conditions, so the implementation should decide that but there is
no
formal way to comunicate this state, but if you look closely,
ComponentHandler
"delegate" all that steps described on the javadoc, so in reality this class
does
not do anything described there and that can be considered an error in the
javadoc.
All that documentation should be on
TagHandlerDelegateFactory.createComponentHandlerDelegate.
regards,
Leonardo Uribe
2010/3/19 Alexander Smirnov <asmirnov(a)exadel.com>
The problem is far worse then you think :-( UIData itself has
transient
properties, the most important of them is dataModel. Standalone UIData
caches model between phases, but resets it if you have enclosed
iteration components that gives huge impact on performance if data model
does not cache results itself. In Mojarra, UIRepeat knows about UIData
but latest has no clue about repeater, so if you put data table inside
the <ui:repeat> , results will be unpredictable. Is MyFaces has a better
implementation ? I haven't seen deep into its UIData code for a while...
Also, it is permanent pain in the complex components that require to
store some information during request or work like iterator ( that are
really all trees, menus, tab panels, Tomahawk columns, and more ).
Numeric row ids in the data model is additional limitation for models
that connected to the database in concurrent environment there
additional code requires to keep references between row number and
primary key; otherwise application have a chance to update/delete wrong
record.
For these reasons, I think that all UIRepeat/UIData should be rewritten
from scratch as it already done in the Trinidad/ADF. I'll take a closer
look to your patch.
On 03/19/2010 12:07 PM, Leonardo Uribe wrote:
> Hi
>
> Yes, I know this too. In few words, all "transient" properties, that
means,
> all properties that are not stored in the state are not saved. There are
> very
> few examples. To solve this one, there are multiple alternatives. I was
> thinking
> on extend StateHelper interface to add some methods like
> putTransientProperty and getTransientProperty. Trinidad stores all
> properties
> on the FacesBean object.
>
> Anyway, I think an "mixed" solution could solve this problem: create an
> interface called RowStateTransientHolder, so only the components that
> implements this type of attributes could have the chance to
> save and restore its state.
>
> But note in any case, it is necessary to do something like this to solve
the
> use case described.
>
> I didn't knew that localValueSet was a transient property too.
>
> regards,
>
> Leonardo Uribe
>
> 2010/3/19 Alexander Smirnov <asmirnov(a)exadel.com
> <mailto:asmirnov@exadel.com>>
>
> I've thought about these problems for a long time too. Your proposal
has
> a disadvantage because per-row state is quite different from the
> component state. Take a look for the UIInput implementation:
> localValueSet field is never saved in the state while it should be
vary
> from row to row. The same thing happens to the UIForm.submitted
> attribute, that has been eventually added to the UIData spec instead
of
> common solution, that would save/restore all local variables. My
> proposal was a separate RowStateHolder interface with
> save/restoreRowState methods there component developer can preserve
> internal variables. In the your stenario, there should be some flag
that
> tells component to save all values, even these that are not supposed
to
> be preserved between requests.
>
> On 03/19/2010 10:27 AM, Leonardo Uribe wrote:
> > In short, this topic is an attempt to add full state to components
> > inside UIData. I'll do a brief resume, so people can understand
> this one
> > easily.
> >
> > UIData uses the same component instances to render multiple rows.
> > Suppose this example:
> >
> > <h:dataTable id="cities" var="city"
value="#{country.cities}">
> > <h:column>
> > <h:outputText value="#{city}" />
> > </h:column>
> > </h:dataTable>
> >
> > In the component tree it is created this hierarchy:
> >
> > HtmlDatatable
> > UIColumn
> > HtmlOutputText
> >
> > If we have 10 cities, the same component is used over and over to
> render
> > all 10 cities. The reason to do that in this way is keep state as
> small
> > as possible.
> >
> > Now let's suppose something like this:
> >
> > <h:dataTable id="cities" var="city"
value="#{country.cities}">
> > <h:column>
> > <h:inputText value="#{city}" />
> > </h:column>
> > </h:dataTable>
> >
> > It was changed the output component for an input one. If this table
is
> > in a form and the values are submitted, the same component is used
to
> > apply request values, validate and apply them to the model (update
> > process). To make this possible, UIData class has some code to
> preserve
> > this values between phases (using EditableValueHolder interface),
so
> > when each phase is processed, all rows are traversed and you get
the
> > expected behavior.
> >
> > Now suppose something more complex: We have a code that use
> > invokeOnComponent to change the style of my inputText. In theory,
only
> > one row should change. But in fact, all rows are rendered with the
> same
> > color. Why? because we use the same component to render all rows,
> and we
> > don't preserve the children component state between rows.
> >
> > There is a lot of issues, questions, and side effects related to
this
> > issue, but just to put some of them here:
> >
> > TOMAHAWK-1062 InputTextArea doesn't work properly inside facet
> DetailStamp
> > TOMAHAWK-96 Data table Scroller not working the dataTable which was
> > actually contained in other DataTable
> >
>
https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=153
> > Problems with UIData state saving
> >
> > Also, it is well know that one reason why people uses c:forEach in
> > facelets, is because this one create "full" components per each
> row. It
> > is very easy to find articles on internet.
> >
> > Now, with jsf 2.0 we have partial state saving, so we have a chance
to
> > fix this one once and for all. I tried fix this one per months
(maybe
> > years!), but talking with Martin Marinschek on JSFDays, some ideas
> came
> > out and finally it was found a possibility to fix this one using
the
> > existing api and with little changes on the spec.
> >
> > The proposal is this:
> >
> > 1. Do not call UIComponent.markInitialState() on
> > ComponentTagHandlerDelegate, as ComponentHandler javadoc says,
instead
> > call it after PostAddToViewEvent are published on vdl.buildView().
We
> > need to call it from root to nodes, so the parent component is
marked
> > first. I know the place where this call comes is from trinidad tag
> > handler, but this call needs to be fixed in a more predictable way.
> > 2. Use an attribute on facesContext to identify when the VDL is
> marking
> > the initial state (in myfaces there is already an attribute called
> > "org.apache.myfaces.MARK_INITIAL_STATE"). This is necessary to
> indicate
> > UIData instances that it is time to save the full state of all
> component
> > children,
> > 3. Allow UIData to hold a map where the key are client ids and the
> value
> > are the deltas of all components per row. This map should be saved
and
> > restored.
> > 4. Change UIData.setRowIndex() to restore and save the component
> state.
> >
> > I'll attach a patch on myfaces issue tracker with the algorithm
> proposed
> > (because it is based on myfaces codebase). It was tested and it
works.
> > But note it is necessary to fix the javadoc for
> > UIData.markInitialState(), ComponentHandler and maybe
vdl.buildView(),
> > so the intention is propose this change for jsf 2.0 rev A. Note
this
> > works only with PSS enabled because without it we don't have a
> place to
> > notify UIData instances that it is necessary to get the full state.
> > Also, note this patch preserve backward compatibility, because the
old
> > way to store/save is applied after the full state is restored.
> >
> > Really, I have the strong temptation to apply some similar code on
> > myfaces UIRepeat component (because this class is private), but I
> prefer
> > first ask to EG to know what you guys think about it.
> >
> > Suggestions are welcome,
> >
> > regards,
> >
> > Leonardo Uribe
> >
>
>