Treelayout doesnt scrollToRect() if I expandTree() of parent node

I have a tree based on Tree View sample. I have a search routine that locates a child node, selects it and scrollsToRect. It works fine if the parent is expanded but if I expandTree(parent), it selects the node but fails to scroll to it. Here is how I am doing that part. thanks for your help.

  doSearchTargetsTree: function() { // called from Search button.
var searchStr = $('#searchTerm').val();
var searchStrRegExp = new RegExp(searchStr, 'i');
var obj = new Object();
obj.title = searchStrRegExp ;
var iter = tree.myDiagram.findNodesByExample(obj);
var node = iter.first();
if (node) {
  var parentKey = node.data.parent;
  var parent = tree.myDiagram.findNodeForKey(parentKey);
  if (parent) {
    parent.expandTree();
    // expand root node, if necessary.
    var root = tree.myDiagram.findNodeForKey('0');
    root.expandTree();
    tree.myDiagram.clearSelection();
    tree.myDiagram.select(node);
    // scroll tree to center on bounds of the selected node.
    var rect = node.actualBounds;
    if (rect) {
      console.log('actual bounds of selected node: ' + rect);
      tree.myDiagram.scrollToRect(rect);
      // sometimes scroll doesn't work. using setTimeout() did NOT help.
      ms = '2000';
      window.setTimeout(addressBook.scrollTreeToKey(node.data.key), ms);
    } else {
      alert('There was a problem scrolling to selected target. Please scroll down/up to locate it.');
    }

  scrollTreeToKey: function(key) {
var node = tree.myDiagram.findNodeForKey(key);
if (node) {
  var rect = node.actualBounds;
  tree.myDiagram.scrollToRect(rect);
} else {
  alert('scrollTreeToKey: invalid node');
}

},

I think you have the right idea – you do need to wait for expansion to happen, even if layout animation is disabled. But I think you need to expand from the root, not from the node’s parent node.

You can call Part.isVisible to see if a Node or a Link is “visible”.
You don’t need to call Diagram.clearSelection if you are calling Diagram.select.
The second arg to setTimeout should be a number, not a string.

BTW, CommandHandler.scrollToPart does this automatically in version 2.0. So in the future you might want to do something like:

    var node = . . .
    node.diagram.selectCollection(node.findTreeParentChain());
    node.diagram.commandHandler.scrollToPart(node);

I changed it to expand root first but it still only scrolls if the expansion didn’t happen. Do you think
there is something wrong with my Tree?
var node = iter.first();
if (node) {
var parentKey = node.data.parent;
var parent = tree.myDiagram.findNodeForKey(parentKey);
if (parent) {
// expand root node, if necessary.
if (!parent.isVisible()) {
var root = tree.myDiagram.findNodeForKey(‘0’);
root.expandTree();
}
// expand parent node, if necessary.
if (!node.isVisible()) {
parent.expandTree();
}
tree.myDiagram.select(node);
// scroll tree to center on bounds of the selected node.
var rect = node.actualBounds;
if (rect) {
console.log('actual bounds of selected node: ’ + rect);
tree.myDiagram.scrollToRect(rect);
// sometimes scroll doesn’t work. using setTimeout() did NOT help.
ms = 2000;
window.setTimeout(addressBook.scrollTreeToKey(node.data.key), ms);

here is the tree code:

  var tree = {
myDiagram: null,
nodeDataArray : null,
init:function () {
if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
var $ = go.GraphObject.make;  // for conciseness in defining templates

tree.myDiagram =
  $(go.Diagram, "treeDiv",
    {
      allowMove: false,
      allowCopy: false,
      allowDelete: false,
      allowHorizontalScroll: false,
      maxSelectionCount: 1,
      layout:
        $(go.TreeLayout,
          {
            alignment: go.TreeLayout.AlignmentStart,
            angle: 0,
            compaction: go.TreeLayout.CompactionNone,
            layerSpacing: 16,
            layerSpacingParentOverlap: 1,
            nodeIndent: 2,
            nodeIndentPastParent: 0.88,
            nodeSpacing: 0,
            setsPortSpot: false,
            setsChildPortSpot: false
          })
  });

tree.myDiagram.nodeTemplate =
  $(go.Node,
    { // no Adornment: instead change panel background color by binding to Node.isSelected
      selectionAdorned: false,
      isTreeExpanded: false,
      // a custom function to allow expanding/collapsing on double-click
      // this uses similar logic to a TreeExpanderButton
      doubleClick: function(e, node) {
        var cmd = tree.myDiagram.commandHandler;
        if (node.isTreeExpanded) {
          if (!cmd.canCollapseTree(node)) return;
        } else {
          if (!cmd.canExpandTree(node)) return;
        }
        e.handled = true;
        if (node.isTreeExpanded) {
          cmd.collapseTree(node);
        } else {
          cmd.expandTree(node);
        }
      }
    },
    $("TreeExpanderButton",
      {
        width: 14,
        "ButtonBorder.fill": "whitesmoke",
        "ButtonBorder.stroke": null,
        "_buttonFillOver": "rgba(0,128,255,0.25)",
        "_buttonStrokeOver": null
      }),
    $(go.Panel, "Horizontal",
      { position: new go.Point(16, 0) },
      new go.Binding("background", "isSelected", function (s) { return (s ? "lightblue" : "white"); }).ofObject(),
      $(go.Picture,
        {
          width: 18, height: 18,
          margin: new go.Margin(0, 4, 0, 0),
          imageStretch: go.GraphObject.Uniform
        },
        // bind the picture source on two properties of the Node
        // to display open folder, closed folder, or document
        new go.Binding("source", "isTreeExpanded", tree.imageConverter).ofObject(),
        new go.Binding("source", "isTreeLeaf", tree.imageConverter).ofObject()),
      $(go.TextBlock,
        { font: '9pt Verdana, sans-serif' },
        new go.Binding("text", "title", function(s) { return s; }))
    ),  // end Horizontal Panel
  {
    toolTip:  // define a tooltip for each node that displays the color as text
      $(go.Adornment, "Auto",
        $(go.Shape, { fill: "#FFFFCC" }),
        $(go.TextBlock, { margin: 4, maxSize: new go.Size(300,80), wrap: go.TextBlock.WrapFit },
          new go.Binding("text", "title", function(s) {return s;}))
      )  // end of Adornment
  }
  );  // end Node

// without lines
tree.myDiagram.linkTemplate = $(go.Link);

// // with lines
// myDiagram.linkTemplate =
//   $(go.Link,
//     { selectable: false,
//       routing: go.Link.Orthogonal,
//       fromEndSegmentLength: 4,
//       toEndSegmentLength: 4,
//       fromSpot: new go.Spot(0.001, 1, 7, 0),
//       toSpot: go.Spot.Left },
//     $(go.Shape,
//       { stroke: 'gray', strokeDashArray: [1,2] }));


if (false) {
  // create a random tree
  //tree.nodeDataArray = [{ key: 0 }];
  var max = 30;
  var count = 0;
  while (count < max) {
    count = tree.makeTree(3, count, max, tree.nodeDataArray, tree.nodeDataArray[0]);
  }
}
// tree.myDiagram.model = new go.TreeModel(nodeDataArray);
},

loadTreeArray: function(jsonObj) {
var arr = jsonObj.TargetsTree;
if (!arr) {
  alert('Cannot find array of tree data.');
  return false;
} else {
  if (tree.nodeDataArray) {
    tree.nodeDataArray.length = 0; // this clears the array.
  }
  tree.nodeDataArray = new Array();
  var i = 0;
  var len = arr.length;
  while (i < len) {
    var obj = arr[i];
    console.log('json: key: ' + obj.key + ' parent: ' + obj.parent + ' code: ' + obj.code   + ' title: ' + obj.title);
    tree.nodeDataArray.push(obj);
    i++;
  }
}
return true;
},
drawTree: function() {
tree.myDiagram.model = new go.TreeModel(tree.nodeDataArray);
},
selectedCode:function() {
// assumes only 1 selected.
// (tree.myDiagram.selection.each(function(part) {if (part instanceof go.Node) {return('selected ' +  
 part.data.key);}}));
// or tree.myDiagram.iterator.each(function(part) {if (part instanceof go.Node) {console.log('selected ' + 
part.data.key);}});
var it = tree.myDiagram.selection.iterator;
while (it.next()) {
  var part = it.value;
  var code = part.data.code;
  return code;
}
return null;
},
selectedTitle:function() {
// assumes only 1 selected.
// (tree.myDiagram.selection.each(function(part) {if (part instanceof go.Node) {return('selected ' + 
part.data.key);}}));
// or tree.myDiagram.iterator.each(function(part) {if (part instanceof go.Node) {console.log('selected ' + 
part.data.key);}});
var it = tree.myDiagram.selection.iterator;
while (it.next()) {
  var part = it.value;
  var ttl = part.data.title;
  return ttl;
}
return null;
},
numSelected: function() {
return tree.myDiagram.selection.iterator.count;
},
makeTree:function (level, count, max, nodeDataArray, parentdata) {
var numchildren = Math.floor(Math.random() * 10);
for (var i = 0; i < numchildren; i++) {
  if (count >= max) return count;
  count++;
  var childdata = { key: count, parent: parentdata.key , code: 'code for ' + count};
  nodeDataArray.push(childdata);
  if (level > 0 && Math.random() > 0.5) {
    count = tree.makeTree(level - 1, count, max, nodeDataArray, childdata);
  }
}
return count;
},

 // takes a property change on either isTreeLeaf or isTreeExpanded and selects the correct image to use
imageConverter:function (prop, picture) {
var node = picture.part;
if (node.isTreeLeaf) {
  return "/gojs/samples/images/document.png";
} else {
  if (node.isTreeExpanded) {
    return "/gojs/samples/images/openFolder.png";
  } else {
    return "/gojs/samples/images/closedFolder.png";
  }
}
}
};

You could try doing something like:

  var node = . . .
  node.diagram.commit(function(diag) {
    node.findTreeParentChain().each(function(n) {
        if (n instanceof go.Node && n !== node) n.expandTree();
    });
  }, "expanded tree");
  node.diagram.commandhandler.scrollToPart(node);

Thanks.
Do you mean ‘commitTransaction’? I got an error there is no commit method.
when I tried commitTransaction, nothing was expanded. the console says:
Ending transaction without having started a transaction: function(diag) {
node.findTreeParentChain().each(function(n) {
if (n instanceof go.Node && n !== node) n.expandTree();
});
}

Are you not using version 1.8? If not, I highly recommend that you get the latest.

no.I have 1.7.11.

thanks! I upgraded and your code works now. I don’t need the delay either. Is that safe?

Also, can you explain in a sentence what that commit() and commandhandler() are doing?
I read page about commandhandler and don’t see what it has to do with scrolling.
couldn’t find correct commit() page.

thanks for your timely help today!