Primefaces: Input Text / Overlay panel / Retain focus - jsf

We have a p:inputText element that should display an overlay panel for various options. (Its a global search, so you can tick categories to search in)
Users usually click in the textbox, start typing and THEN look at the screen again
The Problem is: As soon as the overlay panel is shown, the textbox looses its focus.
<p:inputText id="searchItem"></p:inputText>
<p:overlayPanel id="gsOverlay" for="searchItem" my="left top"
at="left bottom" dynamic="true"
onShow="resizeGSOverlay();">
So i tried to fix this, by immediately focusing back on the "search" inputtext using
<p:overlayPanel id="gsOverlay" for="searchItem" my="left top"
at="left bottom" dynamic="true"
onShow="PrimeFaces.focus('globalSearchForm:searchItem'); resizeGSOverlay();">
However, there is a split second, where the inputfield lost focus, leading to searches missing the first charater.
Can i display the overlay panel, without having the inputtext loosing its focus? (Each component inside the overlay panel will focus back after clicking, that's fast enough - just the initial focus-back is to slow)

Just found the "holy grail":
Default for the overlayPanel is:
PrimeFaces.widget.OverlayPanel.prototype.applyFocus = function(){
this.jq.find(':not(:submit):not(:button):input:visible:enabled:first').focus();
}
so, I just put the following javascript AFTER including the primefaces resources, which will then override the default implementation:
<script type="text/javascript">
PrimeFaces.widget.OverlayPanel.prototype.applyFocus = function() {
if (this.id == "globalSearchForm:gsOverlay")
return;
else
this.jq.find(':not(:submit):not(:button):input:visible:enabled:first').focus();
}
</script>
So - No focus for any element within the overlay panel in question once it becomes visible. Works like a charm.
Update:
using the Proxy Pattern (http://api.jquery.com/Types/#Proxy_Pattern) seems a more reliable solution, as it avoids the need to duplicate the content of the original implementation, which might be different in one of the next Primefaces releases:
<script type="text/javascript">
(function() {
var proxied = PrimeFaces.widget.OverlayPanel.prototype.applyFocus;
PrimeFaces.widget.OverlayPanel.prototype.applyFocus = function(){
if (this.id == "globalSearchForm:gsOverlay")
return;
return proxied.apply(this, arguments);
};
})();
</script>

Related

Primefaces NotificationBar close icon not visible

In the documentation of primefaces, it is said that "Note that notificationBar has a default built-in close icon to hide the content.". But so far I could not get it displayed ? Is there a special property or facet required to show the close icon ?
pf version I am using is 6.2
If you see the notification.js resource inside the Primefaces library, you can see that they took into account to give to the close icon the "hide functionality":
primefaces-6_2\src\main\resources\META-INF\resources\primefaces\notificationbar\notificationbar.js =>
/**
* PrimeFaces NotificationBar Widget
*/
PrimeFaces.widget.NotificationBar = PrimeFaces.widget.BaseWidget.extend({
init: function(cfg) {
this._super(cfg);
var _self = this;
//relocate
this.jq.css(this.cfg.position, '0').appendTo($('body'));
//display initially
if(this.cfg.autoDisplay) {
$(this.jq).css('display','block')
}
//bind events
this.jq.children('.ui-notificationbar-close').click(function() {
_self.hide();
});
},
So, considering the previous code, if a children component has the ui-notificationbar-close class and you click on it, the NotificationBar component will be hided calling to hide function automatically (without having to use the PF(widgetVar).hide().
I have tested with the following code and in effect, the notificationbar disappears after clicking on the close icon:
<p:notificationBar id="notificationBar" position="top" effect="slide" styleClass="top" widgetVar="myNotificationBarWV" autoDisplay="false">
<i class="ui-icon ui-icon-closethick ui-notificationbar-close"></i>
<h:outputText value="You Rock!" style="font-size:1.5 rem;"/>
</p:notificationBar>

How to temporarily stop refreshing Primefaces line chart when zooming

I am using JSF/Primefaces p:chart to display a dynamic line chart that updates periodically (e.g. with an interval of 3 seconds) via p:poll tag according to the real-time data, and the zoom property is set to true. Now, the problem is that I want the chart to temporarily stop updating when I zoom and resume when I double-click to reset zoom. However, the updated chart always override the zoomed chart that I am watching. How can I configure the chart to let it stop refreshing when zooming?
I have solved the problem, it turns out that both Zoom and ResetZoom have events, so I can bind javascript functions to these events in order to stop and start p:poll.
Let me share my solution as the following code snippet.
index.xhtml
<h:form id="dashboardForm">
<p:poll interval="3" update="chart" process="#this" global="false" ignoreAutoUpdate="true" widgetVar="chartpoll"/>
<p:chart id="chart" type="line" model="#{chartView.zoomModel}" style="height:300px;" widgetVar="chart" responsive="true"/>
</h:form>
<script type="text/javascript">
function lineChartExtender() {
$('#dashboardForm\\:chart').bind('jqplotZoom', function(ev, gridpos, datapos, plot, cursor){
PF('chartpoll').stop();
});
$('#dashboardForm\\:chart').bind('jqplotResetZoom', function(ev, gridpos, datapos, plot, cursor){
PF('chartpoll').start();
});
}
</script>
ChartView.java
LineChartModel model = new LineChartModel();
model.setZoom(true);
model.setExtender("lineChartExtender");

calling a Javascript function that makes a click on a (hidden) button or link

I call a javascript function that makes a click on a hidden commandButton but the click run when the page loads, not when I call the function.
this is the html code :
<p:inputText id="aa" value="#{bonBonneManagedBean.sel}" onkeyup="fnc()" />
<h:commandButton id="hh" onclick="#{bonBonneManagedBean.kk()}" style="display:none"/>
and the javaScript function :
function fnc()
{
length = document.getElementById('form:aa').value.length;
if(length == 10)
{
$("#hh").click();
document.getElementById('form:aa').value = "";
}
}
You should use the action attribute instead of onclick like this
<h:commandButton id="hh" action="#{bonBonneManagedBean.kk()}" style="display:none"/>
Note that you might have to add form prefix to the selector, like this
$("#myFormId\\:hh").click();
The same would work for a commandLink or any other 'clickable' component.
Must there be a button on this page(which I doubt, seeing as you're hiding it anyways)? You're better served using something like <p:remoteCommand/> here
Ditch the hidden button and replace with
<p:remoteCommand name="fnc" actionListener="#{bonBonneManagedBean.kk}"/>
But, if it's an absolute requirement, you can very easily emulate a button click in js with the click() method from your function you need to
Change the <h:commandButton/> to a <p:commandButton/> so you can assign the button a widgetVar attribute(to guarantee the name of the element in the DOM). The widgtetVar attribute can be set on almost all primefaces components to make your life easier
Using the widgetVar, just call the click() method in your function
<p:commandButton ajax="false" widgetVar="theButton" id="hh" action="#{bonBonneManagedBean.kk()}" style="display:none"/>
<p:inputText id="aa" widgetVar="theTextBox" value="#{bonBonneManagedBean.sel}" onkeyup="fnc()" />
and in the function:
function fnc(){
length = theTextBox.value.length;
if(length == 10){
theButton.click()
theTextBox.value = "";
}
}
If the top answers from Daniel and Kolossus doesn't help out someone: I found that characters got put in front of the id I set in my case, therefore this helped me:
$('[id$=hh]').click();
Basically the selector is saying the id ends with 'hh' but may not be the full id.
This is also helpful in SF development as the same thing happens with comandButtons, etc.

How to do double-click prevention in JSF

We have a few search pages that run against a lot of data and take a while to complete. When a user clicks on the search button, we'd like to not allow them to submit the search result a second time.
Is there a best practice for doing "double-click" detection/prevention in JSF?
The PrimeFaces component seems like it can do what we want as it will disable the UI for a period of time between when the search button is clicked and when the search completes, but is there a more generic strategy we can use (perhaps something that isnt reliant on PrimeFaces)? Ideally, any click of the button will either be disabled or disregarded until the search completes. We dont necessarily need to disable the entire UI (as blockUI allows you to do).
If you're using solely ajax requests, you could use jsf.ajax.addOnEvent handler of the JSF JavaScript API for this. The below example will apply on all buttons of type="submit".
function handleDisableButton(data) {
if (data.source.type != "submit") {
return;
}
switch (data.status) {
case "begin":
data.source.disabled = true;
break;
case "complete":
data.source.disabled = false;
break;
}
}
jsf.ajax.addOnEvent(handleDisableButton);
Alternatively, if you need this on specific buttons only, use the onevent attribute of <f:ajax>.
<h:commandButton ...>
<f:ajax ... onevent="handleDisableButton" />
</h:commandButton>
If you also need to apply this on synchronous requests, then you need to take into account that when you disable a button during onclick, then the button's name=value pair won't be sent as request parameter and hence JSF won't be able to identify the action and invoke it. You should thus only disable it after the POST request has been sent by the browser. There is no DOM event handler for this, you'd need to use the setTimeout() hack which disables the button ~50ms after click.
<h:commandButton ... onclick="setTimeout('document.getElementById(\'' + this.id + '\').disabled=true;', 50);" />
This is only rather brittle. It might be too short on slow clients. You'd need to increase the timeout or head to another solution.
That said, keep in mind that this only prevents double submits when submitting by a web page. This does not prevent double submits by programmatic HTTP clients like URLConnection, Apache HttpClient, Jsoup, etc. If you want to enforce uniqueness in the data model, then you should not be preventing double submits, but preventing double inserts. This can in SQL easily be achieved by putting an UNIQUE constraint on the column(s) of interest.
See also:
Pure Java/JSF implementation for double submit prevention
How to handle multiple submits before response is rendered?
You can use 'onclick' and 'oncomplete' listeners. When user click on button - disable it. When action completed - enable.
<p:commandButton id="saveBtn"
onclick="$('#saveBtn').attr('disabled',true);"
oncomplete="$('#saveBtn').attr('disabled',false);"
actionListener="#{myBean.save}" />
i came upon this question, having the same problem. The solution did not work for me - after a brief look at primefaces.js i guess they do not use jsf.ajax there anymore.
so i had to work something out myself and here is my solution, for people who also can not use the one in the answer by BalusC:
// we override the default send function of
// primeFaces here, so we can disable a button after a click
// and enable it again after
var primeFacesOriginalSendFunction = PrimeFaces.ajax.AjaxUtils.send;
PrimeFaces.ajax.AjaxUtils.send = function(cfg){
var callSource = '';
// if not string, the caller is a process - in this case we do not interfere
if(typeof(cfg.source) == 'string') {
callSource = jQuery('#' + cfg.source);
callSource.attr('disabled', 'disabled');
}
// in each case call original send
primeFacesOriginalSendFunction(cfg);
// if we disabled the button - enable it again
if(callSource != '') {
callSource.attr('disabled', 'enabled');
}
};
None of alternatives above has worked for me (I've really tried each one of them). My form was always sent twice when user double-clicked the login button. I'm working with JSF (Mojarra 2.1.6) on Glassfish 3.1.2.
Consider that it was a non-AJAX login page.
So here's the way I solved it:
define a global JavaScript var to control submition in the page header or anywhere outside your form:
var submitting = false;
set it to true when submit h:form onsubmit event is fired:
<h:form onsubmit="submitting = true">
Check the var's value on h:commandLink's click event:
<h:commandLink ... onclick="if(submitting){return false}">
This is just another simple alternative and it was tested in Chrome [Version 47.0.2526.106 (64-bit)], Mozilla Firefox (37.0.2) and Internet Explorer 11. I hope it helps someone.
For me works this way:
<h:commandLink ... onclick="jQuery(this).addClass('ui-state-disabled')">
PrimeFaces 12 and up
From PrimeFaces 12, p:commandButtons are disabled by default when they trigger an Ajax request. The button is enabled again when the Ajax request is finished.
To disable this default behavior, use disableOnAjax="false".
See a demo at: https://www.primefaces.org/showcase/ui/button/commandButton.xhtml
PrimeFaces 11 and lower
The approach by BalusC is great, but if you are using PrimeFaces you'll run into styling issues. Because some classes are not toggled, the button will not look disabled.
If you are looking for a solution which takes care of styling as well, you can replace the CommandButtonRenderer with one that disables the button on click using the button's widget to disable and enable it.
PrimeFaces Extensions 8 or up contains such a renderer. You can add this to your faces-config.xml like:
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.CommandButtonRenderer</renderer-type>
<renderer-class>org.primefaces.extensions.renderer.CommandButtonSingleClickRenderer</renderer-class>
</renderer>
</render-kit>
You can see it in action in the showcase.
If you cannot or don't want to use PFE, you can add the render class to your project by getting it from:
https://github.com/primefaces-extensions/primefaces-extensions/blob/master/core/src/main/java/org/primefaces/extensions/renderer/CommandButtonSingleClickRenderer.java
Note: this still requires you to add the renderer to your faces-config.xml.
See also
How to use resolveWidgetVar before PrimeFaces 8?
very useful solution jsf-primefaces, used with facelets template spreads to other pages consumers
<f:view>
<Script language="javascript">
function checkKeyCode(evt)
{
var evt = (evt) ? evt : ((event) ? event : null);
var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
if(event.keyCode==116)
{
evt.keyCode=0;
return false
}
}
document.onkeydown=checkKeyCode;
function handleDisableButton(data) {
if (data.source.type != "submit") {
return;
}
switch (data.status) {
case "begin":
data.source.disabled = true;
break;
case "complete":
data.source.disabled = false;
break;
}
}
jsf.ajax.addOnEvent(handleDisableButton);
</Script>
</f:view>
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="./resources/css/default.css" rel="stylesheet" type="text/css" />
<link href="./resources/css/cssLayout.css" rel="stylesheet" type="text/css" />
<title>infoColegios - Bienvenido al Sistema de Administracion</title>
</h:head>
<h:body onload="#{login.validaDatos(e)}">
<p:layout fullPage="true">
<p:layoutUnit position="north" size="120" resizable="false" closable="false" collapsible="false">
<p:graphicImage value="./resources/images/descarga.jpg" title="imagen"/>
<h:outputText value="InfoColegios - Bienvenido al Sistema de Administracion" style="font-size: large; color: #045491; font-weight: bold"></h:outputText>
</p:layoutUnit>
<p:layoutUnit position="west" size="175" header="Nuestra InstituciĆ³n" collapsible="true" effect="drop" effectSpeed="">
<p:menu>
<p:submenu>
<p:menuitem value="Quienes Somos" url="http://www.primefaces.org/showcase-labs/ui/home.jsf" />
</p:submenu>
</p:menu>
</p:layoutUnit>
<p:layoutUnit position="center">
<ui:insert name="content">Content</ui:insert>
</p:layoutUnit>
</p:layout>
</h:body>
Did a simple work with hide and show, works well with element having input type submit Jquery
$(":submit").click(function (event) {
// add exception to class skipDisable
if (!$(this).hasClass("skipDisable")) {
$(this).hide();
$(this).after("<input type='submit' value='"+$(this).val()+"' disabled='disabled'/>");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<form>
<input type="submit" value="hello">
</form>
I addressed this issue by simply hiding the button after clicking it:
<p:commandButton... onclick="jQuery(this).css('visibility','hidden')" />

Getting the co-ordinates of a component

How to get the co-ordinates of a JSF component after onclick event ? I need to place an overlay just below an icon that was clicked.
Is there any such in-built facility provided by JSF rather than manually writing a javascript function for that ?
No, there isn't. The position is browser (client) dependent. In the client side there is also totally no means of JSF, it's all just plain HTML/CSS/JS. The location can only be extracted from the HTML DOM element. You'd have to pass it from JS to JSF or do the business job fully in JS instead of JSF.
As PrimeFaces ships with jQuery, your best way to retrieve the element position relative to the document is using jQuery.offset().
var $element = jQuery('#someid');
var offset = $element.offset();
var x = offset.left;
var y = offset.top;
// ...
In addition to BalusC's answer, here is an example, where a click on a component with id=placeholder (maybe a link or button) will open another component (id=menu) directly below the triggering component. If the mouse is moved away from the triggering component, the menu will disappear:
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery("#placeholder").click(function(event) {
//get the position of the placeholder element
var pos = jQuery(this).offset();
var height = jQuery(this).height();
//show the menu directly below the placeholder
jQuery("#menu").css( { "left": pos.left + "px", "top": (pos.top + height) + "px" } );
jQuery("#menu").show();
});
// hide the menu on mouseout
jQuery("#placeholder").mouseout(function(event) {
jQuery("#menu").hide();
});
});
</script>
The component with id=menu can be a div or a jsf h:panelGroup or any other component. The style attribute with display: none will initially hide the component:
<h:panelGroup style="position: absolute; display: none;" id="menu">
<!-- content here -->
</h:panelGroup>
Make sure that the jQuery id selector gets the correct id of the component. E.g. if your elements are inside a form with id=form1, the jQuery call has to look something like this:
jQuery("#form1\\:placeholder").click(function(event)
Notice the double backslash.
I'd recommend using a JSF component library like RichFaces or IceFaces. Many of them have AJAX capabilities and JavaScript library integration, and therefore have components for doing that kind of thing.

Resources