GWT: How to programmatically reload CellBrowser?

杀马特。学长 韩版系。学妹 提交于 2019-12-03 17:42:07

问题


I'm using GWT 2.1's CellBrowser with a custom TreeViewModel. The TreeViewModel in turn uses an AsyncDataProvider to fetch data dynamically. This all works beautifully- when the user clicks on a node my AsyncDataProvider fetches the results via RPC, and the CellBrowser dutifully displays them.

I feel silly for not being able to figure this out, but how can I programmatically tell the CellBrowser to reload (and display) the data? I'm guessing that I need to somehow get a handle to the AsyncDataProvider for my root node and then call updateRowData() & updateRowCount() on it, but I don't see an obvious way to query the browser (or its model) for the root DataProvider.

I guess I could add code to my AsyncDataProvider constructor that looks for a null argument, and by that means recognize "hey, I'm the root" and store a reference somewhere, but that seems hackish. Surely there's a better way to do this.

Apologies for dumping so much code here, but I don't know how to boil this down to anything simpler and still provide enough context.

My AsyncDataProvider:

private static class CategoryDataProvider extends AsyncDataProvider<Category>
{           
    private Category selectedCategory;

    private CategoryDataProvider(Category selectedCategory)
    {
        this.selectedCategory = selectedCategory;
    }

    @Override
    protected void onRangeChanged(HasData<Category> display)
    {
        new AsyncCall<List<Category>>()
        {
            @Override
            protected void callService(AsyncCallback<List<Category>> cb)
            {
                // default to root
                String categoryId = "-1";
                if (selectedCategory != null)
                {
                    categoryId = selectedCategory.getCategoryId();
                }

                // when a category is clicked, fetch its child categories
                service.getCategoriesForParent(categoryId, cb);
            }

            @Override
            public void onSuccess(List<Category> result)
            {
                // update the display
                updateRowCount(result.size(), true);
                updateRowData(0, result);
            }
        }.go();

    }
}

My model:

private static class CategoryTreeModel implements TreeViewModel
{
    private SingleSelectionModel<Category> selectionModel;

    public CategoryTreeModel(SingleSelectionModel<Category> selectionModel)
    {
        this.selectionModel = selectionModel;
    }

    /**
     * @return the NodeInfo that provides the children of the specified category
     */
    public <T> NodeInfo<?> getNodeInfo(T value)
    {
        CategoryDataProvider dataProvider = new CategoryDataProvider((Category) value);

        // Return a node info that pairs the data with a cell.
        return new TreeViewModel.DefaultNodeInfo<Category>(dataProvider, new CategoryCell(), selectionModel, null);
    }

    /**
     * @return true if the specified category represents a leaf node
     */
    public boolean isLeaf(Object value)
    {
        return value != null && ((Category) value).isLeafCategory();
    }
}

And finally, here's how I'm using them:

        CategoryTreeModel model = new CategoryTreeModel(selectionModel);
        CellBrowser cellBrowser = new CellBrowser(model, null);

回答1:


It looks like a lot of people are having this same issue. Here's how I handled refreshing the data from an AsyncDataProvider.

First, create an interface that adds the ability to refresh data provider by wrapping the AsyncDataProvider's protected onRangeChanged method. For example...

HasRefresh.java

public interface HasRefresh<HasData<T>>{
  /**
   * Called when a display wants to refresh 
   *
   * @param display the display to refresh
   */
   void refresh(HasData<T> display);

}

Then the CellTable needing to be refreshed can then call into it through an Activity or however your controller logic is setup.

/**
* A custom {@link AsyncDataProvider}.
*/
public class MyAsyncDataProvider extends
            AsyncDataProvider<Entity> implements HasRefresh<Entity> {

            public void refresh(Entity display){

                  onRangeChanged(display);
             }

             /**
         * {@link #onRangeChanged(HasData)} is called when the table requests a
         * new range of data. You can push data back to the displays using
         * {@link #updateRowData(int, List)}.
         */
        @Override
        protected void onRangeChanged(
                HasData<Entity> display) {
            currentRange = display.getVisibleRange();
            final int start = currentRange.getStart();

            dataServiceQuery = context.getEntities();

            dataServiceQuery
                    .execute(new DataServiceQueryHandler<Entity>() {

                        @Override
                        public void onQueryResponse(
                                DataServiceQueryResponse<Entity> response) {


                            updateRowCount(response.getCount(), true);
                            updateRowData(start, response.getEntities());

                        }
                    });

        }// end onRangeChanged()

    }// end MyAsyncDataProvider class



回答2:


Found out now, the following code is basically the same but from the HasData interface instead of using AsyncDataProvider.

HasData<T> display;
...
display.setVisibleRangeAndClearData(display.getVisibleRange(), true);



回答3:


I don't understand why affablebloke's answer is marked as "correct" and upvoted so much!?

Caffeine Coma asked about updating a Cell Browser and not a Cell Table, that are totally different things!

For me, the following trick worked: I define the DataProviders outside of the TreeViewModel and pass them as an argument to the constructor. In my case they provide empty data structures at the beginning.

Now you have a reference to the DataProviders from "outside" in order to update the Cell Browser.

Example:

private class MyTreeViewModel implements TreeViewModel {

    AbstractDataProvider<MyDataStructure> myDataProvider;

    public MyTreeViewModel(AbstractDataProvider<MyDataStructure> myDataProvider)
    {
        this.myDataProvider = myDataProvider;
    }

    @Override
    public <T> NodeInfo<?> getNodeInfo(T value) {
        if(value == null)
        {
            // Level 0
            return new DefaultNodeInfo<MyDataStructure>(myDataProvider, new MyCellImpl());
        }
    }
}


AbstractDataProvider<MyDataStructure> myDataProvider = new ListDataProvider<MyDataStructure>(new ArrayList<MyDataStructure>()); // or AsyncDataProvider
MyTreeViewModel myModel = new MyTreeViewModel(myDataProvider);

CellBrowser<MyDataStructure> cellBrowser = new CellBrowser.Builder<MyDataStructure>(myModel, null).build();

/*
  here you can do some stuff, for example, bind the CellBrowser to a template with UiBinder
*/

/*
 if you use a ListDataProvider, do
*/

myDataProvider.setList(myDataStructureFromSomewhere);
myDataProvider.refresh();

/*
 if you use an AsyncDataProvider, do
*/

myDataProvider.updateRowData(..., myDataStructureFromSomewhere);
myDataProvider.updateRowCount(...);



回答4:


I had the same problem and this worked for me:

 List<TreeGridRecord> dataToTable = ....

int firstPosition = this.pager.getDisplay().getVisibleRange().getStart();
this.dataProvider.updateRowData(firstPosition, dataToTable);

int rowCount = this.pager.getPageStart() + dataToTable.size();
this.dataProvider.updateRowCount(rowCount, exactCount);

Where the "pager" is a SimplePager to control the table pagination.

Hope this helps!



来源:https://stackoverflow.com/questions/4702102/gwt-how-to-programmatically-reload-cellbrowser

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!