2011-01-15 18 views
9

Używam GWT 2.1's CellBrowser z niestandardowym TreeViewModel. TreeViewModel z kolei korzysta z AsyncDataProvider do dynamicznego pobierania danych. To wszystko działa pięknie - kiedy użytkownik kliknie na węzeł, mój AsyncDataProvider pobiera wyniki przez RPC, a CellBrowser sumiennie je wyświetla.GWT: Jak programowo załadować CellBrowser?

Czuję się głupio, bo nie jestem w stanie tego obliczyć, ale w jaki sposób mogę programowo nakazać serwerowi CellBrowser ponowne załadowanie (i wyświetlenie) danych? Zgaduję, że muszę jakoś uzyskać uchwyt do AsyncDataProvider dla mojego węzła głównego, a następnie wywołać na nim updateRowData() & updateRowCount(), ale nie widzę oczywistego sposobu na zapytanie do przeglądarki (lub jej modelu) dla głównego DataProvider.

Chyba mógłbym dodać kod do mojego konstruktora AsyncDataProvider, który szuka argumentu zerowego, a przez to rozpoznać "hej, jestem rootem" i przechowywać gdzieś referencję, ale to wydaje się hackowe. Na pewno jest lepszy sposób na zrobienie tego.

Przepraszam za umieszczenie tutaj tak dużo kodu, ale nie wiem, jak to sprowadzić do czegoś prostszego i nadal zapewniać wystarczającą ilość kontekstu.

Moja 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(); 

    } 
} 

Mój 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(); 
    } 
} 

I wreszcie, oto jak mam je za pomocą:

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

Odpowiedz

10

Wygląda na to, że wiele osób ma to same issue. Oto jak odświeżyłem dane z AsyncDataProvider.

Najpierw utwórz interfejs dodający możliwość odświeżenia dostawcy danych przez zawinięcie metody onRangeChanged chronionej przez AsyncDataProvider. Na przykład ...

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); 

} 

Następnie CellTable konieczności odświeżenia może wtedy zadzwonić do niego przez działanie lub jednak logika kontroler jest ustawiony.

/** 
* 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 
+0

literówka na "HashRefresh", ale poza tym +1! :) – HaveAGuess

+0

Dlaczego wszyscy głosują na tę odpowiedź? Ten poniżej jest znacznie lepszy - zmiana jednej linii, która to wszystko robi. –

+0

updateRowData (start, response.getEntities()) aktualizuje wszystkie wyświetlacze dla providera, zamiast tego można użyć updateRowData (display, start, response.getEntities()). Ale display.setVisibleRangeAndClearData (display.getVisibleRange(), true) jest jeszcze lepszy :) – Quiz

7

Okazało się teraz, poniższy kod jest w zasadzie to samo, ale z interfejsu HasData zamiast korzystania AsyncDataProvider.

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

Działa dla mnie! Dzięki! –

2

Nie rozumiem, dlaczego odpowiedź "affablebloke" jest oznaczona jako "poprawna" i została tak mocno przejęta !?

Caffeine Coma poproszony o aktualizację przeglądarki komórkowych a nie tabela komórkowych, że są zupełnie różne rzeczy!

Dla mnie zadziałała następująca sztuczka: S definiuję DataProvider s poza TreeViewModel i przekazuję je jako argument do konstruktora. W moim przypadku zapewniają one puste struktury danych na początku.

Teraz masz odniesienie do DataProvider s z "zewnątrz" w celu aktualizacji przeglądarki komórkowej.

Przykład:

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(...); 
+0

Dziękuję za udzielenie tej odpowiedzi. Działa idealnie. Od jakiegoś czasu zastanawiałem się, jak odświeżyć CellBrowser. –

0

miałem ten sam problem i to pracował dla mnie:

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); 

gdzie "pager" jest SimplePager kontrolować stronicowanie tabeli.

Mam nadzieję, że to pomoże!