Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Tree load on demand questions

    SmartClient Version: v9.1p_2014-05-07/PowerEdition Deployment (built 2014-05-07)
    GWT 2.5.1
    IE-11

    I'm in the process of implementing the Tree load on demand feature for a TreeGrid. The example listed in the showcase is a very simply example setup for client only. I am working with a Dynamic Datasource which is a little more complex. Starting out, I have a simple question:

    How do I get an instance of ResultTree? Am I suppose to create that on my own, or do I simply do a TreeGrid.getData() call which returns a Tree? Assuming that Tree returned is an implementation of a ResultTree?

    #2
    A ResultTree is a subclass of Tree.

    A ResultTree is automatically created by a databound TreeGrid when it fetches data from its dataSource. For a load-on-demand tree with initial data, like the one in the showcase example, the initial data is used to seed the generated ResultTree with an initial set of results provided on the client.

    If necessary, you can apply some configuration to the generated ResultTree via the "setDataProperties" API.

    You can get at the generated ResultTree by looking at the TreeGrid's data object.

    Note - if you want to you can also create a ResultTree explicitly in your code and either manipulate it directly using its documented APIs or pass it to a TreeGrid via a setData call. This is a more advanced usage and not usually necessary.

    Regards
    Isomorphic Software

    Comment


      #3
      Here is how I am setting up my TreeGrid:

      Code:
                  ResultTree resultTree = new ResultTree();
                  resultTree.setRootValue("0-1");
                  resultTree.setModelType(TreeModelType.PARENT);
                  resultTree.setIdField("cid");
                  resultTree.setParentIdField("pid");
      
                  treeGrid.setData(resultTree);
      When I set the ResultTree into the TreeGrid via treeGrid.setData(resultTree); I am seeing this error message:

      Code:
      ERROR: Uncaught exception escaped
      com.google.gwt.core.client.JavaScriptException: (TypeError) @com.google.gwt.core.client.impl.Impl::apply(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)([JavaScript object(8464), JavaScript object(8463), JavaScript object(8495)]): Unable to get property 'getTreeRelationship' of undefined or null reference
      	at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:249)
      	at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:136)
      	at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:576)
      	at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:284)
      	at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
      	at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
      	at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:347)
      	at sun.reflect.GeneratedMethodAccessor46.invoke(Unknown Source)
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at java.lang.reflect.Method.invoke(Method.java:606)
      	at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
      	at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
      	at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
      	at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
      	at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
      	at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
      	at java.lang.Thread.run(Thread.java:744)

      Comment


        #4
        It appears you haven't provided a DataSource for your ResultTree.

        Comment


          #5
          OK, that was the issue. Thank you.

          Now I need to add some custom data to the Criteria that gets sent in the Criteria that contains the parent ID, in my case, the Criteria has 'pid' -> "0-1".

          I tried overriding the TreeGrid.getCriteria() method to insert the application specific data into the Criteria, but I am not seeing that added data in the Criteria on the first one being passed to load data. All I see is 'pid' -> "0-1".

          How can I add custom data to the Criteria for loading more data to the tree on demand?

          To clarify, I need to add application data to the Criteria that gets sent to expand/load more data to the Tree, how can I do this? Is there a method I can over-ride to add data items to the Criteria (which already contains the 'pid' value of the node to be expanded)?
          Last edited by JLivermore; 26 Aug 2014, 11:45.

          Comment


            #6
            In the TreeDataBinding javadoc, it says
            Code:
            When the user expands a folder whose children have not yet been loaded from the server (or you programmatically call openFolder() on such a node), the client automatically sends a DSRequest to the server to ask for all immediate children of that node.
            My question is how can I add Criteria data items to the Criteria that is automatically sent to the server for node expansion/loading?

            We have our own back end API to load nodes and I need to send more application state along with the parent node ID to load more Tree data on demand.

            How would I do this?

            Comment


              #7
              Just to show how my Dynamic DataSource is generating the parent/child relationship, here is the output for the Parent and Child fields:

              Code:
              <?xml version="1.0" encoding="UTF-8"?>
              <DataSource ID="pivot_table_tree_fact_POF_384_682" serverConstructor="com.anstca.tz.server.floater.pivottable.ds.PivotTableTreeDataSource" serverType="generic">
              <fields>
              <field name="cid"   type="text"   primaryKey="true" required="true" hidden="true"/>
              <field name="pid"   type="text" foreignKey="pivot_table_tree_fact_POF_384_682.cid" rootValue="0-1" required="true" hidden="true"/>
              Does this look correct for setting up 'cid' and 'pid'? Note 'pid' has a foreignKey that simply points to 'cid'. Do I need the data source name pre-pended to the field name if the foreign key is local to this data source? Or can I just define it w/o prepending the local data source ID like this?

              Code:
              <?xml version="1.0" encoding="UTF-8"?>
              <DataSource ID="pivot_table_tree_fact_POF_384_682" serverConstructor="com.anstca.tz.server.floater.pivottable.ds.PivotTableTreeDataSource" serverType="generic">
              <fields>
              <field name="cid"   type="text"   primaryKey="true" required="true" hidden="true"/>
              <field name="pid"   type="text" foreignKey="cid" rootValue="0-1" required="true" hidden="true"/>
              Last edited by JLivermore; 27 Aug 2014, 06:58.

              Comment


                #8
                Another way to go, would be to key off the FolderOpened event when you click on the folder icon to open the folder. From there I can create my Criteria manually and call fetchData(Criteria).

                ResultTree appears to be for auto-fetch purposes only, I can't seem to get it to work with a manual fetch.
                Last edited by JLivermore; 27 Aug 2014, 15:19.

                Comment


                  #9
                  Firstly, if you haven't already, we recommend you start by reading the Tree DataBinding overview.
                  This lays out the basics of how a databound trees work.

                  The ResultTree is a subclass of Tree which automatically handles issuing fetch requests against your DataSource when you open folders for which data has not yet been loaded.

                  Common usage does not require you to create a ResultTree directly yourself - when you create a TreeGrid and specify a DataSource for it, it will automatically create a ResultTree with appropriate settings to fetch data from that dataSource when necessary.
                  If autoFetchData is true on your TreeGrid, a fetch occurs on draw (passing in initialCriteria), otherwise you have to manually call "fetchData()" on the TreeGrid to cause the ResultTree to be created and the resulting data to be displayed.

                  Aside: If you need to customize the generated ResultTree in some way, this can be achieved via setDataProperties() on your TreeGrid.
                  You can also create a ResultTree explicitly in your code, but this is typically not required. We wouldn't recommend this unless you have a specific reason not to go through standard TreeGrid data-binding.

                  When the TreeGrid performs its fetch (Either from draw when autoFetchData is true, or a call to fetchData()), the ResultTree is created and fetches its initial set of data (those that will be displayed at the root level).
                  The specified "criteria" passed into fetchData at the TreeGrid level will be handed to the ResultTree as an overarching set of criteria to be passed to the server every time it requests nodes. You can think of it as a "super-set" of criteria to apply in addition to the criteria specific to each actual node. So if you open a parent, the fetch request will include these criteria plus the parent-id field value for the parent node.

                  This brings us to your specific problem: You want to have the criteria passed to the server when fetching the children of a parent include additional information than just the parent-id field value.
                  The easiest way to achieve this is to have your DataSource modify the criteria as part of transformRequest. Note that for a server-side dataSource (i.e. - a dataSource defined using a .ds.xml file), you can modify transformRequest via the DataSource.get(...) API, passing in a custom RequestTransformer. If you set the sendParentNode attribute on your DataSource, when fetch requests are issued, the entire parent node object will be available on the request as dsRequest.parentNode. Your custom logic can then query this object and modify the criteria as you see fit before sending them to the server.

                  To answer your more specific questions:
                  - we would not recommend you attempt to react to a folderOpened event on a tree and perform your own fetch and plug the result into your Tree. This would be extremely complicated to do and to get right, and the ResultTree plus standard DataSource / DataBinding techniques should give you everything you need already.

                  - your parent/child relationship seems fine. You don't need to reiterate the DataSource ID on the foreignKey for a field within the same DS - the fieldName is sufficient, but it shouldn't do any harm if you do prepend with the DS ID.

                  Regards
                  Isomorphic Software

                  Comment


                    #10
                    Thank you for the detailed response.

                    I do have a requirement to refresh the TreeGrid on demand which would involved a manual fetchData. The way my TreeGrid is setup now, this is what I am doing based on the older example (before July 11) on the Wiki for refreshing a Grid. The only problem is my data sets are too large to fetch the entire Tree in one shot, so now I have to implement the Tree load on demand.

                    Would you recommend I implement the Tree on demand load first like you suggest above, and then work on the manual Grid update/refresh after the Tree on demand is working? Or should I just go down the more complicated path of having a TreeGrid load on demand with a manual fetchData() and tie that into the FolderOpened event?

                    One thing to note, I am using a Dynamic Datasource if that matters.

                    Do you have an example of the more complicated path of Tree on demand by doing a manual fetchData()? You mentioned this is very complicated to do right, I think this is what I need to do. Is this something you can post to the Wiki so that the steps are clear and we get it right?
                    Last edited by JLivermore; 28 Aug 2014, 05:46.

                    Comment


                      #11
                      I created a RequestTransformer to test:
                      Code:
                      private class TreeGridRequestTransformer extends RequestTransformer {
                      
                              @Override
                              protected Object transformRequest(DSRequest dsRequest) {
                                  Criteria criteria = new Criteria();
                                  criteria.addCriteria("look", "atMe");
                                  dsRequest.setCriteria(criteria);
                                  return super.getDefaultTransformRequest(dsRequest);
                              }
                          }
                      and then I added it when requesting my Custom DataSource:
                      Code:
                      treeDataSource = DataSource.get(treeGridFactTableDataSource, new TreeGridRequestTransformer(), null);
                              if (treeDataSource != null) {
                      
                                  if(DEBUG) GWT.log("treeGrid DataSource set : " + treeDataSource);
                                  if(treeDataSource.getShowPrompt()) treeDataSource.setShowPrompt(Boolean.FALSE);
                      
                      
                                  treeGrid.setAutoFetchData(Boolean.TRUE);
                                  treeGrid.setDataSource(treeDataSource);
                      
                              }
                      And then setting a break point on the server code to inspect the Criteria, I do not see the parentId set? Only my 'look' criteria is contained in the Criteria passed?

                      If I simply change the DataSource.get() call back to
                      Code:
                      treeDataSource = DataSource.get(treeGridFactTableDataSource);
                      I see the parentId value in the Criteria. Is this correct? I was expecting to see the parentId value and my test value when passing in the TreeGridRequestTransformer when doing a DataSource.get()?
                      Last edited by JLivermore; 28 Aug 2014, 07:44.

                      Comment


                        #12
                        The requestTransformer is working correctly - the thing is that by calling "setCriteria" you're overriding the current criteria of the request.
                        To also retain the original criteria, you'll want to call request.getCriteria() and then combine that with the additional criteria you need to apply.

                        Note also that the request transformer will be called for each request for this dataSource, so if your dataSource is used for more operations than just a fetch, you may want to check the operationType and only apply your customizations to fetches.

                        --
                        To answer your earlier post:
                        For your usage - yes - we'd recommend you use the approach we describe - the standard TreeGrid databinding, plus a request transformer to modify the criteria so your fetch returns appropriate records.
                        If you need to refresh the tree after it has already loaded nodes, you have a couple of options:

                        - if changes have been made to some specific record or records in the server data and you need to simply update the tree to reflect these changes:
                        * if you went through standard DataBinding methods (dataSource.updateData, or a save on a databound component), the treegrid will automatically refresh and display the modified data.
                        * if the data change occurred some other way - for example due to a change on the server data which you want to react to - you can tell the DataSource about the change by building your own DSResponse which indicates what has changed and passing it to updateCaches.

                        - If you want to simply tell the TreeGrid to refresh it's entire dataSet, you can call invalidateCache() on the TreeGrid (inherited method from ListGrid).

                        - If you want to perform a fetch with new overarching criteria, you can call "fetchData()" on the treeGrid again, and it'll perform a new fetch with the new set of criteria [potentially loading an entirely different root node, etc].

                        If none of these match your requirements, can you explain your exact use case and we'll try to steer you in the most practical direction.

                        Regards
                        Isomorphic Software

                        Comment


                          #13
                          Thank you for the detailed response. I changed the RequestTransformer like you suggested, and everything is working like you explained.
                          Code:
                          private class TreeGridRequestTransformer extends RequestTransformer {
                          
                                  @Override
                                  protected Object transformRequest(DSRequest dsRequest) {
                                      if(dsRequest.getOperationType().equals(DSOperationType.FETCH)) {
                                          dsRequest.getCriteria()addCriteria("look", "atMe");
                                      }
                                      return super.getDefaultTransformRequest(dsRequest);
                                  }
                              }
                          I will look into your suggestions in terms of doing a Grid refresh. Thank you again, you've been very helpful.

                          Comment


                            #14
                            Does the FolderOpened event trigger before or after the DataSource sends it's request for Tree on demand loading?

                            The reason I ask, is I need make a change to our data structure about the Node being expanded *before* the DataSource sends the DSRequest with the ParentID to load more data.

                            The FolderOpened event does trigger before the DataSource send's it's request.
                            Last edited by JLivermore; 29 Aug 2014, 12:23.

                            Comment


                              #15
                              Problem
                              --------
                              Our TreeGrid can dynamically expand in depth depending on user interaction with our grid. There will be cases when leaf nodes will become folder nodes. Because the TreeGrid assumes all nodes being returned are folder nodes, we would like to retain that state for the leaf nodes, because the user may make a change to the state of underlying data structures external to the TreeGrid that will in fact make those leaf nodes become folder nodes that are expandable and go deeper.

                              So when the user clicks on a Leaf node, even though there are no records under that node at the time the user clicks, we would like to retain the folder icon for that node. Because the user may make changes outside of the TreeGrid that may make the TreeGrid data deeper, thus afterwards, the user will beable to click on that (previously leaf node) folder node and go deeper into the tree data.

                              Possible Solution
                              ----------------
                              Is there anyway in my RequestTransformer to cancel the DSRequest being made for a leaf node? I'm not sure if we can suppress a DSRequest? If a DSRequest is made, the TreeGrid is waiting for a response. If we get no response back, we will loose the folder icon displayed in the Tree. Perhaps we should manually set the node back to a Folder node?

                              Seems like I will have to get all the leaf nodes and reset them to folder nodes manually to make the folder icon reappear.
                              Last edited by JLivermore; 4 Sep 2014, 12:24.

                              Comment

                              Working...
                              X