I am using primefaces datatable to show my data in UI. As we all know., we can do sorting and filtering in the data table itself. But it starts to search the data when I type a single character in the datatable sorting field, I dont want it. I need to search the data only when the user types atleast 3 characters in the field. Is it possible to do that..? If so., in what way? please provide your comments on that.
Thanks in advance.
I did a small investigation of Primefaces' data table and here are my findings.
Actually, recompilation is not necessary, source javascript substitution as well.
You need to register a new handler for filter event instead of the the one provided by Primefaces.
In this case a data table will be used like this:
<h:outputScript name="js/customprimefacestable.js" target="body"/>
<p:dataTable var="data" value="#{filterBean.data}" filteredValue="#{filterBean.filteredData}" widgetVar="tableWidget">
<p:column filterBy="#{data.name}" headerText="Name" filterMatchMode="contains">
<h:outputText value="#{data.name}" />
</p:column>
<p:column filterBy="#{data.value}" headerText="Value" filterMatchMode="contains">
<h:outputText value="#{data.value}" />
</p:column>
...
</p:dataTable>
And the javascript will be like this:
$(document).ready(function() {
tableWidget.thead.find('> tr > th.ui-filter-column > .ui-column-filter').each(function() {
var filter = $(this);
if(filter.is('input:text')) {
if(tableWidget.cfg.filterEvent!="enter"){
//unbind current handler
filter.unbind(tableWidget.cfg.filterEvent);
//bind new handler that accounts for conditional filtering
filter.bind(tableWidget.cfg.filterEvent, function(c) {
if(filter.val().length > 3) {
//Primefaces 3.5 implementation
if(tableWidget.filterTimeout){
clearTimeout(tableWidget.filterTimeout);
}
tableWidget.filterTimeout=setTimeout(function(){
tableWidget.filter();
tableWidget.filterTimeout=null},
tableWidget.cfg.filterDelay);
}
});
}
}
});
});
Things to note:
target="body": the javascript must not be executed in the <head>, because Primefaces initializes its widget variables in $(document).ready(), so it is not guaranteed that your function will execute after the Primefaces initialization has taken place;
as the filtering starts with at least 4 characters typed in column's search box (done), you should restore unfiltered view when user deletes text to below 4 characters by yourself (not done);
the solution above is aimed at Primefaces 3.5 <p:dataTable>. Primefaces implementation varies from version to version, so be sure to check out the implementation of the version you are using, or upgrade to version 3.5;
implementation of filtering events with input fields rendered as dropdown boxes is not covered;
the table will listen to the default (keyup) event.
Related
I need to make a calculator in a jsf view using only client-side part. No data can be passed to server-side.
I have the view splitted in a couple of <h:form> with calculator in the middle:
<h:form id="customer_form">
// view here working nice
</h:form>
<h:panelGroup layout="block" styleClass="ui-grid-col-2 offset-boxes" >
<p:panel id="calculator" header="Calculadora" styleClass="half-screen-height calculator-table">
<h:inputText id="result" widgetVar="result" styleClass="result-text"/>
<p:button value="X" widgetVar="X" ajax="false" onclick="calculate('x')" styleClass="right"></p:button>
// more calculator buttons
</p:panel>
</h:panelGroup>
<h:form id="consumption_form">
// view here working nice
</h:form>
Until here all is ok, but when I try to manage calculator with javascript:
<script type="text/javascript">
function calculate(sel){
if (sel == "x") {
document.getElementById("result").value = "";
} else {
var input = document.getElementById("result").value;
document.getElementById("result").setAttribute("value", input + sel);
}
}
</script>
Each time i change result value, server-side is called and value is reset to original value.
EDIT according to PrimeFaces documentation of p:button::onclick event
Client side callback to execute when button is clicked.
Either <h:button> tag is acting like this, so I guess problem is the way JSF or PrimeFaces are handling the javascript event...
I also tried to call and set input value by jquery with $("result").value but the result i get is a function(b7) not the value itself.
What am I doing wrong?
Thanks! ;)
J
you got to make sure you're JS code will not try to submit the form, so that will trigger a server side call, for that I suggest you return false and try using event.preventDefault() on your HTML buttons onclick calls, so you will avoid bubbling up the event to any listeners.
I am facing a problem where i want to test that whether a user has selected any record or not from the multiple page in rich:dataTable. These pages are created through rich:datascroller. Related code is given below.
<rich:datascroller id="dataScroller" selectedStyleClass="myClass" renderIfSinglePage="false"
align="center" for="dataList" maxPages="10"
pageIndexVar="currentPage" pagesVar="totalPages" reRender="pageNum,dataList" ajaxSingle="false">
<f:facet name="first"><h:outputText value="First" /></f:facet>
<f:facet name="last"><h:outputText value="Last" /></f:facet>
</rich:datascroller>
<h:inputHidden id="pageNum" value="#{currentPage}"></h:inputHidden>
<h:inputHidden id="numOfRows" value="10"></h:inputHidden>
The problem is that if user selects a record (say on 3rd page) and after that he moves to page one. Now in this case when i try to iterate through the table in Java Script to know that whether he has selected any record or not than it always allows me to iterate till the current page on which i am and hence i am unable to give user the proper alert message for selecting a record first on pressing of submit button. All i want is to know that user has checked the check box against a record on any page from the available pages , irrespective of the fact that on which page is currently opened. Any ideas?
Try it via the Richfaces Javascript API.
For every component you have in your page, there's a representing Javascript object, named as next:
Richfaces_componentId (small 'f' for faces).
Using that and to understand it live, go to the ScrollableDatatable Live Demo, open your chrome browser console, and execute this:
Richfaces_ScrollableGrid_j_id353_carList.selectionManager.selection.ranges
This will print out an empty javascript array.
Try selecting some rows, and running that line again, you'll have an array containing the indexes of your selected rows.
So to put that in a function:
<script>
function tableHasSelection() {
return Richfaces_ScrollableGrid_j_id353_carList.selectionManager.selection.ranges.length != 0;
}
</script>
Now you call this function via onclick of your submit button, and return false in case of no selection to prevent submission.
Of course you would replace Richfaces_ScrollableGrid_j_id353_carList with your component's javascript representing object.
Hopefully this will help.
[UPDATE]
According to our discussion in the comments, here is an update that might be helpful.
Your data table would be like this:
<rich:extendedDataTable value="..." var="element" id="table" selectionMode="..." selection="...">
<rich:column>
<h:selectBooleanCheckbox value="..." onclick="toggleSelection(this, #{element.id});" />
</rich:column>
</rich:extendedDataTable>
<h:commandButton value="Submit" action="#{...}" onclick="return tableHasSelection();" />
And your script would have these functions:
<script>
var selectedElements = [];
function toggleSelection(checkbox, id) {
if ($(checkbox).is(':checked')) {
//add selected row if checkbox is checked
selectedElements.push(id);
} else {
//or find it in the array and remove it
var index = selectedElements.indexOf(id);
if (index > -1) {
array.splice(index, 1);
}
}
}
function tableHasSelection() {
return selectedElements.length != 0;
}
</script>
(<rich:extendedDataTable> has selectable rows, you may want to look at that)
One thing you can do is to submit the selection when the user clicks it. E.g.
<h:selectBooleanCheckbox onclick="saveSelection(#{rowId})">
…
<a4j:jsFunction name="saveSelection">
<a4j:param name="id" assignTo="#{bean.selectedId}">
</a4j:jsFunction>
But if the user will only be choosing one row then simply say if the selected row is not on the current page the user didn't select anything, I don't think that's a wrong approach.
i use JSF 2.0 and primefaces 4.0
my JSF code :
<c:forEach items="#{materielbean.materielist}" var="list" >
<p:dataTable var="car" value="#{materielbean.listeitemsmaterielbyidmateriel(list.idmateriel)}" rowKey="#{car.iditemsmateriel}"
selection="#{fournisseurbean.selectedItemsMateriel}" selectionMode="multiple" style="width : 664px; ">
<f:facet name="header">
#{list.nommateriel}
</f:facet>
<p:column headerText="designation">
#{car.designation}
</p:column>
<p:column headerText="Unité">
#{car.unite}
</p:column>
</p:dataTable>
</c:forEach>
and the used function
listeitemsmaterielbyidmateriel(list.idmateriel)
is defined like this
public List listeitemsmaterielbyidmateriel(int i){
return itemmaterielDAO.DisplayItemsMaterielDAOselonmMateriel(i);
}
And finally this is the DAO code
public List DisplayItemsMaterielDAOselonmMateriel(int idmateriel )
{
Query q = em.createQuery("select LIM from ItemsMateriel LIM inner join LIM.materiel where LIM.materiel.idmateriel= :idmateriel");
q.setParameter("idmateriel", idmateriel);
List l = new ArrayList();
l= q.getResultList();
return l ;
}
when i run this code , i got no errors , but selection="#fournisseurbean.selectedItemsMateriel}"
returns only the values of the final iteration(final datatable) , it's because in every iteration the list crashes old selected objects and put new selected objects , how can i do to prevent this ??
It's dangerous to mix JSTL core tags with JSF- and/or *Faces tags. Basically JSTL tag handler get executed during view build time, while JSF UIComponents get executed during view render time. See the very good answer at "JSTL in JSF2 Facelets… makes sense?"
Have you already tried to solve the issue using Facelets ui:repeat component?
Well sorry for late , but i discovored that the solution is easier than i thought :
First create a new list
List<ItemsMateriel> Tampon = new ArrayList<ItemsMateriel>();
Second modify the setter to :
public void setSelectedItemsMateriel(List<ItemsMateriel> selectedItemsMateriel) {
Tampon.addAll(selectedItemsMateriel);
this.selectedItemsMateriel = selectedItemsMateriel;
}
And finally to use the new list (Tampon) and you'll find all selected objects
I have an autocomplete event that fires correctly once a value is selected. I want another event to fire once I erase the value in the textbox and reset the value to null. I was thinking of using the onChange attribute but I was having issues so I reverted back to my original code.
<p:autoComplete id="deviceAuto" dropdown="true" scrollHeight="250"
value="#{summaryReportController.device.nickname}"
forceSelection="true"
completeMethod="#{summaryReportController.deviceComplete}">
<p:ajax event="itemSelect"
listener="#{summaryReportController.handleDeviceSelect}"
update="printThis" />
</p:autoComplete>
public void handleDeviceSelect(SelectEvent event) {
String deviceSelect = event.getComponent().getId();
if (deviceSelect.equalsIgnoreCase("deviceAuto")) {
Device selectedDevice = deviceMgr.getDevicebyNickname(device.getNickname());
setDevice(selectedDevice);
}
updateInterface();
}
When you modify the text content of the AutoComplete textfield, the search method (aka. completeMethod) will be called on the backing bean. You can reset the value to null there if you get an empty string.
Backing Bean
// insert getter and setter for the device property ...
/** Search for devices by name */
public List<String> deviceComplete(String search) {
if (StringUtils.isBlank(search)) {
setDevice(null); // textfield was cleared, reset device value!
return Collections.emptyList();
} else {
// search for devices ...
return deviceNames;
}
}
Note that I used Apache Commons StringUtils.isBlank(String) to check if the string was null or did only contain whitespace characters.
JSF View
In your XHTML file you probably want to listen to any Ajax event to update your view -- or you figure out the event you need (blur, change, whatever) yourself:
<p:autoComplete ...>
<p:ajax event="itemSelect" listener="..." update="..." />
<p:ajax process="#this" update="..." />
</p:autocomplete>
I hope this helps.
An alternative could be something like a "clear" or "reset" button next to the search textfield to make it clear to the user that the value will be cleared.
The default autoComplete minQueryLength attribute equals 1 and your search string will be updated when you deleting it until it has lenght of 1 character.
E.g.:
You entering 'foo' - and this string is provided to search method (updating after entering first character - minQueryLength = 1)
But when you delete search string - it is also updated until it will have length of 1.
Solution:
set attribute minQueryLength="0"
Or:
if you need bigger value add to your autoCompleteMethod(String search) condition:
if (search.length()<={your minQueryLength attribute} ) field = null;
Old question, but I think it worths another view.
The problem with minQueryLenth = 0 or minQueryLenth = 1 is that it can return hundreds of options (and for sure the user won't read all of them to choose one). My solution was as follows.
First of all I need the input to be sent to the server as soon as the user select one of its values (in my use case the user is not allowed to go to next step in a wizard if this value is null or empty). So I put an ajax function triggered in the event of a selected value.
xhtml:
<p:autoComplete
id="someId"
value="#{myViewScopedBean.selectedValue}"
...
...
minQueryLenth="5"
onblur="autoCompleteLostFocus('someId', 'someCommand()')">
<p:ajax
event="itemSelect"
listener="#{myViewScopedBean.newValueSelected}"
process="#this"
update="commandButtonGoToNextStep" />
</p:autoComplete>
<p:remoteCommand
name="someCommand"
actionListener="#{myViewScopedBean.setValueNull}"
update="commandButtonGoToNextStep" />
<p:commandButton
id="commandButtonGoToNextStep"
...
...
disabled="#{myViewScopedBean.selectedValue == null}" />
If the user clean the text, I need to send that value to "myViewScopedBean" and update the component that allows the user to go to the next step. I solved that putting a javascript function that is called when the autocomplete lose focus.
javascript:
function autoCompleteLostFocus(autocompleteId, comand) {
if ($("[id='" + autocompleteId + "_input']").val().trim() == "") {
eval(comando);
}
}
in myViewScopedBean:
public void setValueNull() {
selectedValue = null;
}
I hope it helps. A lot of work, but the behaviour is exactly what I wanted. The reason for the javascript function is that it just send information to the servlet if the value is equals to "", otherwise it does nothing.
From a completely different angle...
Why do you have summaryReportController.device.nickname as a value in autoComplete?
I'd suggest you to use device as a value and specify
var
itemLabel
itemValue
converter
in your autocomplete, while your completeMethod will return list of devices filtered by nickname. Converter is implementation of javax.faces.convert.Converter.
See the POJO case in PF Showcase.
For a while now, I've been working on a JAVA-based web project, and have found this website to be very helpful on numerous occasions.
So, 'got some problems of my own now - and would appreciate if you help out!
Ok, so Here's the thing -
I'm trying to render a list of messages, each of which consists of a message title and a message body. The trick I'm working on is that the message body, for as long as the title (which is an a4j:commandLink) hasn't been clicked-on, should be hidden. And once the title's clicked - the body should be displayed. And once its clicked again - hide. And so forth.
Here's what I did at the JSF side (some parts have been omitted for simplicity):
<a4j:repeat var="msg" value="#{ForumRenderJSF.messages}">
<a4j:region id="msgAjaxRegion" renderRegionOnly="true">
<a4j:outputPanel id="msgPanel" ajaxRendered="true">
<rich:panel style="width: 100%; border: 0">
<a4j:form id="msgForm">
<!--
The message's title.
Each click results in either the revealing or hiding of the message's body.
-->
<a4j:commandLink value="#{msg.title}" action="#{ForumRenderAssistJSF.reevaluateRender}"/>
<h:outputText value=" By: <i>#{msg.userNick}</i>, #{msg.timestamp}" escape="false"/>
<!--
The message's body.
-->
<!-- A (textual) flag, indicating whether the body should be rendered. -->
<h:inputText id="renderBodyFlag"/>
<br/>
<!-- The body. -->
<a4j:outputPanel rendered="#{rich:findComponent('renderBodyFlag').value == true}">
<h:outputText value="#{msg.body}"/>
</a4j:outputPanel>
</a4j:form>
</rich:panel>
</a4j:outputPanel> <!-- msgPanel -->
</a4j:region>
</a4j:repeat>
Note the usage of:
1. A dummy "renderFlag" field (should be hidden, eventually), which value denotes whether the body should be rendered.
2. A backing bean for rendering assistance (ForumRenderAssistJSF); It goal is to flip the proper renderFlag's value from "true" to "false", and vice-versa.
3. An a4j:region to isolate each message when firing the request for ForumRenderAssistJSF.reevaluateRender() -- so that the bean can find the right "renderFlag" field.
As for the bean:
public class ForumRenderAssistJSF {
public void reevaluateRender()
{
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
UIComponent renderFlagComp = (new UIComponentLookup()).lookup(root, compLookup); // My recursive search
String renderFlagVal = (String) ((HtmlInputText)renderFlagComp).getValue();
if (!renderFlagVal.equals("true"))
{
((HtmlInputText)renderFlagComp).setValue("true");
}
else
{
((HtmlInputText)renderFlagComp).setValue("false");
}
}
}
AND THE PROBLEM IS:
The trick actually works -- but only for the first message in the list!
For the rest of them, I see that the server can reach the right renderFlag input-text component (tested by inserting values at the client), but for some reason - the client always renders it blank (no content) upon AJAX reply!
I tried digging deep inside richfaces tutorials and manuals, and to me it seems like everything is as it should be. So, I'm kind'a lost here, and as I said - would deeply appreciate your help in regards!
Thanks!
Have you tried using a rich:simpleTogglePanel? It seems to provide all the functionality you need.