问题
PrimeFaces has very well done filter for p:dataTable. It is great from UX site, because the filter field is in column header, so there's no doubt what you are filtering, and it's working live - the data changes as you type (well, only if you make a short pause, but it's in my opinion exactly what user expects).
Now I'd like to place something custom in the header that would act as filter. So, my idea was to place a component in header facet:
<p:column ...>
<f:facet name="header">
<some:myComponent onkeydown="filterAction()"/>
</f:facet>
</p:column>
The problem is that filterAction
must not update the whole dataTable component (because the component the user is typing in would be re-created), but it must update the table body.
I've thought I can do it with PrimeFaces Selectors (based on jQuery selectors), but according to the topic How to update data rows only using a selector for dataTable? it is not possible. And the datatable.js contains specialized Ajax call to achieve that (line: 839 in PrimeFaces 3.5, release):
options.onsuccess = function(responseXML) {
var xmlDoc = $(responseXML.documentElement),
updates = xmlDoc.find("update");
....
$this.tbody.html(content);
My question is, is it possible to use such individual filter component, that would refresh only table body, exactly like the standard filter it does, without going deep into PF internals and writing specialized AJAX handler like that?
Of course, it's possible to make filter outside the dataTable, but than the UX would be reduced (the people who are now using my application like the current design very much).
回答1:
It is possible to put any component in column header using facet
. However, it is not possible to request only dataTable body refresh. So the solution is, to send normal request, but to take over the response and handle it in custom code part.
If you create remoteCommand like that:
<p:remoteCommand id="refreshDataTable" name="refreshDataTable"
actionListener="#{carTable.doFilter}"
update="dataTable" />
the ID of that command is sent to the Server. Knowing that, we can prepare the custom AJAX request:
var options = {
source: 'main:tabView:refreshDataTable',
update: carsTable.id,
formId: carsTable.cfg.formId
}
options.onsuccess = customHandler
PrimeFaces.ajax.AjaxRequest(options);
where customHandler code looks like that:
var xml = $(resp.documentElement)
updates = xml.find('update')
for(var i=0; i < updates.length; i++) {
var update = updates.eq(i),
id = update.attr('id'),
content = update.text();
if(id == carsTable.id){
var tbody = $(content).find('tbody')
carsTable.tbody.html(tbody.html());
}
else {
PrimeFaces.ajax.AjaxUtils.updateElement.call(this, id, content);
}
PrimeFaces.ajax.AjaxUtils.handleResponse.call(this, xml);
var paginator = carsTable.getPaginator();
if(paginator) {
paginator.setTotalRecords(this.args.totalRecords);
}
if(carsTables.cfg.scrollable) {
carsTable.alignScrollBody();
}
return true;
We need to set in the variable totalRecords in Java code:
RequestContext context = RequestContext.getCurrentInstance();
context.addCallbackParam("totalRecords", filteredCars.size());
回答2:
For Primefaces 5, there is a new attribute filterFunction that makes possible to define custom filter in Java code: http://blog.primefaces.org/?p=3084
However, filter input is still a string in input text.
If you need a custom component to input filter values, or you are stuck with Primefaces 4 (as I am on a recent project), I will describe what worked for me.
I extended filtering behavior using these key steps
- put a normal JSF input component into header facet of a column instead of using filterBy attribute
- attach a javascript callback to this component triggered on user input, which calls PF('dataTableWidgetVar').filter()
- add filteredValue attribute to the dataTable, which applies a custom filter in Java setter on top of existing filter
Key thing is to take advantage of filteredValue attribute - when Primefaces filter() function is called or when primefaces filters change, filteredValue is set to list filtered values (or null if no filter applied). Then Primefaces reads filteredValues back from getter to update list of items in dataTable. If we put our filter in between these calls (either in getter or setter, setter is more efficient as it is called only when filters change), we modify original filtered list with our filter and return it back through the getter.
Some code:
Definition of datatable with inputText as filter component:
<p:dataTable filteredValue="#{view.filteredResults} >
...
<p:columnGroup type="header">
...
<p:row>
...
<p:column>
<f:facet name="header">
<p:inputText value="#{view.filterValue}" />
</f:facet>
</p:column>
...
</p:dataTable>
Java Setter and Getter of filteredResults in view named view:
public void setFilteredResults(List<?> filteredResults) {
this.filteredResults = applyPremiumFilters(filteredResults, filterValue);
}
public List<?> getFilteredResults() {
return this.filteredResults;
}
The rest is Javascript code to apply filter on dataTable when value in filter component is changed.
来源:https://stackoverflow.com/questions/17858753/primefaces-custom-component-for-live-filtering-in-datatable