Skip to content
Martin@MBP edited this page Mar 9, 2016 · 39 revisions

About Fancytree edit extension.

Allow to change node titles or create new nodes using inline editing.

Options

  • (See comments in example code below.)

Events

  • (See comments in example code below.)

Methods

  • {void} node.editStart()
    Start inline editing of current node title.

  • {void} node.editEnd(applyChanges)
    Stop inline editing.
    {boolean} [applyChanges=false]

  • {void} node.editCreateNode(mode, init)
    Create a new child or sibling node and start edit mode.
    {string} [mode='child'] 'before', 'after', or 'child'
    {object} [init] NodeData (or simple title string)

Example

In addition to jQuery, jQuery UI, and Fancytree, include jquery.fancytree.edit.js:

  <script src="//code.jquery.com/jquery-1.12.1.min.js" type="text/javascript"></script>
  <script src="//code.jquery.com/ui/1.11.4/jquery-ui.min.js" type="text/javascript"></script>
  <link href="skin-win8/ui.fancytree.css" rel="stylesheet" type="text/css">
  <script src="js/jquery.fancytree.js" type="text/javascript"></script>
  <script src="js/jquery.fancytree.edit.js" type="text/javascript"></script>

Enable edit extension and pass options:

$("#tree").fancytree({
  extensions: ["edit"],
  edit: {
    // Available options with their default:
    adjustWidthOfs: 4,   // null: don't adjust input size to content
    inputCss: {minWidth: "3em"},
    triggerCancel: ["esc", "tab", "click"],
    triggerStart: ["f2", "dblclick", "shift+click", "mac+enter"],
    beforeEdit: $.noop,  // Return false to prevent edit mode
    edit: $.noop,        // Editor was opened (available as data.input)
    beforeClose: $.noop, // Return false to prevent cancel/save (data.input is available)
    save: $.noop         // Save data.input.val() or return false to keep editor open
    close: $.noop,       // Editor was removed
  },
  [...]
});

For example:

$("#tree").fancytree({
  extensions: ["edit"],
  ...
  edit: {
    beforeEdit: function(event, data){
      // `data.node` is about to be edited.
      // Return false to prevent this.
    },
    edit: function(event, data){
      // `data.node` switched into edit mode.
      // The <input> element was created (available as jQuery object `data.input`) 
      // and contains the original `data.node.title`.
    },
    beforeClose: function(event, data){
      // Editing is about to end (either cancel or save).
      // Additional information is available:
      // - `data.orgTitle`: The previous node title text.
      // - `data.input`:    The input element (jQuery object).
      //                    `data.input.val()` returns the new node title.
      // - `data.save`:     false if saving is not required, i.e. user pressed  
      //                    cancel or text is unchanged.
      // - `data.dirty`:    true if text was modified by user.
      // - `data.isNew`:    true if this node was newly created using `editCreateNode()`.
      // Return false to prevent this (keep the editor open), for example when 
      // validations fail.
    },
    save: function(event, data){
      // Only called when the text was modified and the user pressed enter or
      // the <input> lost focus.
      // Additional information is available (see `beforeClose`).
      // Return false to keep editor open, for example when validations fail.
      // Otherwise the user input is accepted as `node.title` and the <input> 
      // is removed.
      // Typically we would also issue an Ajax request here to send the new data 
      // to the server (and handle potential errors when the asynchronous request 
      // returns). 
    },
    close: function(event, data){
      // Editor was removed.
      // Additional information is available (see `beforeClose`).
    }
  }
});

New nodes can be created in edit mode like this. Hitting Esc would cancel and Enter will accept:

node.editCreateNode("after", {
  title: "Node title",
  folder: true
});

Recipes

[Howto] Send changes to server (optimistic approach)

Here is an example for a robust handling of node renaming with user feeback.

$("#tree").fancytree({
  extensions: ["edit"],
  ...
  edit: {
    save: function(event, data){
      var node = data.node;

      // Save data.input.val() or return false to keep the editor open
      $.ajax({
        url: "saveNode",
        data: { key: node.key, title: data.input.val() }

      }).done(function(result){
        // Server might return an error or a modified title
        node.setTitle(result.acceptedTitle); // in case server modified it
        // Maybe also check for non-ajax errors, e.g. 'title invalid', ...

      }).fail(function(result){
        // Ajax error: reset title (and maybe issue a warning)
        node.setTitle(data.orgTitle);

      }).always(function(){
        data.input.removeClass("pending");
      });
      // Optimistically assume that save will succeed. Accept the user input
      return true;
    }
  },
  close: function(event, data){
    // Editor was removed. If we started an async request, mark the node as pending
    if( data.save ) {
      $(data.node.span).addClass("pending");
    }
  },
  ...
});

We also might add some CSS for a nice 'pending' style:

span.pending span.fancytree-title {
  font-style: italic;
}
span.pending span.fancytree-title:after {
  content: "\2026"; /* ellipsis */
}
[Howto] Use ext-edit together with ext-dnd

Fancytree uses the standard jQuery UI draggable which prevents mouse clicks from setting the focus (details).

In combination with the ext-edit extension, this can prevent that keyboard input (such as [F2]) is delivered to the tree.
Use the dnd.focusOnClick: true option in this case.

$("#tree").fancytree({
  extensions: ["dnd", "edit"],
  dnd: {
    focusOnClick: true,  // Focus, although draggable cancels mousedown event
    ...
  },
 edit: {
    ...
 },
 [...]
});
Clone this wiki locally