primefaces
primefaces copied to clipboard
OverlayPanel: Race condition with embedded components on AJAX-update
1) Environment
- PrimeFaces version: 8.0
2) Expected behavior
Components (e.g. p:selectOneMenu
) inside a dynamic overlay (e.g. p:overlayPanel
) with appendTo
should not double the found entries when the jq
property of the component widget is resolved
3) Actual behavior
Due to the appendTo
the DOM of the dynamic overlay may survive the AJAX update which effectively doubles the DOM for a short time. The first instance is at the original position (before it is moved to its appendTo
destination) which was added during the AJAX update and the second instance is the old one which may be outside the updated area.
The SelectOneMenu is refreshed before the OverlayPanel. But the old DOM of the OverlayPanel is removed during the refresh of the OverlayPanel (PrimeFaces.utils.registerDynamicOverlay > widget.addRefreshListener). Therefore the SelectOneMenu finds two DOM representation of its widget for SelectOneMenu#jqId
.
4) Steps to reproduce
- Press button
Alert state ofSelectOneMenu
Output should be size = 1 and label = 1 - Press the button
Update OP
- Press button
Alert state ofSelectOneMenu
Output should be size = 2 and label = 11 which is wrong
5) Sample XHTML
<h:form>
<p:commandButton value="Update OP" process="@this pnlTest" update="pnlTest" />
<h:panelGroup id="pnlTest">
<p:commandButton id="btnTest" value="Open Overlay" type="button" />
<p:overlayPanel id="opTest" for="btnTest" appendTo="@(body)">
<p:selectOneMenu value="#{testBean.selectedTest}" widgetVar="somTest">
<f:selectItem itemLabel="1" itemValue="1" />
</p:selectOneMenu>
</p:overlayPanel>
</h:panelGroup>
<p:commandButton value="Alert state ofSelectOneMenu" type="button" onclick="alert('Size of somTest#jq = ' + PF('somTest').jq.length + '\nSelected Label = ' + PF('somTest').getSelectedLabel())" />
</h:form>
6) Sample bean
@ViewScoped
@Named
public class TestBean implements Serializable {
private Integer _selectedTest;
public Integer getSelectedTest() {
return _selectedTest;
}
public void setSelectedTest(Integer selectedTest) {
_selectedTest = selectedTest;
}
}
I will try to provide a test case
Reproducer: https://github.com/fanste/primefaces-test/tree/test-5710
Excellent analysis and thanks for the example.
@fanste maybe I am doing something wrong but I can't reproduce your issue on PF11? Attached it my reproducer I got from your post above just updated for PF11. Can you try this? pf-5710.zip
appendTo="@(body)"
is missing for your p:overlayPanel
.
Ahhh yep reproduced now pf-5710.zip .
The way to fix this right now without being invasive is to add a onstart="PF('overlayPanel').destroy();"
to your command button updating the OP. This fixes this specific issue but its a workaround not a fix
<h:form id="frm">
<p:commandButton id="exports" value="Update OP" process="@this pnlTest" update="pnlTest" onstart="PF('overlayPanel').destroy();" />
<h:panelGroup id="pnlTest">
<p:commandButton id="btnTest" value="Open Overlay" type="button" />
<p:overlayPanel id="opTest" widgetVar="overlayPanel" for="btnTest" appendTo="@(body)">
<p:selectOneMenu value="#{testBean.selectedTest}" widgetVar="somTest">
<f:selectItem itemLabel="1" itemValue="1" />
</p:selectOneMenu>
</p:overlayPanel>
</h:panelGroup>
<p:commandButton value="Alert state ofSelectOneMenu" type="button" onclick="alert('Size of somTest#jq = ' + PF('somTest').jq.length + '\nSelected Label = ' + PF('somTest').getSelectedLabel())" />
</h:form>
That would definitively work.
Another workaround could be to wrap the contents of the overlay in an h:panelGroup
and update that instead of the whole Overlay Panel.
A long term fix could be one of:
- Add a cleanup-script-tag before components like Overlay Panel
- Improve the ajax framework, to recognize updates of such components and run the cleanup internally - that needs to work for clientside updates as well as serverside triggered updates.
Agreed but your #2 is a much more invasive fix and would be tricky to test all scenarios but that would be the right place to fix it.
I proposed a PR some time ago in #3951 fixing updates of appended components.
@VsevolodGolovanov feel free to revive it if you want?
I found an elegant fix to this particular issue if you want to review @VsevolodGolovanov @fanste
in 14.0.0+ this will be a new property destroyWithTarget="true"
<p:overlayPanel id="opTest" for="btnTest" appendTo="@(body)" destroyWithTarget="true">
@melloware , nice! But a bit unexpected that you have to opt in - is there a downside?
Yep the downside is here: https://github.com/primefaces/primefaces/issues/11409
Too many scenarios that could break.