Primefaces Growl onclose event? - events

Is there any way to detect when the growl dialog has been closed, and act on that event?
No ajax events are listed for growl in the PrimeFaces userguide, nor are there any onclose or oncomplete javascript handler attributes.
The reason I need this, is that i use a poller which is deactivated when a dialog is opened, and i wish to reactivate the poller after the growl message is closed.
I was reactivating the poller after the original dialog was closed, but for some reason the addition of the growl broke the reactivation of the poller. The restarted poller polled once till the growl appeared, then stopped.
So .. my idea is to move the reactivation of the poller till after the growl is closed.
The code is in several fragments, one fragment has the poller, which can be started and stopped via a toolbar button (only the poller shown here):
<h:form id="form-toolbar">
<!-- poller -->
<p:poll id="pollerDataTable"
widgetVar="p4mPollerDataTable"
interval="10"
async="true"
global="false"
update=":form-toolbar:dtItems"
listener="#{controller.refreshDataTable}"
autoStart="false" />
[snip]
</h:form>
One fragment has the growls:
<h:form id="form-body-growl">
<p:growl id="growlSuccess" for="success" severity="info" infoIcon="/img/LOGO_H150.png"
warnIcon="/img/LOGO_H150.png" errorIcon="/img/LOGO_H150.png"
sticky="false" life="3000"/>
<p:growl id="growlError" severity="warn, error" infoIcon="/img/LOGO_H150.png"
warnIcon="/img/LOGO_H150.png" errorIcon="/img/LOGO_H150.png"
sticky="true" globalOnly="true"/>
</h:form>
One fragment has a command button to show the dialog:
<p:commandButton id="tbBtnNew"
icon="ui-icon-plus"
title="#{facesUtilsBean.getLocalizedText(webUiConstBean.BUNDLE_BASE_NAME_UI_STRING, 'toolbar.tooltip.new')}"
actionListener="#{controller.onShowNewDialog}"
update=":form-dlgNew:new"
onsuccess="p4mDlgNew.show();"/>
<p:tooltip for="tbBtnNew" showEffect="fade" hideEffect="fade" />
Another contains the dialog being opened:
<!-- new dialog -->
<h:form id="form-dlgNew">
<p:dialog header="New" id="dlgNew"
widgetVar="p4mDlgNew" resizable="false" dynamic="true" modal="true" closable="false">
<div class="dialog-container">
<h:panelGrid id="new" columns="3" columnClasses="formText, formValue, formValidation" styleClass="defaultTable" style="width:100%;" rendered="#{not empty controller.editingItem}">
<ui:insert name="newDialogColumns"/>
<p:spacer/>
<p:column colspan="2">
<p:commandButton styleClass="cmdButtonSaveCancel" ajax="false"
value="#{facesUtilsBean.getLocalizedText(webUiConstBean.BUNDLE_BASE_NAME_UI_STRING, 'dialog.save.button')}"
actionListener="#{controller.onSaveNewDialog}" update=":form-toolbar:dtItems, :form-body-growl:growlSuccess, :form-body-growl:growlError"
onsuccess="p4mDlgNew.hide();" process="new"/>
<p:commandButton styleClass="cmdButtonSaveCancel" ajax="false"
value="#{facesUtilsBean.getLocalizedText(webUiConstBean.BUNDLE_BASE_NAME_UI_STRING, 'dialog.cancel.button')}"
actionListener="#{controller.onCancelNewDialog}" update=":form-toolbar:dtItems" onsuccess="p4mDlgNew.hide();" process="#this"/>
</p:column>
</h:panelGrid>
</div>
</p:dialog>
</h:form>
And then in the backing bean there are event handlers which pause and resume polling
public void suspendPolling() {
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.execute(POLLER_DATATABLE_CLIENT_ID + ".stop();");
}
public void resumePolling() {
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.execute(POLLER_DATATABLE_CLIENT_ID + ".start();");
}
public void onShowNewDialog(ActionEvent event) {
if (isAutoRefreshActive()) {
suspendPolling();
}
[snip]
}
/**
* Called by new button template when 'cancel' button clicked.
* Refreshes data and resumes polling if necessary.
*/
public void onCancelNewDialog(ActionEvent event) {
[snip]
if (isAutoRefreshActive()) {
resumePolling();
}
}
/**
* Called by new button template when 'accept' button clicked.
* Refreshes data and resumes polling if necessary.
*/
public void onSaveNewDialog(ActionEvent event) throws PSoftIOException {
[snip]
if (isAutoRefreshActive()) {
resumePolling();
}
}
So, please, would there be a way to move the resumePolling functionality till after the growls are closed?
Or .. alternatively (and better even), a way to fix the orginial problem, that the growls broke the poller?
Cheers
[edit ]
Further Information regarding the active status of the poller.
We check and controll the active status of the poller using the following code in the Bean:
private ToolBarRefreshController getToolBarRefreshController() {
return (ToolBarRefreshController) FacesUtils.resolveBean("toolBarRefreshController");
}
private boolean isAutoRefreshActive() {
ToolBarRefreshController toolBarRefreshController = getToolBarRefreshController();
if (toolBarRefreshController == null) {
return false;
}
return toolBarRefreshController.isAutoRefreshActive();
}
public void startAutoRefresh() {
// nesting starting polling must be prevented
if (!isAutoRefreshActive()) {
refreshDataTable();
resumePolling();
getToolBarRefreshController().setAutoRefreshActive(true);
}
}
public void stopAutoRefresh() {
// nesting stopping polling must be prevented
if (isAutoRefreshActive()) {
suspendPolling();
getToolBarRefreshController().setAutoRefreshActive(false);
}
}

The Primefaces userguide says the following thing about using requestContext.execute:
RequestContext provides a way to execute javascript when the ajax request completes, this
approach is easier compared to passing callback params and execute conditional javascript.
So if you want to use the RequestContext.execute never put your commandbuttons on ajax="False" because then the requestContext doesn't work.
Also check if your Poller is running already is a good practice because i noticed that if you call POLLER_DATATABLE_CLIENT_ID + ".start();" The poller starts with a new instance but you can't stop this anymore. I don't know if this is a bug or expected behaviour.
For a clean check you can use the following code to check if the poller needs to be started again:
requestContext.execute("if(!" POLLER_DATATABLE_CLIENT_ID +".isActive()){" POLLER_DATATABLE_CLIENT_ID +".start()}");

Related

How can I check if multiple checkboxes are checked with JSF / AjaxBehaviorEvent

I have multiple checkboxes - they are given the same id, "choosen" in the xhtml view code.
I have added an Ajax event listener for them, like so:
<ui:repeat var="posts"
value="#{postBacking.postsOverview.posts}">
<h:selectBooleanCheckbox id="choosen"
styleClass="check-margin"
value="#{posts.choosen}" rendered="#{posts.declined =='No'}">
<f:ajax listener="#{postsBacking.isPostsChosen}" event="click" render="#form" execute="#form"/>
</h:selectBooleanCheckbox>
</ui:repeat>
In the listener method in my backingBean, I would like to check if any of these checkboxes are checked, and then set a button disabled if no checkboxes are marked - and enabled if one or more is set.
But how do I get hold of the values of all checkboxes - can I use the AjaxBehaviorEvent objet for that or is there some other way?
While processing the ajax event listener, the checkbox values should normally have been applied to your data model where you can look up the state of user choice. Depending on that state, simply toggle a boolean bean property which disables the command button:
#Named
#ViewScoped
public class MyBean implements Serializable {
private List<Post> posts;
private boolean buttonDisabled = true;
public void ajaxEventListener(BehaviorEvent e) {
boolean anythingChosen = false;
for (Post post : posts) {
if (post.isChosen() && "No".equals(post.getDeclined())) {
anythingChosen = true;
break;
}
}
// or alternatively using the stream api:
// anythingChosen = posts.stream().filter(Post::isChosen).map(Post::getDeclined)
// .anyMatch("No"::equals);
setButtonDisabled(!anythingChosen);
}
// getters, setters ...
}
Using the following form, the command button is disabled as long as no checkbox is checked:
<h:form>
<ui:repeat var="post" value="#{myBean.posts}">
<h:selectBooleanCheckbox value="#{post.chosen}">
<f:ajax event="click" listener="#{myBean.ajaxEventListener}" render="#form" execute="#form"/>
</h:selectBooleanCheckbox>
</ui:repeat>
<h:commandButton action="#{myBean.doSomething()}" value="doSomething"
disabled="#{myBean.buttonDisabled}" />
</h:form>
You can use valueChangeListener rather than using listener and event attributes. The valueChangeListener will be triggered every time you check/uncheck the checkbox.
To get hold of the values when thevalueChangeListener is invoked, you can loop through the postBacking.postsOverview.posts to see if any value is checked.
Don't forget to update the button on <f:ajax render="#form btnId"/> to enable/ disable it.

Why does PrimeFaces 5.3 not call my listener function when I catch the "blur" and "focus" events?

My intention was to show information on filling a text area component. When it gets the focus I show the info, when it loses the focus I clear the info. I do not want to proccess any input value just catch the focus and blur events and call the listener and show/clear the message.
I have read BalusC's comment on conditional rendering so I put my conditional text message to an "always rendered" JSF component.
My "always rendered" f:outPutPanel component is shown in the generated XHTML code but my listener function is never called. As far as I know the method signature of primefaces listener is just void somefunction() not like in jsf ajax listener.
I also read BalusC's comment about including #this for the proccess attribute but in my case it seems not to be reasonable as I do not want to catch any input values.
I am newbie to JSF and primefaces so any help is appreciated.
My relevant view snippet:
<h:form>
<h:panelGroup layout="block" id="info">
<h:outputText rendered="#{ebookController.infoMessage != null}" value="#{ebookController.infoMessage}"/>
</h:panelGroup>
....
<p:inputTextarea id="description" styleClass="form-control vspace" required="true" requiredMessage="Kérlek, add meg az e-könyv rövid leírását" value="#{ebookController.newEbook.description}" rows="20" cols="30" counter="display" maxlength="500" counterTemplate="{0} karakter lehet még" autoResize="false" validatorMessage="Az e-könyv leírása maximum 500 karakterből állhat">
<f:validateLength maximum="500"/>
<p:ajax event="focus" process="#none" update="info" listener="#{ebookController.setTextAreaInfoMessage()}"/>
<p:ajax event="blur" process="#none" update="info" listener="#{ebookController.clearInfoMessage()}"/>
</p:inputTextarea>
<h:outputText id="display" />
</h:form>
My relevant java code:
#Named(value = "ebookController")
#SessionScoped
public class EbookController implements Serializable {
private String infoMessage;
public String getInfoMessage() {
return infoMessage;
}
public void setInfoMessage(String infoMessage) {
this.infoMessage = infoMessage;
}
public void setTextAreaInfoMessage(){
setInfoMessage("Tipp: Az e-könyved bemutatása során a fontos részeket emeld ki, és tagold a szöveget bekezdésekkel. Ha egy bekezdés végére értél, csak egy ENTER-t üss!");
}
public void clearInfoMessage(AjaxBehaviorEvent e){
setInfoMessage("");
}
....
}
Solution 1 : User f:ajax
<f:ajax immediate="true" event="focus" process="#none" render="info"
listener="#{ebookController.setTextAreaInfoMessage()}" />
<f:ajax immediate="true" event="blur" process="#none" render="info"
listener="#{ebookController.clearInfoMessage()}" />
Managed Bean
public void setTextAreaInfoMessage() {
System.out.println("setTextAreaInfoMessage");
setInfoMessage(
"Tipp: Az e-könyved bemutatása során a fontos részeket emeld ki, és tagold a szöveget bekezdésekkel. Ha egy bekezdés végére értél, csak egy ENTER-t üss!");
}
public void clearInfoMessage() {
System.out.println("clearInfoMessage");
setInfoMessage("");
}
Reference Problem with h:form and p:ajax

PF Dialog framework with RemoteCommand

I'd like to open a dialog via the remotecommand action and get the value from the closeDialog(Object data) in the backing bean of the page.
The remoteCommand is correctly firing the actionListener in the bb on page load
<p:remoteCommand autoRun="true" actionListener="#{userManagerBB.showDialog}"/>
And the dialog is also correctly shown with this showDialog method:
public void showDialog(ActionEvent e){
...
otherBean.openDialog(selectedUser);
}
And in the otherBean:
public void openDialog(SiteUser user) {
...
RequestContext.getCurrentInstance().openDialog("userDialog", options, params);
}
The problem is that I don't know how to listen to the event triggered by:
RequestContext.getCurrentInstance().closeDialog(user);
In the PF example http://www.primefaces.org/showcase/ui/dialogFrameworkData.jsf a commandButton and p:ajax is used like this
<p:commandButton value="Select Car" icon="ui-icon-extlink" actionListener="#{dialogBean.chooseCar}">
<p:ajax event="dialogReturn" listener="#{dialogBean.onCarChosen}" update="growl" />
</p:commandButton>
But I cannot put the p:ajax inside of the remoteCommand tag:
<p:ajax> Unable to attach <p:ajax> to non-ClientBehaviorHolder parent
I've also tried to add the ajax behavior to a hidden commandButton, but the listener is never called that way. (of course I could do synchronization of data between the backing bean, but that would hurt the design of reusable dialog)
Is there some way how to work around that?
I've solved the issue by creating hidden command button and calling javascript from remoteCommand:
<p:remoteCommand autoRun="true" oncomplete="sync.jq.click()"/>
<p:commandButton widgetVar="sync" actionListener="#{userManagerBB.showDialog}" style="display:none" >
<p:ajax event="dialogReturn" listener="#{userManagerBB.userChanged}" update="form"/>
</p:commandButton>

PrimeFaces: Dialog closes on validation errors [duplicate]

Minimal example dialog:
<p:dialog header="Test Dialog"
widgetVar="testDialog">
<h:form>
<p:inputText value="#{mbean.someValue}"/>
<p:commandButton value="Save"
onsuccess="testDialog.hide()"
actionListener="#{mbean.saveMethod}"/>
</h:form>
</p:dialog>
What I want to be able to do is have the mbean.saveMethod somehow prevent the dialog from closing if there was some problem and only output a message through growl. This is a case where a validator won't help because there's no way to tell if someValue is valid until a save is submitted to a back end server. Currently I do this using the visible attribute and point it to a boolean field in mbean. That works but it makes the user interface slower because popping up or down the dialog requires hitting the server.
The onsuccess runs if ajax request itself was successful (i.e. there's no network error, uncaught exception, etc), not if action method was successfully invoked.
Given a <p:dialog widgetVar="yourWidgetVarName">, you could remove the onsuccess and replace it by PrimeFaces RequestContext#execute() inside saveMethod():
if (success) {
RequestContext.getCurrentInstance().execute("PF('yourWidgetVarName').hide()");
}
Note: PF() was introduced in PrimeFaces 4.0. In older PrimeFaces versions, you need yourWidgetVarName.hide() instead.
If you prefer to not clutter the controller with view-specific scripts, you could use oncomplete instead which offers an args object which has a boolean validationFailed property:
<p:commandButton ...
oncomplete="if (args && !args.validationFailed) PF('yourWidgetVarName').hide()" />
The if (args) check is necessary because it may be absent when an ajax error has occurred and thus cause a new JS error when you try to get validationFailed from it; the & instead of & is mandatory for the reason explained in this answer, refactor if necessary to a JS function which you invoke like oncomplete="hideDialogOnSuccess(args, 'yourWidgetVarName')" as shown in Keep <p:dialog> open when validation has failed.
If there is however no validation error and the action method is successfully triggered, and you would still like to keep the dialog open because of e.g. an exception in the service method call, then you can manually trigger validationFailed to true from inside backing bean action method by explicitly invoking FacesContext#validationFailed(). E.g.
FacesContext.getCurrentInstance().validationFailed();
Using the oncomplete attribute from your command button and really simple script will help you a lot.
Your dialog and command button would be something similar to this:
<p:dialog widgetVar="dialog">
<h:form id="dialogView">
<p:commandButton id="saveButton" icon="ui-icon-disk"
value="#{ui['action.save']}"
update=":dataList :dialogView"
actionListener="#{mbean.save()}"
oncomplete="handleDialogSubmit(xhr, status, args)" />
</h:form>
</p:dialog>
An the script would be something like this:
<script type="text/javascript">
function handleDialogSubmit(xhr, status, args) {
if (args.validationFailed) {
dialog.show();
} else {
dialog.hide();
}
}
</script>
I've just googled up this solution. Basically the idea is to use actionListener instead of button's action, and in backing bean you add callback parameter which will be then check in button's oncomplete method. Sample partial code:
JSF first:
<p:commandButton actionListener="#{myBean.doAction}"
oncomplete="if (!args.validationFailed && args.saved) schedulesDialog.hide();" />
Backing bean:
public void doAction(ActionEvent actionEvent) {
// do your stuff here...
if (ok) {
RequestContext.getCurrentInstance().addCallbackParam("saved", true);
} else {
RequestContext.getCurrentInstance().addCallbackParam("saved", false);
}
}
Hope this helps someone :)
I use this solution:
JSF code:
<p:dialog ... widgetVar="dlgModify" ... >
...
<p:commandButton value="Save" update="#form" actionListener="#{AdminMB.saveTable}" />
<p:commandButton value="Cancel" oncomplete="PF('dlgModify').hide();"/>
Backing bean code:
public void saveTable() {
RequestContext rc = RequestContext.getCurrentInstance();
rc.execute("PF('dlgModify').hide()");
}
I believe this is the cleanest solution.
Doing this you don't need to change your buttons code.
This solution overrides the hide function prototype.
$(document).ready(function() {
PrimeFaces.widget.Dialog.prototype.originalHide = PrimeFaces.widget.Dialog.prototype.hide; // keep a reference to the original hide()
PrimeFaces.widget.Dialog.prototype.hide = function() {
var ajaxResponseArgs = arguments.callee.caller.arguments[2]; // accesses oncomplete arguments
if (ajaxResponseArgs && ajaxResponseArgs.validationFailed) {
return; // on validation error, prevent closing
}
this.originalHide();
};
});
This way, you can keep your code like:
<p:commandButton value="Save" oncomplete="videoDetalheDialogJS.hide();"
actionListener="#{videoBean.saveVideo(video)}" />
The easiest solution is to not have any "widget.hide", neither in onclick, neither in oncomplete. Remove the hide functions and just put
visible="#{facesContext.validationFailed}"
for the dialog tag

Keep p:dialog open when a validation error occurs after submit

Minimal example dialog:
<p:dialog header="Test Dialog"
widgetVar="testDialog">
<h:form>
<p:inputText value="#{mbean.someValue}"/>
<p:commandButton value="Save"
onsuccess="testDialog.hide()"
actionListener="#{mbean.saveMethod}"/>
</h:form>
</p:dialog>
What I want to be able to do is have the mbean.saveMethod somehow prevent the dialog from closing if there was some problem and only output a message through growl. This is a case where a validator won't help because there's no way to tell if someValue is valid until a save is submitted to a back end server. Currently I do this using the visible attribute and point it to a boolean field in mbean. That works but it makes the user interface slower because popping up or down the dialog requires hitting the server.
The onsuccess runs if ajax request itself was successful (i.e. there's no network error, uncaught exception, etc), not if action method was successfully invoked.
Given a <p:dialog widgetVar="yourWidgetVarName">, you could remove the onsuccess and replace it by PrimeFaces RequestContext#execute() inside saveMethod():
if (success) {
RequestContext.getCurrentInstance().execute("PF('yourWidgetVarName').hide()");
}
Note: PF() was introduced in PrimeFaces 4.0. In older PrimeFaces versions, you need yourWidgetVarName.hide() instead.
If you prefer to not clutter the controller with view-specific scripts, you could use oncomplete instead which offers an args object which has a boolean validationFailed property:
<p:commandButton ...
oncomplete="if (args && !args.validationFailed) PF('yourWidgetVarName').hide()" />
The if (args) check is necessary because it may be absent when an ajax error has occurred and thus cause a new JS error when you try to get validationFailed from it; the & instead of & is mandatory for the reason explained in this answer, refactor if necessary to a JS function which you invoke like oncomplete="hideDialogOnSuccess(args, 'yourWidgetVarName')" as shown in Keep <p:dialog> open when validation has failed.
If there is however no validation error and the action method is successfully triggered, and you would still like to keep the dialog open because of e.g. an exception in the service method call, then you can manually trigger validationFailed to true from inside backing bean action method by explicitly invoking FacesContext#validationFailed(). E.g.
FacesContext.getCurrentInstance().validationFailed();
Using the oncomplete attribute from your command button and really simple script will help you a lot.
Your dialog and command button would be something similar to this:
<p:dialog widgetVar="dialog">
<h:form id="dialogView">
<p:commandButton id="saveButton" icon="ui-icon-disk"
value="#{ui['action.save']}"
update=":dataList :dialogView"
actionListener="#{mbean.save()}"
oncomplete="handleDialogSubmit(xhr, status, args)" />
</h:form>
</p:dialog>
An the script would be something like this:
<script type="text/javascript">
function handleDialogSubmit(xhr, status, args) {
if (args.validationFailed) {
dialog.show();
} else {
dialog.hide();
}
}
</script>
I've just googled up this solution. Basically the idea is to use actionListener instead of button's action, and in backing bean you add callback parameter which will be then check in button's oncomplete method. Sample partial code:
JSF first:
<p:commandButton actionListener="#{myBean.doAction}"
oncomplete="if (!args.validationFailed && args.saved) schedulesDialog.hide();" />
Backing bean:
public void doAction(ActionEvent actionEvent) {
// do your stuff here...
if (ok) {
RequestContext.getCurrentInstance().addCallbackParam("saved", true);
} else {
RequestContext.getCurrentInstance().addCallbackParam("saved", false);
}
}
Hope this helps someone :)
I use this solution:
JSF code:
<p:dialog ... widgetVar="dlgModify" ... >
...
<p:commandButton value="Save" update="#form" actionListener="#{AdminMB.saveTable}" />
<p:commandButton value="Cancel" oncomplete="PF('dlgModify').hide();"/>
Backing bean code:
public void saveTable() {
RequestContext rc = RequestContext.getCurrentInstance();
rc.execute("PF('dlgModify').hide()");
}
I believe this is the cleanest solution.
Doing this you don't need to change your buttons code.
This solution overrides the hide function prototype.
$(document).ready(function() {
PrimeFaces.widget.Dialog.prototype.originalHide = PrimeFaces.widget.Dialog.prototype.hide; // keep a reference to the original hide()
PrimeFaces.widget.Dialog.prototype.hide = function() {
var ajaxResponseArgs = arguments.callee.caller.arguments[2]; // accesses oncomplete arguments
if (ajaxResponseArgs && ajaxResponseArgs.validationFailed) {
return; // on validation error, prevent closing
}
this.originalHide();
};
});
This way, you can keep your code like:
<p:commandButton value="Save" oncomplete="videoDetalheDialogJS.hide();"
actionListener="#{videoBean.saveVideo(video)}" />
The easiest solution is to not have any "widget.hide", neither in onclick, neither in oncomplete. Remove the hide functions and just put
visible="#{facesContext.validationFailed}"
for the dialog tag

Resources