Filter
Sam Chuang, Engineer, Potix Corporation
October 30, 2013
ZK 6.5 and later
Introduction
ZK give developer the power of creating advanced component, extending the component set for your application. In this article, I will create a re-usable component: Filterpopup. It's designed for working with collection component that needs filter functionality. For instance, the Filterpopup component can be used with Grid, user can use it to filter out huge amount of data, search the required information quickly and intuitively. Besides, it's also designed for highly customizable, able to integrate with different type of constrain, seamlessly works with different type of data type.
- Live Demo
http://screencast.com/t/Uqi5bZNS
How it Works
The Filterpopup component is composed from three regions, with a default implementation of View Model.
Regions
Top
The top region is responsible for configuration settings. The default implementation includes:
- Textbox: used for input constraint to perform filtering command.
- Checkbox: to perform select all command.
Default implementation:
<div id="top" vflex="min">
<vlayout>
<textbox value="@bind(vm.constraint)" instant="true"
onChanging="@command('performFiltering', constraint=event.value)"></textbox>
<checkbox checked="@load(vm.selectAll)" label="(Select All)"
onCheck="@command('selectAll', checked=event.checked)"></checkbox>
</vlayout>
</div>
- Top region:
- With constrant:
Center
The center region is responsible for showing the filter result by constraint, by using Listbox component.
- Default implementation:
<div id="center" vflex="true">
<custom-attributes org.zkoss.zul.listbox.rod="true"/>
<listbox model="@bind(vm.filteredByConstraintModel)"
checkmark="true" id="listbox" vflex="true">
<template name="model">
<listitem>
<listcell label="@load(each)" />
</listitem>
</template>
</listbox>
</div>
Selection
The Listbox in center region is responsible for marking user's selection. The selection will become filtered result of Filterpopup (when user confirm the selection by click the ok button). Note that if user doesn't confirm the selection or user click the cancel button, the selection will be discard.
Bottom
The bottom region is responsible for commands, The default implementation includes:
- OK button: perform ok command to confirm user's selection, and fire a FilterEvent with selection.
- Cancel button: perform cancel command to discard any changes.
<div id="bottom" vflex="min">
<hbox pack="end" width="100%" spacing="5px">
<button onClick="@command('ok')" label="OK" mold="trendy" width="75px"></button>
<button onClick="@command('cancel')" label="Cancel" mold="trendy" width="75px"></button>
</hbox>
</div>
Usage
Event
- FilterEvent: triggered when user click OK button (perform ok command) and when the selection is not empty. Use this event to get filtered result.
MVC Usage:
@Listen("onFilter=#filterpopup")
public void onFilter(FilterEvent evt) {
grid.setModel(new ListModelList(evt.getFiltered()));
}
MVVM Usage:
View Model
Bindings
- constraint: the value of constraint is used for performing filtering task, the result will be showing in the listbox of center area
- API signature: public void setConstraint(Object constraint)
- Usage:
Bind constraint with String
<textbox value="@bind(vm.constraint)" instant="true"
onChanging="@command('performFiltering', constraint=event.value)"></textbox>
Bind constraint with Date
<datebox value="@bind(vm.constraint)"
onChange="@command('performFiltering', constraint=self.value)"></datebox>
Commands
setFilter
Sets the filter of Filterpopup and perform filtering task if constrant is not empty
- API signature: @Command public void setFilter(@BindingParam("filter") Filter filter)
- Usage:
<radio label="Begins with..."
onCheck="@command('setFilter', filter=StartWithStringFilter)">
</radio>
- Default implementation:
@Command
public void setFilter(@BindingParam("filter") Filter filter) {
_filterExt.setFilter(filter);
if (_constraint != null) {
HashMap arg = new HashMap();
arg.put("constraint", _constraint);
_binder.postCommand("performFiltering", arg);
}
}
TODO: setComparator
selectAll
- API signature:
- Usage:
- Default implementation:
performFiltering
- API signature: @Command public void performFiltering(@BindingParam("constraint") Object constraint)
- Usage:
Perform filtering with onChanging event, with String constraint from Textbox
<textbox value="@bind(vm.constraint)" instant="true"
onChanging="@command('performFiltering', constraint=event.value)"></textbox>
Perform filtering with onChange event, with Date constraint from Datebox
<datebox value="@bind(vm.constraint)"
onChange="@command('performFiltering', constraint=self.value)"></datebox>
ok
Confirm user selection. If selection is not empty, set filtered property of Filterpopup then trigger FilterEvent
- API signature: @Command public void ok()
- Default implementation:
@Command
public void ok() {
Set<?> selection = _filteredByConstraintListModel.getSelection();
if (_filteredByConstraint != null && selection != null && !selection.isEmpty()) {
_filterExt.setFiltered(selection, false);
FilterEvent event = new FilterEvent(_filterExt, selection);
Events.postEvent(event);
}
_filterExt.stopFiltering();
}
cancel
Discard constraint and user selection changes
- API signature: @Command public void cancel()
Customization
Settings
Use template with name="top" to customize the top region.
<filterpopup id="filterpopup" width="300px" height="400px">
<template name="top">
<vlayout>
<textbox value="@bind(vm.constraint)" instant="true"
onChanging="@command('performFiltering', constraint=event.value)"></textbox>
<checkbox checked="@load(vm.selectAll)" label="(Select All)"
onCheck="@command('selectAll', checked=event.checked)"></checkbox>
</vlayout>
</template>
...
</filterpopup>
Filter
Use setFilter command to set filter
<filterpopup id="filterpopup" width="300px" height="400px">
<template name="top">
<vlayout>
<radiogroup>
<radio label="Contains..."
onCheck="@command('setFilter', filter=ContainsStringFilter)"
checked="true">
</radio>
<radio label="Begins with..."
onCheck="@command('setFilter', filter=StartWithStringFilter)">
</radio>
</radiogroup>
...
</vlayout>
</template>
...
</filterpopup>
Comparator
Head
Use template with name="head" to customize the listhead of Listbox in center region.
<filterpopup id="filterpopup" width="300px" height="400px">
<template name="head">
<listhead>
<listheader label="Name"/>
<listheader label="Birth"/>
</listhead>
</template>
...
</filterpopup>
Model
Use template with name="model" to customize the Listbox in center region.
<filterpopup id="filterpopup" width="300px" height="400px">
<template name="model">
<listitem>
<listcell label="@load(each.name)" />
<listcell label="@load(each.birth)" />
</listitem>
</template>
...
</filterpopup>
Summary
With the power of ZK technology (MVVM, template etc...), it allows me to created highly customizable component: Filterpopup, that can be used with various scenario to perform filter task.
Resource
You can get the complete source of the example used in this article from its github or download the Filterpopup binary file here.