How to refer to dataTable parent within the dataTable? - java

Consider a dummy case:
<h:form id="wrapperForm">
<h:panelGroup id="rowsContainer">
<h:dataTable id="rowsTable" value="#{bean.rows}" var="row" >
<h:column>
<h:commandButton value="Click me to update (#{component.parent.parent.parent.clientId})">
<f:ajax render=":#{component.parent.parent.parent.clientId}" />
</h:commandButton>
</h:column>
</h:dataTable>
</h:panelGroup>
</h:form>
On button click, the id=rowsContainer gets successfully updated as it should.
However, if I add ui:repeat there, it does not work anymore:
<h:form id="wrapperForm">
<ui:repeat id="blocksRepeat" var="block" value="#{bean.blocks}">
<h:panelGroup id="rowsWrapper">
<h:dataTable id="rowsTable" value="#{block.rows}" var="row" >
<h:column>
<h:commandButton value="Click me 2 update (#{component.parent.parent.parent.clientId})">
<f:ajax render=":#{component.parent.parent.parent.clientId}" />
</h:commandButton>
</h:column>
</h:dataTable>
</h:panelGroup>
</ui:repeat>
</h:form>
Instead, this gets:
<f:ajax> contains an unknown id ':wrapperForm:blocksRepeat:0:rowsWrapper' - cannot locate it in the context of the component j_idt363
However, that component is really there with that ID, so the EL should be ok. Somehow the ui:repeat breaks the case. Is it possibly trying to evaluate the EL before the actual loop?
How do you refer to the rowsWrapper element from within the dataTable?
Note: I recently asked about odd dataTable naming within ui:repeat, which turned out to be a bug. This issue should not be related to that, however, as I am wrapping the dataTable within a panelGroup as suggested here.

There are actually two things going wrong:
1) ui:repeat is broken
As answered by BalusC in the comments of the question, the first issue occurs (once again) due to a bug in Mojarra. It seems ui:repeat is so broken even the wrapper container holding the h:dataTable does not help. For more details, see question "Why doesn't h:dataTable inside ui:repeat get correct ID?" and the comments of this question.
As BalusC suggests, a workaround is to use h:dataTable instead of ui:repeat. It will provide unconvenient (<table>) HTML but works. This removes some odd issues when adding and removing rows to/from the inner iteration.
Note: some issues regarding ui:repeat seem to be fixed in Mojarra 2.0.3 but not all.
2) The references just fail
Even with h:dataTable workaround, the reference to the inner h:dataTable from the button inside it fails. As there is no ui:repeat in use, this must be datatable's internal issue. I don't see any solution at the moment, so I filed a ticket for this behavior as well.

Related

PrimeFaces filling dataTable with List<Object> inside another List<Object>

UPDATE:
I found that the issue with the dataGrid not looping through a list inside a list was an issue on the XML code side. Now the dataGrid is reading the List and looping through it creating panels based on the name. The issue now seems to be accessing certain properties in this List.
<p:dataGrid value="#{objList.objList2}" var="objList2" columns="1">
<p:panel id="obj2" header="#{objList2.name}" toggleable="true" collapsed="false">
<h:panelGrid columns="2" style="width: 100%" columnClasses="scenario-viewer-leftcolumn,scenario-viewer-rightcolumn">
<h:outputText value="Output Name:" />
<p:inputText value="#{objList2.objType}" styleClass="textBoxStyle"/>
<h:outputText value="Output Bool:" />
<p:selectBooleanCheckbox value="#{objList2.boolVar}"/>
</h:panelGrid>
</p:panel>
</p:dataGrid>
</p:panel>
</p:dataGrid>
When I try to access a variable under objList2 is gives me the error saying The class obj2 does not have a readable property 'objType'. I double checked and it does have this property/variable and it is initialized.
What makes this even more strange is if I use objList2.boolVar in place of objList2.name in the header for each panel, it displays the information fine. This proves that the property exists and is initialized. Why wouldn't this work in inputText?? Any help would be greatly appreciated.
ORIGINAL:
I am working with PrimeFaces and trying to create a xhtml form for my UI. I am trying to dynamically display data from a list into a dataGrid structure. I am able to do this successfully, but I also need to display data from a list inside the first list.
For example:
I have a List under the variable objList which I use for my first dataGrid. I am able to loop through this list and create panels for each object and some other String data per object.
<p:dataGrid value="#{scenarioTree.objList}" var="objList" columns="1">
<p:panel id="obj" header="#{objList.name}" toggleable="true" collapsed="true">
<h:panelGrid columns="2" style="width: 100%" columnClasses="scenario-viewer-leftcolumn,scenario-viewer-rightcolumn">
<h:outputText value="text:" />
<p:inputText value="#{objList.variable}" styleClass="textBoxStyle"/>
<h:outputText value="text2:" />
<p:inputText value="#{objList.variable2}" styleClass="textBoxStyle"/>
</h:panelGrid>
Now I want to be able to create a second dataGrid that refers to objList2 which is inside objList1. Doing this though returns No records found in the UI even though there is a bunch of records there.
<p:dataGrid value="#{objList.objList2}" var="objList2" columns="1">
<p:panel id="obj2" header="#{objList2.name}" toggleable="true" collapsed="false">
</p:panel>
</p:dataGrid>
</p:panel>
</p:dataGrid>
Is this a bug with PrimeFaces or am I not able to do this? Or am I doing this wrong?
whenever we are talking about iterating list in xhtml we have dataTable,dataGrid, etc and panelGrid also becoz dataTable and all others are child of panelGrid(we can say this becoz panelGrid is also going to form table and others as well i meant child from this way) so in this case try to iterate the inner list in panelGrid since you are using panelGrid and referring variable is list so internally jsf is going to understand like this:
objList.get(i).getVariable();//For the first row
but according to your code it is understanding like this:
objList.getVariable();
which is not at all present so it is giving issue take out panel i don't find it's use and use like this:
<p:panelGrid value="#{objList}" var="obj">
//Access properties inside that
</p:panelGrid>
you can use <f:facet> tag if you want something in header

<f:ajax> tag fails to render multiple components [duplicate]

The following code is inspired from PrimeFaces DataGrid + DataTable Tutorials and put into a <p:tab> of a <p:tabView> residing in a <p:layoutUnit> of a <p:layout>. Here is the inner part of the code (starting from p:tab component); the outer part is trivial.
<p:tabView id="tabs">
<p:tab id="search" title="Search">
<h:form id="insTable">
<p:dataTable id="table" var="lndInstrument" value="#{instrumentBean.instruments}">
<p:column>
<p:commandLink id="select" update="insTable:display" oncomplete="dlg.show()">
<f:setPropertyActionListener value="#{lndInstrument}"
target="#{instrumentBean.selectedInstrument}" />
<h:outputText value="#{lndInstrument.name}" />
</p:commandLink>
</p:column>
</p:dataTable>
<p:dialog id="dlg" modal="true" widgetVar="dlg">
<h:panelGrid id="display">
<h:outputText value="Name:" />
<h:outputText value="#{instrumentBean.selectedInstrument.name}" />
</h:panelGrid>
</p:dialog>
</h:form>
</p:tab>
</p:tabView>
When I click the <p:commandLink>, the code stops working and gives the message:
Cannot find component with expression "insTable:display" referenced from "tabs:insTable:select".
When I try the same using <f:ajax>, then it fails with a different message basically telling the same:
<f:ajax> contains an unknown id "insTable:display" cannot locate it in the context of the component "tabs:insTable:select"
When it happens during another Ajax postback and the JSF project stage is set to Development, then it fails with a JavaScript alert with the message:
malformedXML: During update: insTable:display not found
How is this caused and how can I solve it?
Look in HTML output for actual client ID
You need to look in the generated HTML output to find out the right client ID. Open the page in browser, do a rightclick and View Source. Locate the HTML representation of the JSF component of interest and take its id as client ID. You can use it in an absolute or relative way depending on the current naming container. See following chapter.
Note: if it happens to contain iteration index like :0:, :1:, etc (because it's inside an iterating component), then you need to realize that updating a specific iteration round is not always supported. See bottom of answer for more detail on that.
Memorize NamingContainer components and always give them a fixed ID
If a component which you'd like to reference by ajax process/execute/update/render is inside the same NamingContainer parent, then just reference its own ID.
<h:form id="form">
<p:commandLink update="result"> <!-- OK! -->
<h:panelGroup id="result" />
</h:form>
If it's not inside the same NamingContainer, then you need to reference it using an absolute client ID. An absolute client ID starts with the NamingContainer separator character, which is by default :.
<h:form id="form">
<p:commandLink update="result"> <!-- FAIL! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
<p:commandLink update=":result"> <!-- OK! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
<p:commandLink update=":result"> <!-- FAIL! -->
</h:form>
<h:form id="otherform">
<h:panelGroup id="result" />
</h:form>
<h:form id="form">
<p:commandLink update=":otherform:result"> <!-- OK! -->
</h:form>
<h:form id="otherform">
<h:panelGroup id="result" />
</h:form>
NamingContainer components are for example <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation> (thus, all composite components), etc. You recognize them easily by looking at the generated HTML output, their ID will be prepended to the generated client ID of all child components. Note that when they don't have a fixed ID, then JSF will use an autogenerated ID in j_idXXX format. You should absolutely avoid that by giving them a fixed ID. The OmniFaces NoAutoGeneratedIdViewHandler may be helpful in this during development.
If you know to find the javadoc of the UIComponent in question, then you can also just check in there whether it implements the NamingContainer interface or not. For example, the HtmlForm (the UIComponent behind <h:form> tag) shows it implements NamingContainer, but the HtmlPanelGroup (the UIComponent behind <h:panelGroup> tag) does not show it, so it does not implement NamingContainer. Here is the javadoc of all standard components and here is the javadoc of PrimeFaces.
Solving your problem
So in your case of:
<p:tabView id="tabs"><!-- This is a NamingContainer -->
<p:tab id="search"><!-- This is NOT a NamingContainer -->
<h:form id="insTable"><!-- This is a NamingContainer -->
<p:dialog id="dlg"><!-- This is NOT a NamingContainer -->
<h:panelGrid id="display">
The generated HTML output of <h:panelGrid id="display"> looks like this:
<table id="tabs:insTable:display">
You need to take exactly that id as client ID and then prefix with : for usage in update:
<p:commandLink update=":tabs:insTable:display">
Referencing outside include/tagfile/composite
If this command link is inside an include/tagfile, and the target is outside it, and thus you don't necessarily know the ID of the naming container parent of the current naming container, then you can dynamically reference it via UIComponent#getNamingContainer() like so:
<p:commandLink update=":#{component.namingContainer.parent.namingContainer.clientId}:display">
Or, if this command link is inside a composite component and the target is outside it:
<p:commandLink update=":#{cc.parent.namingContainer.clientId}:display">
Or, if both the command link and target are inside same composite component:
<p:commandLink update=":#{cc.clientId}:display">
See also Get id of parent naming container in template for in render / update attribute
How does it work under the covers
This all is specified as "search expression" in the UIComponent#findComponent() javadoc:
A search expression consists of either an identifier (which is matched exactly against the id property of a UIComponent, or a series of such identifiers linked by the UINamingContainer#getSeparatorChar character value. The search algorithm should operates as follows, though alternate alogrithms may be used as long as the end result is the same:
Identify the UIComponent that will be the base for searching, by stopping as soon as one of the following conditions is met:
If the search expression begins with the the separator character (called an "absolute" search expression), the base will be the root UIComponent of the component tree. The leading separator character will be stripped off, and the remainder of the search expression will be treated as a "relative" search expression as described below.
Otherwise, if this UIComponent is a NamingContainer it will serve as the basis.
Otherwise, search up the parents of this component. If a NamingContainer is encountered, it will be the base.
Otherwise (if no NamingContainer is encountered) the root UIComponent will be the base.
The search expression (possibly modified in the previous step) is now a "relative" search expression that will be used to locate the component (if any) that has an id that matches, within the scope of the base component. The match is performed as follows:
If the search expression is a simple identifier, this value is compared to the id property, and then recursively through the facets and children of the base UIComponent (except that if a descendant NamingContainer is found, its own facets and children are not searched).
If the search expression includes more than one identifier separated by the separator character, the first identifier is used to locate a NamingContainer by the rules in the previous bullet point. Then, the findComponent() method of this NamingContainer will be called, passing the remainder of the search expression.
Note that PrimeFaces also adheres the JSF spec, but RichFaces uses "some additional exceptions".
"reRender" uses UIComponent.findComponent() algorithm (with some additional exceptions) to find the component in the component tree.
Those additional exceptions are nowhere in detail described, but it's known that relative component IDs (i.e. those not starting with :) are not only searched in the context of the closest parent NamingContainer, but also in all other NamingContainer components in the same view (which is a relatively expensive job by the way).
Never use prependId="false"
If this all still doesn't work, then verify if you aren't using <h:form prependId="false">. This will fail during processing the ajax submit and render. See also this related question: UIForm with prependId="false" breaks <f:ajax render>.
Referencing specific iteration round of iterating components
It was for long time not possible to reference a specific iterated item in iterating components like <ui:repeat> and <h:dataTable> like so:
<h:form id="form">
<ui:repeat id="list" value="#{['one','two','three']}" var="item">
<h:outputText id="item" value="#{item}" /><br/>
</ui:repeat>
<h:commandButton value="Update second item">
<f:ajax render=":form:list:1:item" />
</h:commandButton>
</h:form>
However, since Mojarra 2.2.5 the <f:ajax> started to support it (it simply stopped validating it; thus you would never face the in the question mentioned exception anymore; another enhancement fix is planned for that later).
This only doesn't work yet in current MyFaces 2.2.7 and PrimeFaces 5.2 versions. The support might come in the future versions. In the meanwhile, your best bet is to update the iterating component itself, or a parent in case it doesn't render HTML, like <ui:repeat>.
When using PrimeFaces, consider Search Expressions or Selectors
PrimeFaces Search Expressions allows you to reference components via JSF component tree search expressions. JSF has several builtin:
#this: current component
#form: parent UIForm
#all: entire document
#none: nothing
PrimeFaces has enhanced this with new keywords and composite expression support:
#parent: parent component
#namingcontainer: parent UINamingContainer
#widgetVar(name): component as identified by given widgetVar
You can also mix those keywords in composite expressions such as #form:#parent, #this:#parent:#parent, etc.
PrimeFaces Selectors (PFS) as in #(.someclass) allows you to reference components via jQuery CSS selector syntax. E.g. referencing components having all a common style class in the HTML output. This is particularly helpful in case you need to reference "a lot of" components. This only prerequires that the target components have all a client ID in the HTML output (fixed or autogenerated, doesn't matter). See also How do PrimeFaces Selectors as in update="#(.myClass)" work?
first of all: as far as i know placing dialog inside a tabview is a bad practice... you better take it out...
and now to your question:
sorry, took me some time to get what exactly you wanted to implement,
did at my web app myself just now, and it works
as I sayed before place the p:dialog out side the `p:tabView ,
leave the p:dialog as you initially suggested :
<p:dialog modal="true" widgetVar="dlg">
<h:panelGrid id="display">
<h:outputText value="Name:" />
<h:outputText value="#{instrumentBean.selectedInstrument.name}" />
</h:panelGrid>
</p:dialog>
and the p:commandlink should look like this (all i did is to change the update attribute)
<p:commandLink update="display" oncomplete="dlg.show()">
<f:setPropertyActionListener value="#{lndInstrument}"
target="#{instrumentBean.selectedInstrument}" />
<h:outputText value="#{lndInstrument.name}" />
</p:commandLink>
the same works in my web app, and if it does not work for you , then i guess there is something wrong in your java bean code...
It's because the tab is a naming container aswell... your update should be update="Search:insTable:display" What you can do aswell is just place your dialog outside the form and still inside the tab then it would be: update="Search:display"
Please note that from PrimeFaces 10 and up, you are able to use observer and event.
This allows you to update components based on a custom event name, set by the #obs(event) keyword. For example:
<p:commandButton update="#obs(myEvent)"/>
<h:panelGroup>
<p:autoUpdate on="myEvent"/>
</h:panelGroup>
See:
https://www.primefaces.org/showcase/ui/ajax/observer.xhtml
I know this already has a great answer by BalusC but here is a little trick I use to get the container to tell me the correct clientId.
Remove the update on your component that is not working
Put a temporary component with a bogus update within the component you were trying to update
hit the page, the servlet exception error will tell you the correct client Id you need to reference.
Remove bogus component and put correct clientId in the original update
Here is code example as my words may not describe it best.
<p:tabView id="tabs">
<p:tab id="search" title="Search">
<h:form id="insTable">
<p:dataTable id="table" var="lndInstrument" value="#{instrumentBean.instruments}">
<p:column>
<p:commandLink id="select"
Remove the failing update within this component
oncomplete="dlg.show()">
<f:setPropertyActionListener value="#{lndInstrument}"
target="#{instrumentBean.selectedInstrument}" />
<h:outputText value="#{lndInstrument.name}" />
</p:commandLink>
</p:column>
</p:dataTable>
<p:dialog id="dlg" modal="true" widgetVar="dlg">
<h:panelGrid id="display">
Add a component within the component of the id you are trying to update using an update that will fail
<p:commandButton id="BogusButton" update="BogusUpdate"></p:commandButton>
<h:outputText value="Name:" />
<h:outputText value="#{instrumentBean.selectedInstrument.name}" />
</h:panelGrid>
</p:dialog>
</h:form>
</p:tab>
</p:tabView>
Hit this page and view the error.
The error is:
javax.servlet.ServletException: Cannot find component for expression "BogusUpdate" referenced from
tabs:insTable: BogusButton
So the correct clientId to use would then be the bold plus the id of the target container (display in this case)
tabs:insTable:display
Try change update="insTable:display" to update="display". I believe you cannot prefix the id with the form ID like that.

JSF p:commandLink and p:ajax have different behavior in update

I have a menu with list items and a p:commandLink inside. While using this for different pages I realized that I have problems updating form with a p:dataTable and p:columns inside. After clicking on the link, form2 which actually has some panels and the table inside should be updated. Now my problem: Only most of the content in form2 is updated correctly, but the p:columns have the old value. If I click again on the link, the correct values are shown.
The bean method public void selectProject(Project myProject) is called correctly and the values are correctly processed.
Now the strange thing: If I add a h:graphicImage with a p:ajax everything works fine!!
<h:form id="form1">
...
<li>
<p:commandLink update=":form2" actionListener="#{bean.selectProject(p)}">
<h:outputText value="#{p.name}" />
</p:commandLink>
<!-- Code below is for testing only, but works fine! -->
<h:graphicImage value="#{iconBean.icon(12,'clock')}">
<p:ajax event="click" update=":form2" listener="#{bean.selectProject(p)}"/>
</h:graphicImage>
</li>
...
</h:form>
Update This does not seem to be related to p:columns, a simplified test case shows the expected behavior of p:commandLink. In the original (still failing) setting the menu is implemented as a composite component.
As a workaround I use
<h:commandLink>
<h:outputText value="#{p.name}"/>
<p:ajax event="click" update=":form2" listener="#{bean.selectProject(p)}"/>
</h:commandLink>

Rules for ids to update components

I'm new to JSF2 and Primefaces and realized an issue to update components.
Lets' assume I have the following piece of code, I can directly update="counter"
<h:form id="f1">
<h:outputText id="counter" value="#{clientBean.counter}" />
<h:graphicImage url="/images/circle-ok.png">
<p:ajax event="click" update="counter" process="#this"
listener="#{clientBean.tag}"/>
</h:graphicImage>
</h:form>
In another h:form I have to use update="f1:counter". Only a update="counter" does not work here.
<h:form id="f2">
<p:dataTable var="var" value="#{clientBean.vf}">
<p:column>
<f:facet name="header">Tag</f:facet>
<h:graphicImage url="/images/circle-ok.png">
<p:ajax event="click" update="f1:counter" process="#this"
listener="#{clientBean.tag}" />
</h:graphicImage>
</p:column>
</p:dataTable>
</h:form>
I haven't faced this with JSF1.2 (and RichFaces), what are the rules to address the ids correctly?
In your first example JSF could lookup up the counter element in the same scope as ajax listener. Form implements NamingContainer which means it prefixes client ids (used in html) with its own id and creates a distinct name space for ids. Have a look at the page source under browser - there will be f1:counter id assigned to your counter. In your second example there is no counter element in the scope (inside form f2) so the lookup fails.
You can disable this form behaviour with prependId="false". This is useful if you are sure there won't be elements with identical ids across all forms.
Icefaces works differently - it automatically calculates the html delta and sends it to the browser as partial update. In most cases this is more convenient for the programmer but comes with considerable performance cost. I believe JSF2 adopted icefaces partial updates concept but requires ids to be passed explicitely.

h:dataTable inside ui:repeat, how specify render id from f:ajax?

I have:
<ui:repeat id="projectsTable" var="project" value="#{projectsBacking.projectList}">
#{project.id}
<h:dataTable id="usersAssignedToProject#{project.id}" var="appUser" value="#{projectsBacking.getAllUsersAssignedToProject(project)}">
#{project.id}
<h:column>
<h:outputText value="#{appUser.getUsername()}"/>
</h:column>
</h:dataTable>
</ui:repeat>
Using <f:ajax> I can not render h:dataTable with given id, can someone explain me what is wrong in this code? When I checked by FireBug <table id="usersAssignedToProject">, but in client side #{project.id} was printed (before and inside datatable).
It is because of id of h:dataTable is resolved when components tree is being build, but var project is only available on render response.
Try using c:forEach in this case instead of ui:repeat.
More information on this:
https://rogerkeays.com/jsf-c-foreach-vs-ui-repeat

Categories