Tuesday, July 31, 2012

Re: AsyncDataProvider and CellTree


On Monday, July 30, 2012 10:02:03 PM UTC+2, Thad wrote:
I have a CellTree in which I want to represent something similar to a directory listing (actually represented in database records):

public class DirectoryObject implements Serializable {
    public String name;
    public String type; // 'file' or 'folder'
    public int group;
    ...
}

This listing comes from a server application which the user must log into. Naturally I can't call the RPC to populate the tree until the login takes place.

My wish is to draw the UI and upon login (or upon pressing a 'List' or 'Refresh' button to return an ArrayList<DirectoryObject>. From this I populate the previously empty tree. Objects of type 'folder' will be the branch nodes and will require a new RPC call with the name as a parameter to find the children (null gets me the highest level).

My question is how, once the empty tree is present, to trigger that call and start my listing. So far I've got what you see below, but I'm not sure if it's right and I'm stumped on the UiHandler for my list button (at the very bottom). I'm trying to follow the CellTree examples, but they use static data or don't start empty.

public class DirectoryPanel extends Composite {

  private static DirectoryPanelUiBinder uiBinder = GWT
      .create(DirectoryPanelUiBinder.class);

  interface DirectoryPanelUiBinder extends
    UiBinder<Widget, DirectoryPanel> {
  }
  
  private static class MyDataProvider extends AsyncDataProvider<DirectoryObject> {
    
    private String folderName;
    
    public MyDataProvider(DirectoryObject directory) {
      if (directory != null)
        folderName = directory.name;
    }

    @Override
    protected void onRangeChanged(HasData<DirectoryObject> display) {
      final Range range = display.getVisibleRange();

      AsyncCallback<ArrayList<DirectoryObject>> callback = 
          new AsyncCallback<ArrayList<DirectoryObject>>() {
        @Override
        public void onFailure(Throwable caught) {
          Window.alert((new GwtOptixException(caught)).getMessage());
        }

        @Override
        public void onSuccess(ArrayList<DirectoryObject> result) {
          int start = range.getStart();
          GWT.log("onRangeChanged, start: "+start);
          updateRowData(start, result);
        }
      };
      Cold.getRpcService().getDirectoryListing(folderName, callback);
    }
  }
  
  private static class DirectoryTreeModel implements TreeViewModel {
    
    private SingleSelectionModel<DirectoryObject> selectionModel = 
        new SingleSelectionModel<DirectoryObject>();

    public DirectoryTreeModel() {
      selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler(){
        public void onSelectionChange(SelectionChangeEvent event){
          /* Huh? 
            but there's no getSelectedObject() in SelectionChangeEvent
            */
          //DirectoryObject rec = (DirectoryObject)event.getSelectedObject();

It's actually selectionModel.getSelectedObject() that returns your real DirectoryObject.
 
        }
      });
    }
    
    @Override
    public <T> NodeInfo<?> getNodeInfo(T arg0) {
      if (arg0 instanceof DirectoryObject) {
        Cell<DirectoryObject> cell = new AbstractCell<DirectoryObject>() {
          @Override
          public void render(Context context, DirectoryObject value,
              SafeHtmlBuilder sb) {
            if (value != null) {
              sb.appendEscaped(value.name);
            }
          }
        };
        MyDataProvider provider = new MyDataProvider((DirectoryObject)arg0);
        return new DefaultNodeInfo<DirectoryObject>(provider, cell, selectionModel, null);
        // or return new DefaultNodeInfo<DirectoryObject>(provider, cell); ??

The first line: generally you should provide a selection model for all your nodes if you want to do something when they are selected (the actual expansion, if not a leaf, is a provider's job).

      }
      return null;
    }

    @Override
    public boolean isLeaf(Object arg0) {
      return arg0 != null && ((DirectoryObject)arg0).name != null &&
          ((DirectoryObject)arg0).type.equals('file');
    }
  }
  
  @UiField(provided = true)
  CellTree tree;
  @UiField
  Button list;
  
  DirectoryTreeModel treeModel;

  public DirectoryPanel() {
    treeModel = new DirectoryTreeModel();
    tree = new CellTree(treeModel, null);
    
    initWidget(uiBinder.createAndBindUi(this));
  }
  
  @UiHandler("list")
  void onList(ClickEvent event) {
    // What here??
  }

}

I'd:
- create the cell tree only if the user has logged in (and show something like an empty message);
- create a root null node (as you did) but refuse the expansion/selection until the user has logged in (probably an async provider's job, i.e., the inner RPC is used in an authentication mechanism and refuses requests from unauthenticated clients);
- create a default root node that does nothing at all, backed by a no-op (null) selection model and when the used logs in, replace it with the real selection model (I think the only way is re-instantiate the cell tree).

I think the best way to programmatically select a node is by using the selection model, although I don't remember if it also expand inner nodes.

Hope that helps.

--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-web-toolkit/-/2lAkaJUvDY8J.
To post to this group, send email to google-web-toolkit@googlegroups.com.
To unsubscribe from this group, send email to google-web-toolkit+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.

No comments:

Post a Comment