Accessing dynamic UIComponents in JSF Managed Bean 2 - jsf-2

I have 6 columns in my dataTable and I am particularly interested in two InputText: I must prevent the possibility of writing in both inputText on the same row. Either is written into one or the other, but not both.
The solution I found is to implement a Validator and browse the dataTable or specifically visiting the tree of components with UIComponent#visitTree() on UIData as advocated by BalusC here. My question is: how do I know on which row I am? How can I retrieve the value of a InputText specifying the row ?
My goal is to validate two InputText relative to another. When one has a value, the other must be null. And reciprocally.
If you have another solution, I'm interested.
Thanks for your help.

Related

Vaadin 14 TreeGrid - Cannot add the same item multiple times

Working on a project that will display hierarchies of "tasks". I'm running into a problem where it will not allow for multiple entries of the same object. From what I can tell, the "duplicate" item is under a different parent.
The domain data allows for this - a given task may appear in lots of places.
It would seem that this is intentional (maybe), but is there a way around this?
It's intentional to a degree; each Grid and TreeGrid data item is expected to be unique. You could work around this by creating your own implementation of the hierarchical DataProvider class (for example extend AbstractHierarchicalDataProvider) which overrides the getId method along with the other required methods. The return value of this method needs to be unique per item, as it's used as a hash key.
Well, this is probably not the best solution, but it works.
I added a field to the abstract super class that is initialized with the current time (long ms). When I am adding items to the tree grid, I check to see if the tree contains the item and if so, I randomize the field and then add it. The new field is marked #Transient so it's not persisted.

Wrong selected option for selectOneMenu with POJOs and Converter

in our company we're hitting a serious problem which we think is a serious design flaw of the JSF spec, if it is the normal behavior.
This is our usecase:
SelectOneMenu (standard JSF or primefaces, doesn't matter, same behavior)
SelectItems with a database entity as it's value and a string as the label
A converter (via attribute on the selectOneMenu) which translates the entity to its ID (getAsString) and from the ID to the entity (getAsObject)
Everything works as expected as long as the entity inside the value attribute of the selectOneMenu is loaded using the same entityManager as the entities inside the selectItems. We have the same POJOs and therefore the same hashcode (Object#equals() returns true). But as soon as one of the entities is loaded via a different entityManager and therefore has a different hashcode, we are never able to get a match to generate the expected selected attribute of an select item (HTML <option /> ).
The cause of this behavior is, that the JSF-impl and primefaces both use the POJOs in the call
boolean selected = isSelected(context, menu, itemValue, valuesArray, converter);
for itemValue and valuesArray. The implementation of isSelected relies on Object#equals() for POJOs. In my opinion it should always use the value of Converter#getAsString if we have a Converter and pass it to isSelected. This is also the behavior for a POST request. Here we got a submittedValue for the selectOneMenu which is compared to the converted value of the POJO (Converter#getAsString).
Now the question:
Is this the expected behavior as it's described in the spec? Isn't the output of the converter the better way to handle this comparision? Now we have to modify our entity classes and overwrite the equals method to be able to use this construct.
Your mistake is that you forgot to implement/autogenerate equals() (and hashCode()) method conform the contract. This is beyond control of JSF. Pointing the accusing finger to JSF isn't making any sense.
It's handy to have a base entity where all your entities extend from so that you don't need to repeat the task over all entities (even though the average IDE/tool can easily autogenerate them). You can find an elaborate example in 2nd "See also" link.
See also:
Validation Error: Value is not valid
Implement converters for entities with Java Generics
I believe that this is the correct behavior. On Java side the values of the component and selection are POJOs. You have full control of the logic of their equality etc. It should not matter how it is converted to UI display and back. As a component user you should not bother to know how the POJO is displayed. As text, icon, color, whatever. The Java side of the component undestands and communicats with POJOs
YOUR CODE ----------> JAVA COMPONENT --------> CONVERTER ----------> HTML
Also I guess that relying on reference equality for entities is a road to problems. Setting equals/hashCode to use actual ID is the best approach.

Dynamic columns with c:forEach, ace:dataTable and IceSoft Wiki example

We are facing a problem with dynamic columns. We have a table where columns depend on some filters previouosly selected. When you enter the page you can select year and some other criteria, and with these values we render the table. Our problem is that columns may vary when you select different criteria, and this is a problem because c:forEach is not so much dynamic.
If you look at the example provided in IceSoft Wiki you can see something similar to what we have in our code (but our code is much more complicated):
http://www.icesoft.org/wiki/display/ICE/DataTable+Dynamic+Columns
The problem comes with this sentence:
<c:forEach items="#{backing.columns}" var="colModel">
backing.columns is static. But if you change its number of elements (in this example it makes no sense because values in "columns" List match to properties in Task class, but if you are printing a List instead of List) you have a problem, as described here:
http://drewdev.blogspot.com.es/2008/08/cforeach-with-jsf-could-ruin-your-day.html
We've tried to recreate component list when we change columns with:
component.getChildren().clear(); //component is of UIComponent type
But didn't work. Also we've tried restoring view from context in a PhaseListener, and no positive results. And we've run out of ideas.
Any idea (or solution :D) would be appreciated. And if someone need more specific code, just ask.
TIA.
PS: This question is also posted in ICEFaces Forum (http://www.icesoft.org/JForum/posts/list/0/21842.page#76787), and I will update with solution (if any) both places.
We've solved the problem, as we've been suggested at IceSoft Forum, redirecting navigation to the same page in order to get a completely new component tree. For this you need your state to be in a bean that will survive that, but since our backing beans are usually viewscoped this is not a problem. To achieve this, we've changd valueChangeListener method that was changing the list behind the c:foreach and used an action method, and in this action method we're returning null as navigation rule to get the page reload.
See more at: http://www.icesoft.org/JForum/posts/list/21842.page#sthash.sXtPazmS.dpuf

Generating unique ID attributes in datatable with JSF2 and IceFaces2

I'm trying to use a code which looks like :
<ice:dataTable id="revisionDocuments" value="#{agendaBean.agenda.revisionsDocuments}" var="revision">
<ice:column>
<ice:inputText value="#{revision.sequenceAdresse}" id="revisionSequenceAdresse#{revision.index}" />
</ice:column>
I'd like to have a different id for my form fields. The revision object do contains an "index" field, representing the index of the object in the List. I want to see it appears in the id. However, nothing happens. The #{revision.index} expression is never interpreted (getIndex() on the revision object is never called).
You'll tell me JSF already make something that looks like :
revisionDocuments:0:revisionSequenceAdresse
revisionDocuments:1:revisionSequenceAdresse
revisionDocuments:2:revisionSequenceAdresse
True, but this affect only the clientId generated in the HTML. The UIComponent representing the form fields (in the ViewRoot from FacesContext) have all the same "id" AND "clientId" (yes, event if the HTML contains "revisionDocuments:0:revisionSequenceAdresse", the "clientId" you will find in ViewRoot is revisionDocuments:revisionSequenceAdresse).
Someone can help with my try ?
Thank you very much, any help will be greatly appreciated.
The component IDs are to be determined during view build time, not during view render time. The #{revision} is not available during view build time, so it always evaluates empty. Basically, you need to bind it to #{agendaBean} or something else which is already in the scope during the view build time. The component ID is specific to the component itself, not to its generated HTML output. You can't assign multiple different IDs to physically the one and same component.
But you actually don't need to fiddle around this approach. Your concrete problem for which you thought that this is the solution is already answered in your previous question: JSF2 + IceFaces 2 - Retrieve UIComponent from ViewRoot.

Binding to multple datasources in Silverlight with String.Format

I am trying to bind a label 2 (or more!) fields in a dataset in Silverlight 4. I get a localized string out of a resource file and do a String.Format on it like so:
<TextBlock Name="lblTotals" Text="{Binding TotalItems, StringFormat='You need \{0\} items and \{1\} products.'}" />
This works fine with 1 item but there's no way of doing multiple binds in SL4 it seems.
I found some blog posts on how to bind a single element to multiple fields but it does not seem to support the String.Format part which is critical.
The last caveat is that it is bound to an ObservableCollection, so when these fields change in the data the UI must update too.
Any suggestions? Thanks!
I found a solution here using a converter and binding to the whole object and passing in the string as a converter param.
Then the totals did not update when the grid values updated (despite being linked to OnPropertyChanged) - this was the solution hack here.

Resources