I am trying to create a datatable that displays a blockUI whenever it is busy, and I have been mostly successful. It now grays out and shows "Loading..." whenever I click either of two commandButtons, sort the datatable by clicking on a header, or page through the datatable. You can see the code for it below.
The problem is that after I have used one of the commandButtons (which runs an ajax update on the blocked element), subsequent actions do not trigger the blockUI (until I refresh the page). For example:
Load page
Click a datatable header - blockUI appears until table is finished sorting
Click one of the datatable page navigation buttons - blockUI appears until the page is loaded
Click one of the commandButtons - blockUI appears until the button's actionListener has finished
Click a datatable header - table sorts, but blockUI does not appear.
Click one of the datatable page navigation buttons - page loads, but blockUI does not appear
Click one of the commandButtons - actionListener runs and table updates, but blockUI does not appear
Reload the page - everything works properly again
Changing the commandButtons' update="" attribute to ajax="false" causes the sorting/paging to always display the blockUI, but the commandButtons to never display the blockUI.
Any ideas?
<div class="buttonDiv">
<p:commandButton ... update="resultsPanel" id="submitButton" ... />
...
<p:commandButton ... update="resultsPanel" id="resetScenarioButton" ... />
</div>
<p:panel header="Results Grid" id="resultsPanel">
...
<p:dataTable ... id="VAResults" ... >
...
</p:dataTable>
....
</p:panel>
<p:blockUI block="resultsPanel" trigger="submitButton, resetScenarioButton, VAResults">
Loading...
</p:blockUI>
The trigger attribute binds jQuery listeners on the specified elements. However if you update an element the binding gets lost. I don't know if it works, but you could try moving the <p:blockUI inside the resultsPanel. I suspect that when you update the panel the blockUI gets updated too and thus re-binding the listener to the data table.
<p:panel header="Results Grid" id="resultsPanel">
...
<p:dataTable ... id="VAResults" ... >
...
</p:dataTable>
....
<p:blockUI block="resultsPanel" trigger="submitButton, resetScenarioButton, VAResults">
Loading...
</p:blockUI>
</p:panel>
I've had the same problem and kind of simillar scenario:
<p:dataTable>
....
<p:ajax event="rowSelect" update="buttons" global="false" onstart="blockMessageButtons.show();" oncomplete="blockMessageButtons.hide();"/>
</p:dataTable>
<p:outputPanel layout="block" id="buttons">
<!-- content to be blocked -->
</p:outputPanel>
<p:blockUI block="buttons" widgetVar="blockMessageButtons"/>
The problem was that panel buttons was both updated by ajax, and blocked by blockUI. I had to divide it in two:
<p:dataTable>
....
<p:ajax event="rowSelect" update="buttons-content" global="false" onstart="blockMessageButtons.show();" oncomplete="blockMessageButtons.hide();"/>
</p:dataTable>
<p:outputPanel layout="block" id="buttons-container">
<p:outputPanel layout="block" id="buttons-content">
<!-- content to be blocked -->
</p:outputPanel>
</p:outputPanel>
<p:blockUI block="buttons-container" widgetVar="blockMessageButtons"/>
#siebz0r already provided the explanation why blockUI stops working when a component is updated.
I am using a one blockUI element in the template for all other pages and thus don't want to include more blockUI elements.
If the blockUI element is updated as well one does not need to move the blockUI inside the component that will be updated.
Here is an example:
<p:panel id="surroundingPanel">
...
<p:commandButton value="ButtonName" styleClass="blockUi"
action="actionToBeExecuted" update=":surroundingPanel :blockUiBinding" />
</p:panel>
<p:outputPanel id="blockUiBinding">
<p:blockUI block=":elementToBeBlocked" trigger="#(.blockUi)">
Loading ...
</p:blockUI>
</p:outputPanel>
The element blockUiBinding can be located anywhere, as long as it can be updated. It is wrapping the blockUI element, because blockUI generates at least two different divs. So when the wrapping element is updated also the blockUI will be updated.
Related
I have a form in my page which has a panel grid inside of it, to show different info. One of the first rows is a breadcrum that shows a tail of selected items at a current moment. I want to define an ajax event for selecting an item and updating that breadcrum (and other components of my page as well). The problem is that I get a "Unable to attach to non-ClientBehaviorHolder parent" error. I searched that this is because PanelGrid Columns (which is the parent component of that breadcrum) doesn't support ajax events, the suggestion was to implement a Data Table, the problem is that i have lots of different components such as Menus, SelectOneMenu, Command Buttons, Graphic Images, etc. that wont render in a DataTable. Is there a way to do this? Here's part of my code as a sample...
<h:form id="catPage">
<p:panelGrid style="width: 100%; margin:auto; text-align: center;">
<!-- Breadcrum of current caregory -->
<p:row>
<p:column colspan="4">
<p:breadCrumb id="breadcrumcat" model="#{categoryBean.dislpayBreadCrumb()}">
<p:ajax event="itemSelect" listener="#{categoryBean.selectCatListener}" update="breadcrumcat" />
</p:breadCrumb>
</p:column>
</p:row>
<!-- More code... -->
</p:panelGrid>
</h:form>
I have the following configuration:
JSF: Apache MyFaces JSF-2.1 Core API (Version: 2.1.10)
PrimeFaces 3.4.2
Tomcat 7.0.35
I have a DataTable with rowExpansion and a panel with button in the rowExpansion. I would like to refresh only the panel inside the rowExpansion when the user clicks on the button.
<p:dataTable id="vmt" var="vmtemplate" value="#{myViewBean.myList}" rowKey="#{vmtemplate.id}">
<p:ajax event="rowToggle" listener="#{myViewBean.onRowToggle}" update=":growl" />
<p:column style="width:2%"><p:rowToggler /></p:column>
<p:column>...</p:column>
<p:rowExpansion>
<p:panel id="exp_panel" header="#{vmtemplate.name} Instances">
<h:form id="instancesForm">
<p:commandButton id="refreshRowExpPanel" title="Refresh" update=":growl" action="#{myViewBean.reloadData}"/>
</h:form>
</p:panel>
</p:rowExpansion>
</p:dataTable>
I think I have to set the update property of the refreshRowExpPanel button in order to refresh the exp_panel, but when I set it to update=":growl :expanel" I get an error:
Cannot find component with identifier ":expanel" referenced from
"mainForm:vmt:0:refreshRowExpPanel".
so the generated ID of the panel is "mainForm:vmt:0:refreshRowExpPanel", but when I specify this to the update (update=":mainForm:vmt:0:refreshRowExpPanel") I got the same error:
Cannot find component with identifier
":mainForm:vmt:0:refreshRowExpPanel" referenced from
"mainForm:vmt:0:refreshRowExpPanel".
with update=":mainForm" it works, but it refreshes the whole dataTable and my expanded row will be closed, so it's not fine.
Could you please suggest a solution how to refresh only my panel inside the rowExp, maybe from JS, or any other idea?
thx!
I recommend you to try using ID of the panel without colon on the beginning, so:
update="mainForm:vmt:0:refreshRowExpPanel"
Also you may like to read this post about making references.
I've just realized, that I had a form outside my dataTable:
<h:form id="outerForm">
<p:dataTable id="vmt" var="vmtemplate" value="#{myViewBean.myList}" rowKey="#{vmtemplate.id}">
pansion>
...
</p:dataTable>
</form>
by removing the outer form, and changing the update value to "#form" solved the problem
I'm using JSF with PrimeFaces to make an application as an assignment for college. I'm struggling to get something working. I'm using PrimeFaces and I have a tabview which contains 5 tabs. In one of those tabs I have a dataTable which has several rows and columns. This all works fine, but the problem is that I want to submit the form without rerendering the (entire page). The thing is, every column in the dataTable has a selectBooleanCheckbox, and when that checkbox is selected, a button should disappear. If it's unselected the button should appear. This works fine with onchange="this.form.submit()" or onclick="this.form.submit()" but it refreshes the entire application, and it causes the first tab to be selected, rather than the one I was at. So I'm looking for a solution to be able to submit and re-render some stuff without refreshing the entire program. This is my code:
<body>
<h:form id="customerForm">
<p:dataTable id="customerlist" var="c" value="#{customerBean.customerList}">
<p:column>
<f:facet name="header">Select</f:facet>
<center>
<p:selectBooleanCheckbox value="#{c.selected}" onchange="this.form.submit()"/>
</center>
</p:column>
</p:dataTable>
<h:commandButton id="testtest" value="test" rendered="#{customerBean.numberSelected() == 0}"/>
</h:form>
</body>
I removed most of the columns for the sake of simplicity. What is the solution to this? I've tried using ajax, but that didn't work
<p:selectBooleanCheckbox value="#{c.selected}">
<f:ajax event="click" execute="#form" render="#none"/>
</p:selectBooleanCheckbox>
Do you really need to submit the whole form? If it's enough that the view is being rerenderd try just to update the form. For that you can use the primefaces ajax event.
<p:selectBooleanCheckbox value="#{c.selected}">
<p:ajax update="#form"/>
</p:selectBooleanCheckbox>
If this does not work, please tell us what "..ajax, didn't work" exactly mean. Is there any POST Request submitted? Is any Action/Setter called?
I have a page that have 2 ui:repeat to iterate a complex collection. I need add commandlinks that will update some of the data on the page without refresh the whole page. The code is like the following:
<h:panelGroup id="panel1">
#{backBean.getNumber()}
</h:panelGroup>
<h:form>
<a4j:commandlink action="#{backBean.increaseNumber()}" reRender="panel1" />
</h:form>
<ui:repeat value="#{masterlist}" var="list">
<ui:repeat value="#{list}" var="element">
#{element.description}
</ui:repeat>
</ui:repeat>
I can see the increaseNumber() method is called and the number is updated, but panel1 is not re-rendered. If I remove one ui:repeat tag and then it will work. I did research online and it seems there are issues with nested ui:repeat, but I couldn't find any solution.
I figured it out. Replace ui:repeat with a4j:repeat worked.
I have noticed that pretty much all PF components that have 'dynamic' attribute set to 'true' will only show up after a short delay (which is understandable), and also will not trigger ajax start/stop event.
This is my (perfectly working) Ajax status component, actual dialog contents omitted for brevity:
<p:ajaxStatus onstart="statusDialog.show();" onsuccess="statusDialog.hide();" onerror="errorDialog.show();"/>
This is a dynamically loaded dialog. It needs to get data from backing beans:
<p:dialog modal="true" widgetVar="confDialog" position="center" id="confD" dynamic="true">
<p:panelGrid>
<p:row>
<p:column>
<h:outputText value="Date"></h:outputText>
</p:column>
<p:column>
<h:outputText value="#{myBean.currentDate}">
<f:convertDateTime locale="#{myBean.currentLanguage}" type="both" dateStyle="full" timeStyle="full" timeZone="#{myBean.currentTimeZone}"></f:convertDateTime>
</h:outputText>
</p:column>
</p:row>
</p:panelGrid>
<p:commandButton value="Close" type="button" onclick="confDialog.hide();">
</p:commandButton>
</p:dialog>
A commandbutton that does something and displays dynamically loaded confirmation dialog:
<p:commandButton value="Do it" type="submit" ajax="true" process="#form"
action="#{bean.processForm}"
oncomplete="if(!args.validationFailed){confDialog.show();}"
update="confD #form"/>
Ajax status dialog is displayed while the commandbutton submits the data to backing bean and gets data back. Then ajax status dialog disappears, there is a delay of 1-2 seconds, and confirmation dialog is shown.
The same happens with dynamically loaded tabs (in PF tabView component).
Why isn't my ajax status dialog displayed during dynamic component load? How can I show it?
EDIT:
thanks to #partlov I figured out a solution without overriding PF function, by adding JQ ajax start/stop handler to the dynamically loaded dialog:
jQuery(document).ready(function() {
confDialog.getJQ().ajaxStart(function(){statusDialog.show()});
confDialog.getJQ().ajaxStop(function(){statusDialog.hide()});
});
As written in Primefaces documentation <p:ajaxStatus> just intercepts global AJAX requests, and this request is not global. There is no easy way to do this what you want. As time of loading of dialog is short I really don't see point for this. Only solution, as I think, is to overload Primefaces dialog JavaScript methods.
Primefaces calls loadContents() method when dialog content is loaded so you can override this method for this. I don't advice you to do that, because this is undocumented and can be changed in future versions of Primefaces:
jQuery(document).ready(function() {
var oldLoadContents = PrimeFaces.widget.Dialog.loadContents;
PrimeFaces.widget.Dialog.loadContents = function() {
statusDialog.show();
oldLoadContents();
}
});
This should show your dialog on dynamic loading. You can use onShow attribute of p:dialog to close your dialog:
onShow="statusDialog.hide()"