簡體   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