Dynamically loading tree nodes

Enterprise applications usually have data sets that prohibit the loading of the full tree in a single JSON request. Large trees can be configured to load children on a per node basis by expanding levels on demand. A few minor changes to our code can allow us to implement this dynamic loading of node children.

When a node is expanded, the tree store proxy submits a request that contains a node parameter with the ID of the node being expanded. The URL submitted is that which is configured in the proxy. We will change our tree store proxy as follows:

proxy: {
  type: 'ajax',
  url: 'company/treenode.json'
}

Note that the URL of the proxy has been changed to treenode. This mapping, when implemented in CompanyHandler, will load one level at a time. The first request submitted by the proxy to load the top level of the tree will have the following format:

company/treenode.json?node=root

This will return the root node's list of companies:

{
    success: true,
    "children": [{
        "id": "C_2",
        "text": "Gieman It Solutions",
        "leaf": false
    }, {
        "id": "C_1",
        "text": "PACKT Publishing",
        "leaf": false
    }]
}

Note that there is no children array defined for each company, and the leaf property is set to false. The Ext JS tree will display an expander icon next to the node if there are no children defined and the node is not a leaf. Clicking on the expander icon will submit a request that has the node parameter set to the id value for the node being expanded. Expanding the "PACKT Publishing" node would hence submit a request to load the children via company/treenode.json?node=C_1.

The JSON response would consist of a children array that would be appended to the tree as children of the PACKT Publishing node. In our example, the response would include the projects assigned to the company:

{
    success: true,
    "children": [{
        "id": "P_3",
        "text": "Advanced Sencha ExtJS4 ",
        "leaf": false
    }, {
        "id": "P_1",
        "text": "EAD with Spring and ExtJS",
        "leaf": false
    }, {
        "id": "P_2",
        "text": "The Spring Framework for Beginners",
        "leaf": false
    }]
}

Once again each project would not define a children array, even if there are tasks assigned. Each project would be defined with "leaf":false to render an expander icon if there are tasks assigned. Expanding the P_1 node would result in the proxy submitting a request to load the next level: company/treenode.json?node=P_1.

This would result in the following JSON being returned:

{
    success: true,
    "children": [{
        "id": "T_1",
        "text": "Chapter 1",
        "leaf": true
    }, {
        "id": "T_2",
        "text": "Chapter 2",
        "leaf": true
    }, {
        "id": "T_3",
        "text": "Chapter 3",
        "leaf": true
    }]
}

This time we define these nodes with "leaf":true to ensure the expander icon is not displayed and users are unable to attempt loading a fourth level of the tree.

The CompanyHandler method that is responsible for this logic can now be defined and mapped to the company/treenode.json URL:

@RequestMapping(value = "/treenode", method = RequestMethod.GET, produces = {"application/json"})
@ResponseBody
public String getCompanyTreeNode(
    @RequestParam(value = "node", required = true) String node,
    HttpServletRequest request) {

  User sessionUser = getSessionUser(request);

  logger.info(node);

  JsonObjectBuilder builder = Json.createObjectBuilder();
  builder.add("success", true);
  JsonArrayBuilder childrenArrayBuilder =Json.createArrayBuilder();

  if(node.equals("root")){

    Result<List<Company>> ar =companyService.findAll(sessionUser.getUsername());
    if (ar.isSuccess()) {                                

      for(Company company : ar.getData()){                   
        childrenArrayBuilder.add(
          Json.createObjectBuilder()
            .add("id", getTreeNodeId(company))
            .add("text", company.getCompanyName())
            .add("leaf", company.getProjects().isEmpty())
        );
      }
    } else {

      return getJsonErrorMsg(ar.getMsg());
    }
  } else if (node.startsWith("C")){

    String[] idSplit = node.split("_");
    int idCompany = Integer.parseInt(idSplit[1]);
    Result<Company> ar = companyService.find(idCompany,sessionUser.getUsername());

    for(Project project : ar.getData().getProjects()){

      childrenArrayBuilder.add(
        Json.createObjectBuilder()
          .add("id", getTreeNodeId(project))
          .add("text", project.getProjectName())
          .add("leaf", project.getTasks().isEmpty())
      );
    }

  } else if (node.startsWith("P")){

    String[] idSplit = node.split("_");
    int idProject = Integer.parseInt(idSplit[1]);
    Result<Project> ar = projectService.find(idProject,sessionUser.getUsername());
    for(Task task : ar.getData().getTasks()){

      childrenArrayBuilder.add(
        Json.createObjectBuilder()
          .add("id", getTreeNodeId(task))
          .add("text", task.getTaskName())
          .add("leaf", true)
      );
    }
  }

  builder.add("children", childrenArrayBuilder);

  return toJsonString(builder.build());
}

The getCompanyTreeNode method determines the type of node being expanded and loads appropriate records from the service layer. The returned JSON is then consumed by the store and displayed in the tree.

We can now run the project in GlassFish and display the 3T Admin interface. The first level of the tree is loaded as expected:

Dynamically loading tree nodes

When the expander icon is clicked, the next level of the tree will be dynamically loaded:

Dynamically loading tree nodes

The third level can then be expanded to display the tasks:

Dynamically loading tree nodes

We will leave it to you to enhance the AdminController for use with dynamic trees. Reloading the tree after each successful save or delete would not be very user friendly; changing the logic to only reload the parent node would be a far better solution.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.163.229