javaee / mojarra

PLEASE NOTE: This project has moved to Eclipse Foundation and will be archived under the JavaEE GitHub Organization. After Feb. 1, 2021, the new location will be github.com/javaee/mojarra. Mojarra - Oracle's implementation of the JavaServer Faces specification
https://github.com/eclipse-ee4j/mojarra
Other
164 stars 58 forks source link

NumberFormatException with dynamically adding UIRepeat in a row of datatable #4287

Open laxman3018 opened 7 years ago

laxman3018 commented 7 years ago

Hi all, I'm trying to add UIRepeat programmatically to Primefaces data table row and there is one command button in row and on click of that one dialog opens. After adding the row and on clicking of command button i am getting exception as NumberFormatException launched by javax.faces.component.UIData#invokeOnComponent. Exception is java.lang.NumberFormatException: Trying to extract rowIndex from clientId 'defaultTable:j_id97:expandedrow' For input string: "j_id97"

Environment

Tomcat 8.5, Mojarra 2.3.0

BalusC commented 7 years ago

How does the final component tree look like in XHTML markup? Did you mean to say that you created a <p:dataTable><ui:repeat><p:column><p:commandButton> hierarchy?

laxman3018 commented 7 years ago

issue Added the screenshot

BalusC commented 7 years ago

I was asking for the component tree in XHTML markup, not for its generated HTML output.

cmunden commented 7 years ago

Could you post your facelet? The inspected markup doesn't relay the true intent.

laxman3018 commented 7 years ago

@BalusC @cmunden This will be the final component tree of data table. cf_issue

Also i have attached the HTML file and the class where i am building the uirepeat and adding it to data table. CustomeTableBuilder.zip

cmunden commented 7 years ago

At first glance... the error seems to be happening because rowIndex is not declared in scope. What happens if you add the following attribute

rowIndexVar="rowIndex"

to your p:dataTable component?

As a side note, what you seem to be trying to achieve with your table might be better suited to be handled by the facelet entirely. This would separate your view from the backing data. You would then build the full table component tree in your facelet and use html + css to selectively choose what to render on screen (with display: none to hide things). Then your button in your first column would be simple javascript (onclick) event handlers to un-hide the dialog in the same row. This is much easier if you use jQuery.

This is just a matter of personal preference though. 👍

laxman3018 commented 7 years ago

Thanks @cmunden for some solution I tried adding the rowIndexVar="rowIndex" but still same issue. And other approach we cannot take as out table structure is bit heavy and to make tree structure light so that rendering of table will be fast we are not adding the Ui repeat at start, only when user clicks on button then we add the uirepeat. I am not understanding this same approach was working in JSF 2.2.4 but when i migrated to 2.3.0 then i am facing this issue.

BalusC commented 7 years ago

laxman3018, solving reported issues related to "dynamically added components" is very hard without having a concrete reproducer project, because there are so many ways to "dynamically add components". There are both good ways and bad ways.

One of easiest and most robust ways is using JSTL because it fits nicely into the XML based flow of a Facelet and you can keep using XHTML to declare components instead of Java. Therefore I was asking for the component tree in XHTML markup. If you're unable to provide one because you're actually using Java instead of XHTML to create components and don't know how to reimplement it in plain XHTML, then you'd best provide a reproducer project.

laxman3018 commented 7 years ago

@BalusC I have attached the source project. Request you to please go through once. I am stuck with this issue from long time.

Thanks spring-webflow-jsf.zip

BalusC commented 7 years ago

I'm not familiar with Spring. In the future please use standard Java EE instead of 3rd party frameworks in reproducer JSF projects so that it will end up less confusing.

For instance, I don't know what the default scope is of Spring's @Component annotation which you've there on the CustomeTableBuilder bean. But based on bean's logic, it has definitely to be request scoped (because it's holding UIComponent instances as bean properties). Is this bean indeed request scoped or not? And, I see that the bean is manipulating the component tree during preRenderView and contains a strange method rebuildColumns(). The best event to manipulate the component tree is postAddToView event. The rebuildColumns() should not have been necessary in first place because JSF already takes care of this. This strongly suggests that the bean is actually not request scoped at all.

All in all, I strongly recommend to rewrite this using pure XHTML. It will end up to be much clearer and more self-documenting. You can use JSTL tags to dynamically build the view.

Related reading: https://stackoverflow.com/q/14911158/157882

cmunden commented 7 years ago

@laxman3018 @BalusC

I ran the reproducer project and the scope of code>@Component</code is beyond request scope. I ran it in debug mode and was clearly able to validate that the CustomTableBuilder was the same object reference on each request (with the constructor only being called once).

Also of note, the Exception that is being thrown in this case is not related specifically to clicking the button (nor any of its associated action/event listeners). It is directly related to the view itself and the dynamic aspects of it are causing issues during the Render_Response phase of the life cycle. This can be confirmed by simply clicking the refresh button in your browser while on the view that contains the UIData component.

The error with rowIndex is not related to the EL reference of rowIndex (as I originally thought). It is a coincidence that the Mojarra implementation of the JSF spec has an inline variable also named rowIndex as it is trying to build and decode the view.

More specifically, (and as we all know), in JSF, clientIds are handled by the framework such that every component is given one if one is not provided. And we know that duplicate client Ids are prevented by allowing NamingContainers. Any UIData is also a NaimingContainer (and a special one at that). Since components in a table are repeated row by row, the UIData will treat each row as a NaimingContainer also. Since rows are dynamic naming containers, UIData will simply name each row with its row index.

For example, if you have a table with 3 rows and a single column with a button in it. Suppose you give your table an id of MyTable and you give your button an id of MyButton. The resulting markup will have clientIds similar to this: MyTable:0:MyButton MyTable:1:MyButton MyTable:2:MyButton

In this case, the rendering of the table is failing because one of the dynamically added components has an ID that does not follow this convention. JSF is trying to evaluate the client Ids in the view to determine which row is being rendered, and as such is trying to parse out the 0, 1, 2, etc from the clientId. The error occurs because one of the clientIds in the table does not have the associated row Id in its full clientId.

This is not a solution... so I apologize for that... but I wanted to shed some light on what was actually causing the error and to indicate that you need not actually click the button to experience the error, you simply need to have the view rendered again (clicking the refresh button will do).

I agree also with @BalusC , you will save yourself a bunch of torment if you build your view purely in XHTML and remove any bean (java) code that directly alters the component tree.

Hope this helps in some way.

laxman3018 commented 7 years ago

@BalusC and @cmunden I will try to attach another project with the same issue using pure XHTML and using Standard JAVA EE.

And the problem with our project is in one screen we have very huge set of component added into datatable in each column and that was taking much time to load the page, the dom structure was getting very heavy. so to make page load faster we have excluded some of the component from datatable and adding it manually when client request it.

Thanks for reply , will attach the new sources soon.

edburns commented 6 years ago

Please see this important message regarding community contributions to Mojarra.

https://javaee.groups.io/g/jsf-spec/message/30

Also, please consider joining that group, as that group has taken the place of the old dev@javaserverfaces.java.net mailing list.

Thanks,

Ed Burns