I did some tests about what can we do about it and based on all information
gathered I reached some conclusions. This is not an easy topic but it is
very important, so below there is a full explanation about what it going on.
Doing some tests, I notice the solution for:
Fix UIData state saving model (spec issue 153)
Which was included as a new feature in JSF 2.1 and included in tomahawk
for JSF 2.0 too, uses the container client id to identify:
- Which dataModel is bound to the component (using
- Which state belongs to a row.
Why? Suppose this case where a nested dataTable is used:
In this case, the inner dataTable is used across rows of the other dataTable.
If the outer dataTable has two rows, both rows share the inner dataTable
In other words, in the component tree there is just one inner dataTable
instance. The reason to do that is just for performance. To do the magic and
handle nested dataTables correctly, internally we use a Map<String, Object>
(see HtmlDataTableHack _rowStates or _rowDeltaStates), and to identify
which "virtual" dataTable instance is we use the container client id as a
key. Note the container client id has the rowIndex of the outer dataTable, so
that makes easier identify the state that belongs to an specific row, and
do not mix the inner dataTable dataModel and row state.
The first conclusion is to identify the dataModel and row state we can't use
an arbitrary identifier that does not take into account the component tree
structure. So, the direction to take is do something in
getContainerClientId() method. This conclusion has one side effect: the
identifier should warrant to be the same for the specified row. Later I'll
explain why is this so important.
On the other hand, a html id must comply with the following conditions:
1. Must begin with a letter A-Z or a-z
2. Can be followed by: letters (A-Za-z), digits (0-9), hyphens ("-"),
underscores ("_"), colons (":"), and periods (".").
3. Values are case-sensitive
The first condition is warrant by the id of the dataTable (because it is
a naming container). The second one must be valid for any string to be
appended on the container client id. The important thing to note is there is no
way to override this condition.
Other consideration to take into account is if the implementation requires
to use the client id and retrieve the derived rowKey'. After checking the
code the conclusion is the only point is on invokeOnComponent, but only as
a 'hint' to find the target component. It is not possible to use a combination
of rowKey' and rowIndex, because that violates the condition about the
identifier should warrant to be the same for the specified row. I tried it
and causes the state to be lost, and the problem becomes critical on nested
The second conclusion is it is not necessary to provide a conversion from
rowKey' to rowKey, because there are not use cases that requires it. If from
the client side some change happens on multiple rows, the ids for each row
must be passed on the ajax refreshing operation or just refresh all table.
If it is not required to convert from rowKey' to rowKey, is it necessary to
convert from rowKey to rowIndex? In practice the answer is no. Since we are
considering rowIndex could change between requests or inclusive on the same
request (dataModel is reloaded on each encodeBegin()), for invokeOnComponent
and visitTree it is required to traverse all rows and then check if the
row match with the required rowKey'.
Really, use a rowIndex for UIData is sometimes enough simple and convenient,
but in fact it is a design flaw of UIData. Frameworks like Trinidad has done
something different in this case (UIXCollection, UIXIterator ....) and proposed
other interfaces to deal with the limits between the model and the view. The
intention here is do not solve that problem, instead is try to enhance the
current JSF standard solution as much as we can.
The solution proposed for ajax stuff ('row' virtual component) is the right
thing to do in this case. From Tomahawk point of view, a solution like
RichFaces ajaxKeys or Trinidad is an unnecessary complexity, but note both are
valid (different frameworks could propose different solutions).
Therefore, the changes to be done are:
1. Add this methods to HtmlDataTableHack:
- Used to assign a value expression that identify in a unique way
a row. This value
- will be used later instead of rowIndex as a key to be appended
to the container
- client id using getDerivedSubClientId() method.
public Object getRowKey()
public void setRowKey(Object rowKey)
- Return the fragment to be used on the container client id to
- identify a row. As a side effect, it will be used to indicate
- a row component state and a datamodel in nested datatable case.
- The returned value must comply with the following rules:
- <li> Can be followed by: letters (A-Za-z), digits (0-9), hyphens ("-"),
- underscores ("_"), colons (":"), and periods (".") </li>
- <li> Values are case-sensitive </li>
protected String getDerivedSubClientId()
2. Change getContainerClientId and use getDerivedSubClientId() to retrieve the
rowKey' value and append it.
3. Update the necessary stuff on invokeOnComponent and other parts that
I did some test and it works well. If no objections I'll commit this