GWT: How to programmatically reload CellBrowser?
Asked Answered
W

4

9

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);
Warp answered 15/1, 2011 at 20:58 Comment(0)
I
10

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
Iminourea answered 21/1, 2011 at 8:0 Comment(4)
typo on "HashRefresh" but otherwise +1 ! :)Pilot
Why is everybody voting up this answer? The one below is much better - a single line change that does all of this.Dimaggio
updateRowData(start, response.getEntities()) update all displays for provider, you can use updateRowData(display, start, response.getEntities()) instead. But display.setVisibleRangeAndClearData(display.getVisibleRange(), true) is still better :)Incredible
See the answer from Hugo. It actually answeres the OP's question regarding CellBrowser, not CellTable.Hemiterpene
T
7

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);
Trilateral answered 28/6, 2012 at 17:8 Comment(0)
L
2

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(...);
Luxurious answered 8/5, 2013 at 10:55 Comment(1)
Thank you providing this answer. It works perfectly. I had been wondering how to refresh a CellBrowser for quite a while.Hemiterpene
K
0

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!

Kathlenekathlin answered 20/5, 2015 at 16:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.