PDA

View Full Version : Tricky tree/treegrid



catalin.ciobanu
30 Sep 2011, 6:37 AM
Hello, is possible to build a tree/treegrid by the following scenario?
Scenario:
Get some json data from server like this:

{
object1{
name
title
id
children{
object2
{name1
length1
some_other_property1
}
object2
{ name2
length2
some_other_property2
}
}
}


When the tree first loads, I want to load only object1 items. When I expand an object1 I want the loader to load the object2 items in a remote way.
I am able to get at one call only objects with type object1and on a different call, the corresponding items with type object2 but I don't know how to set a ModelType for my JsonReader because object1 and object2 have different properties, different fields.

I don't have access to modify the way that data comes from server. So I will need to find a way to do this. Getting all data in a call I tried, using local data but I want to do this in a remote way.

catalin.ciobanu
3 Oct 2011, 12:36 AM
-what method should I override if I want to execute a different request for data after the first load ?
-on expanding a folder I want to make a request for getting a different type of data!

Colin Alworth
3 Oct 2011, 7:22 AM
If the TreePanel is generic on ModelData, and the treestore and treeloader as well, anything that extends ModelData can be loaded into the store and thus displayed on the tree. The TreeLoader will get an item passed to loadChildren, and you can check the type of it to decide how to proceed, and what server call to make to load items.

This is a pretty common use case, and shouldn't be too hard to accomplish. If you are having issues making this work, please share the basic models you want to work with in the tree, and the loader code you've come up with so far.

catalin.ciobanu
3 Oct 2011, 7:40 AM
well, the Tree is ModelData... as the store and loader:
My problem is that I don't know how to setup the ModelType .. because I will load 2 different types of data with different properties. The first time I want my model type to load the folders.


ModelType type = new ModelType();
type.setRecordName(null);
type.setRoot("folder_model");
type.addField("description", "description");
type.addField("name", "name");
type.addField("properties", "properties");
type.addField("title", "title");
type.addField("uuid", "uuid");

For this JSON data:


{
"folder_model":[
{
"description":"description1",
"name":"test1",
"properties":{},
"title":"title1",
"uuid":"0610847b-6b7f-4b91-9e6d-0def9b7944d0"
},
{
"description":"description2",
"name":"test2",
"properties":{},
"title":"title2",
"uuid":"bd9cd53d-b3c0-4c7c-b623-a1a11805724d"
},
{
"description":"description3",
"name":"test3",
"properties":{number},
"title":"title3",
"uuid":"ca235a81-ca6a-40b6-8490-027fec5df02a"
}
]
}


After the first load, I want to get the files (the children) of a folder so I make another request and I will need this model type:


ModelType type = new ModelType();
type.setRecordName(null);
type.setRoot("document_model");
type.addField("name", "name");
type.addField("note", "note");
type.addField("object", "object");
type.addField("properties", "properties");
type.addField("uuid", "uuid");

For this data:


{"document_model":[{
"name":"ssss",
"note":"no_note",
"object":"Object_dwfa",
"properties":{
"number":"Number_sssda"
},
"uuid":"1fd1cf79-8709-48be-b759-b53829c1b86c"
},
{
"name":"ssss",
"note":"some note",
"object":"Ca1Obj",
"properties":{
"number":"CinNumber"
},
"uuid":"757c0a49-95e5-4656-99f8-99c17976132a"
},
{
"name":"Cat23",
"note":"som211",
"object":"CObjOsk",
"properties":{
"number":"NumeroAlto"
},
"uuid":"aec5895e-3f65-4b4a-9f99-5216d0a7c082"
}]
}


So when I expand the tree I should make a request sending the name of the event (EventGetDocuments) along the id of the folder (I suppose model.get("uuid")). How could I set the ModelType in a dynamic way .. to change it on expand ?

I have to say: The first load it is ok, works as expected. The problem shows up when I expand one of the folders of my tree as the loader makes the same request (sending the expanded model, with the first event ... EventGetFolders)

Colin Alworth
3 Oct 2011, 6:01 PM
Ok, I think I am following now - you've got a slightly more interesting problem than I had understood before.

You didn't post a lot of code, so I am assuming you've got some wiring that creates a JsonReader with that ModelType, and probably an HttpProxy that loads this content. Both are presumably given to a TreeLoader. My suggestions that follow are based on this assumption - if they are incorrect, please let me know.

With two ModelTypes, it seems to make sense to make two JsonReaders. The purpose of a DataReader is to turn some data formatted for transport into models which can be used by your application. We'll need one for each of the formats you'll be accepting, and another custom DataReader implementation to wrap them and decide which one to use.

Take a look at the main method in DataReader - it accepts a load config, the data that came over the wire, and returns the model objects you need. We'll be passing the data into which ever reader fits the current use case, and we'll figure that out from the loadConfig - but the specific logic there is up to you. I believe the loadConfig will be the parent model, which hopefully is enough.

Once you can decide which reader to use, invoke read on the relevant reader, and return the result so the loader can pass it to the store, and on to the

catalin.ciobanu
3 Oct 2011, 10:52 PM
Colin thanks a lot for taking from your time to help me!
You are right with your assumption.

Basically, I will have to develop a custom reader .. extending DataReader, giving a new (suitable for my needs) logic to method --public D read(Object loadConfig, Object data)--

Once you can decide which reader to use, invoke read on the relevant reader, and return the result so the loader can pass it to the store

I don't realy know where to do this; I would say in loadChildren .. is that right ?
And also how should I specify a different event ?

The first load I make with this data:


HashMap<String, String> hm = new HashMap<String, String>();
hm.put("eventname", "EventGetFolders");
hm.put("requesttype", "ajax");
String postData = Utilities.convertToPost(hm);
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
URL.encode(GWT.getHostPageBaseURL() + "RequestServlet"));
builder.setHeader("Content-type", "application/x-www-form-urlencoded");
...........

And after, at expanding a folder, I want to specify this data:


HashMap<String, String> hm = new HashMap<String, String>();
hm.put("eventname", "EventGetDocuments");
hm.put("uuidFolder", "the expanded folder's id");
hm.put("requesttype", "ajax");
String postData = Utilities.convertToPost(hm);
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
URL.encode(GWT.getHostPageBaseURL() + "RequestServlet"));
builder.setHeader("Content-type", "application/x-www-form-urlencoded");
.........................


And how i use it...


HttpProxy<ListLoadResult<ModelData>> proxy = new HttpProxy<ListLoadResult<ModelData>>(
builder);

ModelType type = new ModelType();
........

reader = new MyJsonReader<List<ModelData>>(type);

loader = new BaseTreeLoader<ModelData>(proxy, reader) {
@Override
protected Object prepareLoadConfig(Object config) {
return super.prepareLoadConfig(config);
}

@Override
public boolean hasChildren(ModelData parent) {
return true;
}
};

treeStore = new TreeStore<ModelData>(loader) {
@Override
public boolean hasChildren(ModelData parent) {
return true;
}
};

MyStoreSorter sorter = new MyStoreSorter(FilesTree.dir);
treeStore.setStoreSorter(sorter.getSorter());

treeStore.setKeyProvider(new ModelKeyProvider<ModelData>() {
public String getKey(ModelData model) {
return "node_" + model.<String> get("uuid");
}
});

Colin Alworth
4 Oct 2011, 8:52 AM
It looks like you might want two proxies, and a single reader for each proxy. I'll assuming you have these already figured out, and will refer to them as nodeProxy and leafProxy, where the nodeProxy asks for non-leaf nodes, and leafProxy only gets the leaf nodes. Additionally, I'm going to refer to nodeJsonReader and leafJsonReader, JsonReader objects set up with the correct ModelTypes.

TreeLoader is an interface with two methods, one to get children, one to check if children exist. You started off by extending BaseTreeLoader, but this class is designed to take at most one proxy/reader. So we're going to need to cheat slightly. Our interest lies in changing how it gets data from the server, and how it reads it. Construct the BaseTreeLoader with a null for the proxy (or reader), and we'll implement our own way of pulling in data, selecting the correct proxy/reader combination.


new BaseTreeLoader<ModelData>((DataProxy<?>)null) {
protected void loadData(Object config, AsyncCallback<List<M>> callback) {
if (isNodeObject(config)) { //so incoming data are leaves
leafProxy.load(leafJsonReader, config, callback);
} else { //incoming data are nodes
nodeProxy.load(nodeJsonReader, config, callback);
}
}

If I'm mistaken, and you only make one kind of request, just with different parameters, here is a different approach: DataReader is just an interface with one method. Readers from the previous example are used to make one big reader here (not tested either):


DataReader<ModelData> everythingReader = new DataReader<ModelData>() {
public ModelData read(Object loadConfig, Object data) {
if (isNodeObject(loadConfig)) {//so incoming is a leaf
return leafJsonReader.read(loadConfig,data);
} else {// so incoming is a node
return nodeJsonReader.read(loadConfig,data);
}
}
}

catalin.ciobanu
5 Oct 2011, 2:46 AM
Thank you very much Colin, I got a little help and I made it without using proxy/loader/reader at all.
Just getting the string, parsing the JSON and adding the element to the store.

I send the wanted request using the BeforeExpand event with a listener which makes the request. It's faster and it works just fine .. which is good.

Thank you again, keep up the good work.

Colin Alworth
5 Oct 2011, 8:00 AM
Glad to hear it. The Loader can easily be used alone, as you've noticed, and the readers/proxies are meant as tools to plug into it -- but if you can easily do the job with a simpler tool, that is almost always the right choice.