Search code examples
javajsonjspspring-mvcdynatree

Create a dynamic treeview with json data - dynatree in jsp


I have a table in database like this:

id   -   title   -  parentID
-----------------------------
1        Root          null
2        item 1        1
3        item 2        1
4        item 3        1
5        item 3.1      4

this should create something like this:

--Root
----item 1
----item 2
----item 3
-------item 3.1

here's my controller to fetch the root node and its children:

@RequestMapping(value="/CourtBranch/LoadTreeView", method = RequestMethod.GET)
    public void LoadList(@RequestParam("ParentId") String parentID,HttpServletRequest req, HttpServletResponse resp) {
        List lst;
        if (parentID.equals("Root"))
        {
            lst = _COURTBRANCH.LoadTreeChildren(null, "Root");   // selects records which have parent=null
        }
        else
        {
            lst = _COURTBRANCH.LoadTreeChildren(parentID, "TreeNode");   // selects records which have parent=parentID
        }

        resp.setContentType("application/json");
        resp.setCharacterEncoding("utf-8");
        try {
            resp.getWriter().print(_gson.toJson(lst));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

and this script loads my root:

<script type="text/javascript">
  $(function(){
    $("#tree").dynatree({
          initAjax: {
              url: "/CourtBranch/LoadTreeView.json?ParentId=Root",
              data: { mode: "funnyMode" }
              },
      onActivate: function(node) {
        $("#echoActive").text(node.data.title);
      },
      onDeactivate: function(node) {
        $("#echoActive").text("-");
      }
    });
  });
</script>

now I need to know how to send the root's id to my controller to fetch root's children and add them to the root node. should I use appendAjax function? how?


Solution

  • Basically you don't really want to do that straight into your controller. (loose coupling)

    I'd create 2 utils classes Tree<T> and Node<T> to manage your tree and nodes:

    Here is the tree class:

    public class Tree<T> {
    
        private Node<T> rootElement;
    
        public Tree() {
            super();
        }
    
        public Node<T> getRootElement() {
            return this.rootElement;
        }
    
        public void setRootElement(final Node<T> rootElement) {
            this.rootElement = rootElement;
        }
    
        public List<Node<T>> toList() {
            final List<Node<T>> list = new ArrayList<Node<T>>();
            walk(this.rootElement, list);
            return list;
        }
    
        private void walk(final Node<T> element, final List<Node<T>> list) {
            list.add(element);
            for (final Node<T> data : element.getChildren()) {
                walk(data, list);
            }
        }
    }
    

    And the node class with some helper methods

    public class Node<T> {
    
        private T data;
        private List<Node<T>> children;
    
        public Node() {
            super();
        }
    
        public Node(final T data) {
            this();
            setData(data);
        }
    
        public Boolean hasChildren() {
            return this.children.size() != 0;
        }
    
        public List<Node<T>> getChildren() {
            if (this.children == null) {
                return new ArrayList<Node<T>>();
            }
            return this.children;
        }
    
        public void setChildren(final List<Node<T>> children) {
            this.children = children;
        }
    
        public int getNumberOfChildren() {
            return this.children.size();
        }
    
        public void addChild(final Node<T> child) {
            if (this.children == null) {
                this.children = new ArrayList<Node<T>>();
            }
            this.children.add(child);
        }
    
        public void insertChildAt(final int index, final Node<T> child) throws IndexOutOfBoundsException {
            if (index == getNumberOfChildren()) {
                addChild(child);
                return;
            } else {
                this.children.get(index);
                this.children.add(index, child);
            }
        }
    
        public void removeChildAt(final int index) throws IndexOutOfBoundsException {
            this.children.remove(index);
        }
    
        public T getData() {
            return this.data;
        }
    
        public void setData(final T data) {
            this.data = data;
        }
    }
    

    Then I'd have your type of tree extending the tree. based on your controller CourtBranch

    Suppose your CourtBranch model has the following structure (I'm using hibernate + jpa):

    @Entity
    @Table
    public class CourtBranch
    
    private String id;
    private String name;
    private Long partentId;
    
    //getters setters etc...
    

    Create a class that extends the Tree:

    public class CourtBranchTree extends Tree<CourtBranch>
        public CourtBranchTree{
            super();
        }
    

    Now create your TreeGridResponse class:

    public class TreeGridResponse {
        //inject the service to get the id of your model
        @Resource
        CourtBranchService cbService;
        //if you are using a repository for the db queries:
        @Resource
        CourtBranchRepository cbRepo;
    
        public TreeGridResponse(){
        }
    
        //returning the tree as a JSON to use AJAX
        public String cbBTreeAsJson(final CourtBranchTree tree){
            final StringBuffer sb = new StringBuffer();
            final CourtBranch root = tree.getRootElement().getData();
            sb.append("[\r {\"title\": \"" + root.getName() + "\", \"key\": \"" + root.getId() + "\", \"children\": [\r");
            final List<Node<CourtBranch>> children = tree.getRootElement().getChildren();
            loopforChildre(sb, children);
            sb.append("]");
            return sb.toString();
        }
    
        private StringBuffer loopForChildren(final StringBuffer sb, final List<Node<UserRight>> children) {
            for (int i = 0; i < children.size(); i++) {
                final Node<courtBranch> childElement = children.get(i);
                if (i == 0) {
                    sb.append("{\"title\": \"" + childElement.getData().getName() + "\", \"key\": \"" + childElement.getData().getId() + "\"");
                } else {
                    sb.append(", {\"title\": \"" + childElement.getData().getName() + "\", \"key\": \"" + childElement.getData().getId() + "\"");
                }
                if (childElement.hasChildren()) {
                    sb.append(", \"children\": [\r");
                    loopForChildren(sb, childElement.getChildren());
                } else {
                    sb.append("}");
                }
            }
            sb.append("]}");
            return sb;
        }
    
        public CourtBranchTree get() {
            final CourtBranchTreetree tree = new CourtBranchTree();
            final Node<CourtBranch> root = new Node<CourtBranch> (this.cbRepo.findOne(Long.valueOf(1)));//gets your root
            getRecursive(root, tree);
            tree.setRootElement(root);
            return tree;
        }
    
        private void getRecursive(final Node<CourtBranch> courtBranch, final CourtBranchTree tree) {
            final List<CourtBranch> children = this.cbService.findCourtBranchByParentId(courtBranch.getData().getId());
            final List<Node<CourtBranch>> childElements = new ArrayList<Node<CourtBranch>>();
            for (final CourtBranch childCourtBranch : children) {
                final Node<CourtBranch> childElement = new Node<CourtBranch>(childUserRight);
                childElements.add(childElement);
                getRecursive(childElement, tree);
            }
            courtBranch.setChildren(childElements);
        }
    }
    

    Register the TreeGridResponse in your config to get the bean and inject it into your controller.

    @Controller
    public class CBController
    
    @Resource
    private TreeGridResponse gridResponse;
    
    @RequestMapping(value="/CourtBranch/LoadTreeView", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public String cbTree() {
        return this.gridResponse.cbBTreeAsJson(this.gridResponse.get());
    }
    

    Note the @ResponseBody annotation that will indicate Spring to parse your string into JSON so that your AJAX can read it.

    And your jsp:

    <div id ="cbTree"></div>
    <script type ="text/javascript">
        $(function(){
        $("#cbTree").dynatree({
            initAjax:{
                url: /CourtBranch/LoadTreeView
                },
            checkbox: true,
            selectMode: 3
        });
        });
    </script>
    

    Hope this helped...