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);
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);
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
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 DataProvider
s 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 DataProvider
s 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(...);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With