Skip to content
Martin Wendt edited this page Nov 11, 2017 · 6 revisions

How to handle nodes by type.

Assume a large tree of products that is organized by type:

Books
  +- Little Prince
  +- The Hobbit
Computers
  +- PC
  +- Mac

All book nodes should have a book-icon, while all computer nodes should have a computer icon. All nodes should also have icon tooltips.

This would require to define and transport the same data over and over again:

[
  {"title": "Books", "folder": true, "children": [
    {"title": "Little Prince", "icon": "ft-ico-book", "iconTooltip": "This is a book.", "price": 1.23},
    {"title": "The Hobbit", "icon": "ft-ico-book", "iconTooltip": "This is a book.", "price": 2.34}
  ]},
  {"title": "Computers", "folder": true, "children": [
    {"title": "PC", "icon": "ft-ico-computer", "iconTooltip": "This is a computer.", "price": 549.85},
    {"title": "Mac", "icon": "ft-ico-computer", "iconTooltip": "This is a computer.", "price": 789.00}
  ]}
]

Instead, we could use a simple pattern that allows to share data per node type.

NOTE: Proposal: Some of this is not yet implemented!
Still it is already possible to follow the basic idea of this pattern with the current release. (Just access node.data.type/tree.data.types instead of node.type/tree.types/data.typeInfo)

Proposed changes:

  • New first-class property node.type
  • New first-class property tree.types
  • All events pass data.typeInfo (or {} if not defined)
  • TODO: should data.typeInfo.icon be evaluated automatically to set the icon without having to define the icon callback?
  • TODO: should we support hierarchical types? For example: books, books.english, and books.german. We need a use case for this, but at least '.' should be proposed as delimiter for our pattern.

Allow to define options by type name:

$("#tree").fancytree({
  // `types` is a tree option, that can be used to define shared data per node-type.
  // It should contain an object with one key per expected type.
  // We can pass it directly as fancytree option like this:
  types: {
    "book": {icon: "ft-ico-book", iconTooltip: "This is a book"},
    "computer": {icon: "ft-ico-computer", iconTooltip: "This is a computer"},
  },
  source: { url: "/my/web/service"},
  icon: function(event, data) {
    // data.typeInfo contains tree.types[node.type] (or {} if not found)
    // Here we will return the specific icon for that type, or `undefined` if
    // not type info is defined (in this case a default icon is displayed).
    return data.typeInfo.icon;
  },
  iconTooltip: function(event, data) {
     return data.typeInfo.iconTooltip;
  }
});

Sometimes it may be more convenient to get the shared types info from the server. The source JSON data can be a list of child nodes or an object with additional meta data. Here we use the latter format to pass type information as well:

{
  "children": [
    {"title": "Books", "folder": true, "children": [
      {"title": "Little Prince", "type": "book", "price": 1.23},
      {"title": "The Hobbit", "type": "book", "price": 2.34}
    ]},
    {"title": "Computers", "folder": true, "children": [
      {"title": "PC", "type": "computer", "price": 549.85},
      {"title": "Mac", "type": "computer", "price": 789.00}
    ]}
  ],
  "types": {
    "book": {"icon": "ft-ico-book", "iconTooltip": "This is a book"},
    "computer": {"icon": "ft-ico-computer", "iconTooltip": "This is a computer."},
  }
}

Note that sometimes things can even be more simplified by imposing conventions. For example if the icon class names follow a simple rule, we may omit the icon data altogether and derive it from the type:

$("#tree").fancytree({
  ...
  icon: function(event, data) {
    if( data.node.type ) {
      return "ft-ico-" + data.node.type;
    }
  },
});
Clone this wiki locally