Using domino-ui datatable search action with remote data store

76 Views Asked by At

what is the proper way to connect the SearchTableAction header with a server-side search? (Right now the example uses the LocalListDataStore and filters it client-side via a SearchFilter, Regular GWT would use CellTables and AsyncDataProvider)

The winning option right now (which does not seem ideal) would be to copy SearchTableAction UI setup in the constructor and create a new class that would call setData on the LocalDataStore after the server-side call finishes.

2

There are 2 best solutions below

0
On BEST ANSWER

You dont actually need to copy the UI, the search action as well the header filters will fire a table event when they are changed, any data store can listen to those events and read the search filters from the event and use it to load/filter the data, the LocalDatStore does this and filter the data locally, a remote data store will read these filters and sends them to the server and the server will filter the data and return the result to the client.

Here is a sample remote data store

package org.dominokit.domino.ui.sample.store;

import org.dominokit.domino.ui.sample.client.Formats;
import org.dominokit.domino.ui.sample.shared.store.CanLoadItems;
import org.dominokit.domino.ui.sample.shared.store.LoadContext;
import org.dominokit.domino.ui.datatable.events.SearchEvent;
import org.dominokit.domino.ui.datatable.events.SortEvent;
import org.dominokit.domino.ui.datatable.events.TableEvent;
import org.dominokit.domino.ui.datatable.events.TablePageChangeEvent;
import org.dominokit.domino.ui.datatable.model.Filter;
import org.dominokit.domino.ui.datatable.model.FilterTypes;
import org.dominokit.domino.ui.datatable.store.DataChangedEvent;
import org.dominokit.domino.ui.datatable.store.DataStore;
import org.dominokit.domino.ui.datatable.store.StoreDataChangeListener;
import org.dominokit.domino.ui.pagination.HasPagination;
import org.gwtproject.i18n.client.DateTimeFormat;

import java.util.*;

import static java.util.Objects.nonNull;
import static org.dominokit.domino.ui.datatable.events.SearchEvent.SEARCH_EVENT;
import static org.dominokit.domino.ui.datatable.events.SortEvent.SORT_EVENT;

public class RemoteDataStore<T> implements DataStore<T> {
    private List<StoreDataChangeListener<T>> listeners = new ArrayList<>();
    private HasPagination pagination;
    private CanLoadItems<T> uiHandlers;
    private String propertyName;
    private String sortDirection;
    private int activePage = 0;

    private Map<String, String> searchProperties = new HashMap<>();
    private List<T> items;

    public RemoteDataStore(CanLoadItems<T> uiHandlers) {
        this.uiHandlers = uiHandlers;
    }

    @Override
    public void onDataChanged(StoreDataChangeListener<T> dataChangeListener) {
        listeners.add(dataChangeListener);
    }

    @Override
    public void removeDataChangeListener(StoreDataChangeListener<T> dataChangeListener) {
        listeners.remove(dataChangeListener);
    }

    @Override
    public void load() {
        uiHandlers.load(getLoadContext(), loadResult -> {
            this.items = loadResult.getResourceList();
            if (nonNull(loadResult.getPage())) {
                pagination.updatePagesByTotalCount(loadResult.getPage().getTotalElements(), loadResult.getPage().getSize());
                pagination.gotoPage(loadResult.getPage().getNumber() + 1, true);
            }
            fireUpdate();
        });
    }

    private LoadContext getLoadContext() {
        return new LoadContext(propertyName, sortDirection, activePage, searchProperties);
    }

    @Override
    public void handleEvent(TableEvent event) {
        switch (event.getType()) {
            case TablePageChangeEvent.PAGINATION_EVENT:
                activePage = pagination.activePage() - 1;
                load();
                break;
            case SORT_EVENT:
                propertyName = ((SortEvent<?>) event).getColumnConfig().getName();
                sortDirection = ((SortEvent<?>) event).getSortDirection().toString();
                load();
                break;
            case SEARCH_EVENT:
                searchProperties = new HashMap<>();
                List<Filter> filters = ((SearchEvent) event).getFilters();
                this.activePage = 0;
                for (Filter filter : filters) {
                    if (!filter.getValues().isEmpty()) {
                        if (filter.getType().equals(FilterTypes.DATE)) {
                            String longDate = filter.getValues().get(0);
                            String formattedDate = DateTimeFormat.getFormat(Formats.DEFAULT_DATE_PATTERN)
                                    .format(new Date(Long.parseLong(longDate)));
                            searchProperties.put(filter.getFieldName(), formattedDate);
                        } else {
                            searchProperties.put(filter.getFieldName(), filter.getValues().get(0));
                        }
                    }
                }
                load();
                break;
        }
    }

    private void fireUpdate() {
        listeners.forEach(dataChangeListener -> dataChangeListener.onDataChanged(new DataChangedEvent<>(items, items.size())));
    }

    public HasPagination getPagination() {
        return pagination;
    }

    public void setPagination(HasPagination pagination) {
        this.pagination = pagination;
    }

    public int getActivePage() {
        return activePage;
    }

    public void load(int pageNumber) {
        this.activePage = pageNumber;
        load();
    }
}

Here we listen to the events and delegate the actual data load call to the uiHandlers.

Notice that implementing a remote data store depends on the server implementation, so there is no one remote data store that works for everyone, so use this as an example to implement one that works for your server.

0
On

Thank you. (I don't have enough points yet to add a comment on the first answer - so adding a new answer) This is great information! Below is the implementation with GWT Async for server communications - but it could easily be replaced in the overridden load method.

Add the table to your page and tell the RemoteDataService how to load information from the server.

    SimplePaginationPlugin<Person> simplePaginationPlugin = new SimplePaginationPlugin<>(10);

        TableConfig<Person> tableConfig = new TableConfig<>();
        tableConfig
                .addColumn(ColumnConfig.<Person>create("id", "#").textAlign("right").asHeader()
                        .setCellRenderer(cell -> TextNode.of(cell.getTableRow().getRecord().getId() + "")))
                .addColumn(ColumnConfig.<Person>create("status", "Status").textAlign("center").setCellRenderer(cell -> {
                    if (cell.getTableRow().getRecord().isActive()) {
                        return Style.of(Icons.ALL.check_circle()).setColor(Color.GREEN_DARKEN_3.getHex()).element();
                    } else {
                        return Style.of(Icons.ALL.highlight_off()).setColor(Color.RED_DARKEN_3.getHex()).element();
                    }
                }))
                .addColumn(ColumnConfig.<Person>create("firstName", "Name")
                        .setCellRenderer(cell -> TextNode.of(cell.getTableRow().getRecord().getName())));

        tableConfig
        .setFixed(true)
                .addPlugin(new SortPlugin<>())
                .addPlugin(new HeaderBarPlugin<Person>("Demo table", "this a sample table with all features")
                        .addActionElement(new HeaderBarPlugin.SearchTableAction<>()))
                .addPlugin(simplePaginationPlugin);
                ;
        
        RemoteDataStore<Person> remoteDataStore = new RemoteDataStore<Person>() {

            @Override
            public void load() {
                greetingService.getPeople(getSearchProperties(), getSortPropertyName(), getSortPropertyDirection(),
                        new AsyncCallback<ArrayList<Person>>() {

                            @Override
                            public void onSuccess(ArrayList<Person> result) {
                                loadNewItems(result);
                            }

                            @Override
                            public void onFailure(Throwable caught) {
                                Window.alert("Failure");
                            }
                        });
            }
        };

        remoteDataStore.setPagination(simplePaginationPlugin.getSimplePagination());

        DataTable<Person> table = new DataTable<>(tableConfig, remoteDataStore);
        layout.getContentPanel()
                .appendChild(Card
                        .create("BASIC TABLE", "By default a table will auto fit columns and allow custom cell content")
                        .setCollapsible().appendChild(table).element());

        //Load the table data on page load
        remoteDataStore.load();
import static java.util.Objects.nonNull;
import static org.dominokit.domino.ui.datatable.events.SearchEvent.SEARCH_EVENT;
import static org.dominokit.domino.ui.datatable.events.SortEvent.SORT_EVENT;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

//import org.dominokit.domino.ui.sample.client.Formats;
import org.dominokit.domino.ui.datatable.events.SearchEvent;
import org.dominokit.domino.ui.datatable.events.SortEvent;
import org.dominokit.domino.ui.datatable.events.TableEvent;
import org.dominokit.domino.ui.datatable.events.TablePageChangeEvent;
import org.dominokit.domino.ui.datatable.model.Filter;
import org.dominokit.domino.ui.datatable.model.FilterTypes;
import org.dominokit.domino.ui.datatable.store.DataChangedEvent;
import org.dominokit.domino.ui.datatable.store.DataStore;
import org.dominokit.domino.ui.datatable.store.StoreDataChangeListener;
import org.dominokit.domino.ui.pagination.HasPagination;
import org.gwtproject.i18n.client.DateTimeFormat;

public abstract class RemoteDataStore<T> implements DataStore<T> {
    private List<StoreDataChangeListener<T>> listeners = new ArrayList<>();
    private HasPagination pagination;
    private String sortPropertyName;
    private String sortPropertyDirection;
    
    private Entry<String,String> sortProperties;
    
    private int activePage = 0;

    private Map<String, String> searchProperties = new HashMap<>();
    private List<T> items;

    public RemoteDataStore() {
        super();
    }

    @Override
    public void onDataChanged(StoreDataChangeListener<T> dataChangeListener) {
        listeners.add(dataChangeListener);
    }

    @Override
    public void removeDataChangeListener(StoreDataChangeListener<T> dataChangeListener) {
        listeners.remove(dataChangeListener);
    }

    public void loadNewItems(List<T> newItems)
    {
        if(Objects.isNull(getItems()))
        {
            setItems(new ArrayList<T>());
        }
        
        getItems().clear();
        getItems().addAll(newItems);
        
        if(Objects.nonNull(pagination))
        {
            pagination.updatePagesByTotalCount(getItems().size());
            pagination.gotoPage(1, true);
        }
        
        fireUpdate();
    }
    
    @Override
    public void handleEvent(TableEvent event) {
        switch (event.getType()) {
            case TablePageChangeEvent.PAGINATION_EVENT:
                setActivePage(pagination.activePage() - 1);
                fireUpdate();
                break;
            case SORT_EVENT:
                setSortPropertyName(((SortEvent<?>) event).getColumnConfig().getName());
                setSortPropertyDirection(((SortEvent<?>) event).getSortDirection().toString());
                load();
                break;
            case SEARCH_EVENT:
                setSearchProperties(new HashMap<>());
                List<Filter> filters = ((SearchEvent) event).getFilters();
                this.setActivePage(0);
                for (Filter filter : filters) {
                    if (!filter.getValues().isEmpty()) {
                        if (filter.getType().equals(FilterTypes.DATE)) {
                            String longDate = filter.getValues().get(0);
                            String formattedDate = DateTimeFormat.getFormat("dd-MM-yyyy HH:mm:ss")
                                    .format(new Date(Long.parseLong(longDate)));
                            getSearchProperties().put(filter.getFieldName(), formattedDate);
                        } else {
                            getSearchProperties().put(filter.getFieldName(), filter.getValues().get(0));
                        }
                    }
                }
                load();
                break;
        }
    }

    private void fireUpdate() {
        List<T> updateRecords = getRecordsForPage();
        listeners.forEach(dataChangeListener -> dataChangeListener.onDataChanged(new DataChangedEvent<>(updateRecords, updateRecords.size())));
    }


    private List<T> getRecordsForPage() {
        if (nonNull(getPagination())) {
          int fromIndex = getPagination().getPageSize() * (getPagination().activePage() - 1);
          int toIndex = Math.min(fromIndex + getPagination().getPageSize(), getItems().size());
          return new ArrayList<>(getItems().subList(fromIndex, toIndex));
        } else {
            return new ArrayList<>(getItems());
        }
      }
    
    
    public HasPagination getPagination() {
        return pagination;
    }

    public void setPagination(HasPagination pagination) {
        this.pagination = pagination;
    }

    public int getActivePage() {
        return activePage;
    }

    public void load(int pageNumber) {
        this.setActivePage(pageNumber);
        load();
    }

    public Map<String, String> getSearchProperties() {
        return searchProperties;
    }

    public void setSearchProperties(Map<String, String> searchProperties) {
        this.searchProperties = searchProperties;
    }

    public void setActivePage(int activePage) {
        this.activePage = activePage;
    }

    public Entry<String,String> getSortProperties() {
        return sortProperties;
    }

    public void setSortProperties(Entry<String,String> sortProperties) {
        this.sortProperties = sortProperties;
    }

    public String getSortPropertyName() {
        return sortPropertyName;
    }

    public void setSortPropertyName(String sortPropertyName) {
        this.sortPropertyName = sortPropertyName;
    }

    public String getSortPropertyDirection() {
        return sortPropertyDirection;
    }

    public void setSortPropertyDirection(String sortPropertyDirection) {
        this.sortPropertyDirection = sortPropertyDirection;
    }

    public List<T> getItems() {
        return items;
    }

    public void setItems(List<T> items) {
        this.items = items;
    }
}
@SuppressWarnings("serial")
public class Person implements Serializable {

    private int id;
    private String name;
    private boolean active;

    public Person() {
    }

    public Person(int id, String name, boolean active) {
        this.id = id;
        this.name = name;
        this.active = active;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }
}