繁体   English   中英

GWT:向CellTable添加过滤

[英]GWT: add filtering to CellTable

我的任务是对GWT CellTable中显示的数据进行排序和过滤。 值得庆幸的是,GWT已经支持排序,但是看起来我将不得不结合自己的过滤支持。

更准确地说,我要支持的内容类似于Excel提供的过滤,在该过滤中,您可以单击列标题中的下拉菜单,然后单击(例如)复选框以过滤行。基于已过滤列的值。 一张图片胜过千言万语:

Excel列过滤

我的问题 :关于如何在GWT 2.2中实现此建议? 可能吗?

我正在考虑的一种选择是将自定义Header对象传递给CellTable.addColumn()。 如果可能,我将ClickHandler添加到页眉中,然后打开一个弹出窗口,其中显示用于过滤的小部件。 不知道如何在不负面影响排序行为的情况下实现这一点。

任何建议都欢迎。

编辑

感谢下面的John,我得到了以下FilterableHeader类,该类使我至少可以在标头中放置一个图标。 由于图像是通过HTML而不是使用GWT小部件插入的,因此尚不确定如何在该图标上获取ClickHandler。

public class FilterableHeader extends Header<String>
{
    /**
     * Image resources.
     */
    public static interface Resources extends ClientBundle
    {
        ImageResource downArrow();
        ImageResource upArrow();
    }

    private static final Resources RESOURCES = GWT.create(Resources.class);
    private static final int IMAGE_WIDTH = 16;
    private static final String DOWN_ARROW = makeImage(RESOURCES.downArrow());
    private static final String UP_ARROW = makeImage(RESOURCES.upArrow());

    private static String makeImage(ImageResource resource)
    {
        AbstractImagePrototype proto = AbstractImagePrototype.create(resource);
        return proto.getHTML().replace("style='", "style='position:absolute;right:0px;top:0px;");
    }

    private String text;

    public FilterableHeader(String text)
    {
        super(new ClickableTextCell());
        this.text = text;
    }

    @Override
    public String getValue()
    {
        return text;
    }

    @Override
    public void render(Cell.Context context, SafeHtmlBuilder safe)
    {
        int imageWidth = IMAGE_WIDTH;

        StringBuilder sb = new StringBuilder();
        sb.append("<div style='position:relative;cursor:hand;cursor:pointer;");
        sb.append("padding-right:");
        sb.append(imageWidth);
        sb.append("px;'>");
        sb.append(UP_ARROW);
        sb.append("<div>");
        sb.append(text);
        sb.append("</div></div>");

        safe.append(SafeHtmlUtils.fromSafeConstant(sb.toString()));
    }
}

我开发了业务应用程序,其中典型的数据库查询可能返回数百或数千行。 用户发现类似excel的过滤器和列排序非常有帮助。

因此,我实现了一个扩展ListDataProvider的类,以便与支持客户端类似于excel的列过滤和排序的CellTable一起使用。 在所有其他方面,它的行为都非常类似于ListDataProvider。

它依赖于实现以下ColumnAccessor接口,以便为CellTable中的每个列提供符号名称,提供对列级数据的访问以进行排序和过滤,为列进行比较的Comparator以及为标题显示标签。 以下是ColumnAccessor类。 假定您具有某种对行进行建模的数据传输对象<T>。

/**
 * Interface to provide access to a specific
 * column within a data row.
 * @param <T> Object that contains the column
 *  values in a cell table row. Typically a Data Transfer Object.
 */
public interface ColumnAccessor<T> {
    /**
     * Filter display value for blank/null column values
     */
    public final String FILTER_SELECTOR_BLANK = "{Blank}";
    /**

     * Returns A row-unique symbolic name for the column. This name is
     * used as a Map key to access the ColumnAccessor instance by
     * name for filtering and sorting.
     * @return
     */
    public String getColumnName();

    /**
     * Returns text label to appear as column header in CellTable.
     * @return
     */
    public String getLabel();

    /**
     * Returns value of the column as a String
     * @param t Object that models the column values in a
     *          cell table row (Typically a Data Transfer Object)
     * @return
     */
    public String getValue(T t);

    /**
     * Returns Comparator for sorting data rows and for sorting
     * discrete values that appear in a filter's select/option list.
     * While the getValue() method always returns a String,
     * these comparators should sort the column's values in
     * consideration for the data type (for example, dates sorted
     * as dates, numbers sorted as numbers, strings sorted as strings).
     * @return
     */
    public Comparator comparator();

}

以下是FilterSortDataProvider类:

import com.google.gwt.cell.client.SelectionCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.view.client.ListDataProvider;

import java.util.*;

/**
 * Class that extends a ListDataProvider but adds "Excel-Like" column filters and also
 * includes click on column heading sorts.
 * @param <T> Object that contains the column values in a cell table row. Typically a Data Transfer Object.
 */
public class FilterSortDataProvider<T> extends ListDataProvider {
    private List<T> rows;
    private List<T> filteredSortedRows;
    public Map<String, DataColumn> dataColumnMap = new HashMap<String, DataColumn>();
    private String lastSortColumn = "*";
    private int lastSortDirection = 0;

    /**
     * Constructs the DataProvider and columns
     * @param rows Collection of objects that contain column data for cell table rows, typically
     *             Data Transfer Objects.
     * @param columnAccessors List of ColumnAccessor instances for each column that will appear in
     *                        the cell table. Each accessor will render a sortable, filterable column header
     *                        and provides access to column-level data.
     */
    public FilterSortDataProvider(Collection<T> rows, List<ColumnAccessor> columnAccessors) {
        this.rows = new ArrayList<T>(rows);
        this.filteredSortedRows = new ArrayList<T>();
        Iterator<ColumnAccessor> columnAccessorIterator = columnAccessors.iterator();
        while (columnAccessorIterator.hasNext()) new DataColumn(columnAccessorIterator.next());
        // Initialize filters
        filter();
    }

    /**
     * Returns defensive copy of the current collection of filtered/sorted data rows
     * @return
     */
    public List<T> getFilteredSortedRows() {
        return new ArrayList(filteredSortedRows);
    }

    /**
     * Returns a CellTable Header for the named column for use when setting up the CellTable (ie:
     * used as Header value in cellTable.addColumn(TextColumn, Header) call. The header includes
     * the columnAccessor.getLabel() value as a click-to-sort header label, and a drop-down filter
     * where the options include all available values.
     * @param columnName Same value as returned by this columns ColumnAccessor.getColumnName()
     * @return
     */
    public Header getColumnHeader(final String columnName) {
        DataColumn column = dataColumnMap.get(columnName);
        return (column != null ? new FilteredCellTableHeader(column) : null);
    }

    /**
     * Called when user clicks on column header label. Repeated clicks on the same column header will
     * reverse the sort direction. Can also be called prior to display of CellTable to establish an initial
     * sort order.
     * @param sortColumnName
     */
    public void sort(String sortColumnName) {
        if (!sortColumnName.equals("*")) {
            DataColumn column = dataColumnMap.get(sortColumnName);
            if (column != null) {
                // Sort ascending
                Collections.sort(this.filteredSortedRows, column);
                // Re-Sort of same column
                if (sortColumnName.equals(lastSortColumn)) {
                    lastSortDirection *= -1;
                }
                else {
                    lastSortDirection = 1;
                    lastSortColumn = sortColumnName;
                }
                if (lastSortDirection == -1) Collections.reverse(filteredSortedRows);
            }
        }
        this.setList(filteredSortedRows);
    }

    /**
     * Optional call to pre-set filter before initial display of CellTable
     * @param columnName
     * @param value
     */
    public void filter(String columnName, String value) {
        DataColumn column = dataColumnMap.get(columnName);
        if (column != null) column.filter(value);
    }

    /**
     * Filters the rows based on all of the filters, and re-builds the filter drop-down
     * options.
     */
    private void filter() {
        // Build collection of rows that pass all filters
        filteredSortedRows = new ArrayList<T>();
        Iterator<T> rowIterator = this.rows.iterator();
        while (rowIterator.hasNext()) {
            T row = rowIterator.next();
            if (rowPassesFilter(row, null)) filteredSortedRows.add(row);
        }

        // Build filter select/option list for each column based on rows
        // that pass all filters EXCEPT for the column in question.
        Iterator<DataColumn> columnIterator = dataColumnMap.values().iterator();
        while (columnIterator.hasNext()) {
            DataColumn column = columnIterator.next();
            Set<String> optionsSet = new HashSet<String>();
            rowIterator = this.rows.iterator();
            while (rowIterator.hasNext()) {
                T row = rowIterator.next();
                if (rowPassesFilter(row, column)) {
                    optionsSet.add(column.filterOptionValue(row));
                }
            }
            // Sort the options using the ColumnAccessor's comparator
            List<String> optionsList = new ArrayList<String>(optionsSet);
            Collections.sort(optionsList, column.comparator());
            // Make blank option (if any) the last entry in the option list
            if (optionsList.contains(ColumnAccessor.FILTER_SELECTOR_BLANK)) {
                optionsList.remove(ColumnAccessor.FILTER_SELECTOR_BLANK);
                optionsList.add(ColumnAccessor.FILTER_SELECTOR_BLANK);
            }
            // Add the wild-card "All" as the first entry in the option list
            optionsList.add(0, "*");
            // Set the new list of options in the column
            column.filterOptions = optionsList;
        }
        // Re-sort the data with consideration for the current sort column and direction
        lastSortDirection *= -1;
        sort(lastSortColumn);
    }

    /**
     * Returns true if the specified row passes all column filters.
     * @param row Data row to test
     * @param columnToIgnore When specified, this column is assumed to allow the row
     *                       to pass the filter. This is used when building the list
     *                       of filter select/option values.
     * @return
     */
    private boolean rowPassesFilter(T row, DataColumn columnToIgnore) {
        Iterator<DataColumn> columnIterator = dataColumnMap.values().iterator();
        boolean passes = true;
        while (columnIterator.hasNext() && passes) {
            DataColumn column = columnIterator.next();
            if (column != columnToIgnore) {
                passes = column.rowPassesFilter(row);
            }
        }
        return passes;
    }

    /**
     * Inner class that models a CellTable column, its ColumnAccessor, current filter value,
     * and current filter option values.
     */
    public class DataColumn implements Comparator<T> {
        private String filterValue = "*";
        private List<String> filterOptions = new ArrayList<String>();
        private ColumnAccessor columnAccessor;

        /**
         * Constructs a filterable, sortable column
         * @param columnAccessor
         */
        public DataColumn(final ColumnAccessor columnAccessor) {
            this.columnAccessor = columnAccessor;
            FilterSortDataProvider.this.dataColumnMap.put(columnAccessor.getColumnName(), this);
        }

        /**
         * Returns symbolic name of column
         * @return
         */
        public String getName() {
            return this.columnAccessor.getColumnName();
        }

        /**
         * Returns display label for column header
         * @return
         */
        public String getLabel() {
            return columnAccessor.getLabel();
        }

        /**
         * Returns value of column
         * @param row
         * @return
         */
        public String getValue(T row) {
            return columnAccessor.getValue(row);
        }

        /**
         * Returns comparator define in ColumnAccessor for use when sorting
         * data rows and for sorting filter options.
         * @return
         */
        public Comparator comparator() {
            return columnAccessor.comparator();
        }

        /**
         * Called when user changes the value of a column filter
         * @param filterValue
         */
        public void filter(String filterValue) {
            if (this.filterOptions.contains(filterValue)) {
                this.filterValue = filterValue;
                FilterSortDataProvider.this.filter();
            }
        }

        /**
         * Called when user clicks on column label to sort rows
         */
        public void sort() {
            FilterSortDataProvider.this.sort(this.columnAccessor.getColumnName());
        }

        /**
         * Used to sort data rows. Uses comparator specified in ColumnAccessor.
         * @param row1
         * @param row2
         * @return
         */
        public int compare(T row1, T row2) {
            return comparator().compare(getValue(row1), getValue(row2));
        }

        /**
         * Returns true if specified row passes this column's filter
         * @param row
         * @return
         */
        public boolean rowPassesFilter(T row) {
            return filterValue.equals("*") || filterValue.equals(filterOptionValue(row));
        }

        /**
         * Returns value to appear in filter options list. Null or "blank" values appear in options
         * list as {Blank}.
         * @param row
         * @return
         */
        private String filterOptionValue(T row) {
            String value = getValue(row);
            return (value == null || value.trim().length() == 0 ? ColumnAccessor.FILTER_SELECTOR_BLANK : value);
        }

        /**
         * Renders Html Select/Options tag for column filter
         * @return
         */
        public String toHtmlSelect() {
            StringBuffer sb = new StringBuffer();
            sb.append("<select size='1' style='width: 100%;'>");
            Iterator<String> opts = filterOptions.iterator();
            while (opts.hasNext()) {
                String escapedOption = SafeHtmlUtils.htmlEscape(opts.next());
                sb.append("\t<option value='" + escapedOption);
                sb.append((escapedOption.equals(filterValue) ? "' SELECTED>" : "'>"));
                sb.append(escapedOption + "</option>\n");
            }
            sb.append("</select>\n");
            return sb.toString();
        }
    }

    /**
     * Inner class Header wrapper for FilteredSortedCellTableHeaderCell
     */
    public class FilteredCellTableHeader extends Header {
        public FilteredCellTableHeader(DataColumn column) {
            super(new FilteredSortedCellTableHeaderCell(column));
        }
        public Object getValue() {
            return null;
        }
    }

    /**
     * CellTable SelectionCell that includes filter and sort controls, renders controls, and
     * handles onBrowserEvent()
     */
    private class FilteredSortedCellTableHeaderCell extends SelectionCell {
        private DataColumn column;
        public FilteredSortedCellTableHeaderCell(final DataColumn column) {
            super(new ArrayList<String>());
            this.column = column;
        }

        /**
         * Renders Html Submit button as sort control, and Html Select/Option tag for filter.
         * @param context
         * @param value
         * @param sb
         */
        @Override
        public void render(Context context, String value, SafeHtmlBuilder sb) {
            String sortButton = "<input type='submit' value='" + SafeHtmlUtils.htmlEscape(column.getLabel()) +
                    "' style='text-align: center; width: 100%; background: none; border: none; font-weight: bold;'>";
            sb.appendHtmlConstant(sortButton);
            sb.appendHtmlConstant("<br>");
            sb.appendHtmlConstant(column.toHtmlSelect());
        }

        /**
         * Detects filter and sort user interaction events
         * @param context
         * @param parent
         * @param value
         * @param event
         * @param valueUpdater
         */
        @Override
        public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
            super.onBrowserEvent(context, parent, value, event, valueUpdater);
            String type = event.getType();
            Element element = event.getEventTarget().cast();
            String tagName = element.getTagName();
            // Filter selection changed
            if ("change".equals(type) && tagName.equals("SELECT")) {
                // Set filter value and call filter routine
                SelectElement se = (SelectElement)element;
                String filterValue = se.getOptions().getItem(se.getSelectedIndex()).getValue();
                column.filter(filterValue);
            }
            // Click on sort button
            else if (type.equals("focus") && tagName.equals("INPUT")) {
                column.sort();
            }
        }
    }
}

我希望这可能对某人有所帮助。

自定义标头是用于GWT 2.1进行排序的内容。 2.1自行车棚的示例使用自定义标头,并且正在使用一个标头进行排序,直到Mvp4g移至2.2。 要启用过滤,只需添加一个带有其自己的单击处理程序的图像,您就应该会很好-单击它不会触发排序行为,只有标题的其余部分会触发。

table.addColumn(new MyColumn(new MyCell()), new MyFilterHeader());

对于实际的过滤,如果您使用示例中的数据库模型(ListDataProvider的包装器类),那么我认为您将只保留两个列表-分配给ListDataProvider的过滤列表和未过滤的列表列出其依据。

希望有帮助!


在新的示例代码中,您可能希望尝试使用其中具有ClickableTextCell的CompositeCell以及用于过滤部分的ActionCell-如果您可以将图像粘贴到ClickableTextCell中,则应该可以在ActionCell中加上它,将具有您想要的mouseup行为。

我使用鼠标单击位置将自定义单击事件添加到列标题。 换句话说,您可以对其进行设置,以便如果用户单击应该在其中放置图像的“一般区域”,则可以显示一个过滤屏幕。

这是一个示例,其中我忽略了添加的文本字段的点击事件:

        if(col.isFilterable()){
            if (event.getClientY() > (getInputElement(parent).getAbsoluteTop() - 2) && event.getClientY() < (getInputElement(parent).getAbsoluteBottom() + 2)) {
                //ignore on click in area of the text field
                event.preventDefault();
            } else {

//如果用户单击其他任何地方,则进行排序trySort(parent); }

由于因为单元格分别侦听“ keyup”事件,所以当用户按下Enter键(单元格处于焦点状态)时,将执行过滤器。

    if(event.getKeyCode()==13){
        event.preventDefault();
        handleSetFilterValue(parent);
        tryFilter();
    } 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM