Thanks Walter.
The complete example below should illuminate my problem, and show why the data binding on the context menu button versus the actual node is behaving differently. The idea is that the button and the relevant context menu item display at the right time. Should be obvious if you open the example I have provided.
You will see I have two nodes, one node (“Business Partner”) is initialized (in the data model) with some initial documentation content. The context menu works fine for this node. The other node (“Cost Centre”) is not initialized with a documentation value. And the binding to display hide the context menu button does not behave properly for context menu button on this node. However the same data binding expression works fine, to show / hide the actual node button.
The version of the contextmenu binding using part.findObject (shown commented out) works fine. The problem appears to relate to the fact that for one of the nodes the documentation property is not initialized, but it seems to me that data binding on the node versus the contextmenu works differently.
One final question, when I remove height/width property for graphObject “textBlockDocumentation” (line 117), the node location jumps when I invoke the custom editor. Can you explain why because this object is hidden?
<!DOCTYPE html>
<html lang="en">
<head>
<title>ContextMenu Test</title>
<script src="https://unpkg.com/gojs/release/go-debug.js"></script>
<script>
function init() {
var $ = go.GraphObject.make;
var myDiagram = $(go.Diagram, "myDiagramDiv");
// custom editor #####################
var customEditor = new go.HTMLInfo(); // Create an HTMLInfo and dynamically create HTML to show/hide
var documentContainerDiv = document.getElementById("documentContainer");
customEditor.show = function (textBlock, diagram, tool) {
if (!(textBlock instanceof go.TextBlock)) return;
const buttonDoc = textBlock.part.findObject('buttonDocumentation'); // doesnt work for button panels
var loc = buttonDoc.getDocumentPoint(go.Spot.BottomLeft);
var pos = diagram.transformDocToView(loc);
documentContainerDiv.style.display = 'block';
documentContainerDiv.style.left = (pos.x) + "px";
documentContainerDiv.style.top = (pos.y + 20) + "px";
documentContainerDiv.style.display = 'block'; // Unhide div
var textContentDiv = document.getElementById("documentEditor")
textContentDiv.value = textBlock.text;
}
customEditor.hide = function (diagram, tool) {
documentContainerDiv.style.display = 'none';
}
customEditor.valueFunction = function () {
var textContentDiv = document.getElementById("documentEditor")
return textContentDiv.value;
}
// end of custom editor
// ####################################
function editDoc(e, obj) {
var nodeObj;
if (obj.name === "Add Comment")
{
nodeObj = myDiagram.findNodeForKey(obj.part.data.key); // if called from right mouse click, then add need to get node associated with the context
// Initialize comment
const docoTemplate =
'<h1>' + obj.part.data.key + '</h1>\n' +
'<h3>Definition:</h3>\n<p style=\"padding-left: 40px;\">enter definition here...</p>\n';
myDiagram.startTransaction("set init doc");
myDiagram.model.setDataProperty(obj.part.data, "documentation", docoTemplate);
myDiagram.commitTransaction("set init doc");
}
else
nodeObj = obj;
var textBlock = nodeObj.part.findObject('textBlockDocumentation');
if (myDiagram.commandHandler.canEditTextBlock(textBlock))
myDiagram.commandHandler.editTextBlock(textBlock);
}
function removeComment(e, obj) {
myDiagram.commit(function (d) {
d.model.setDataProperty(obj.part.data, "documentation", "");
}, "remove comment");
}
var contextMenuButtonArray = [];
// This menu item calls editDoc which opens up the editor to allow the user to start creating a comment for th enode
contextMenuButtonArray.push($("ContextMenuButton", { name: "Add Comment" }
, new go.Binding("visible", "documentation", function (v) { return v.length === 0; }) // THIS BINDING DOESN'T WORK PROPERLY
//, new go.Binding("visible", "key", function (v){ return !myDiagram.findNodeForKey(v).part.findObject('buttonDocumentation').visible; } )
, $(go.TextBlock, "Add Comment", { margin: 4 }), { click: editDoc })
);
// This menu item removes the comment
contextMenuButtonArray.push($("ContextMenuButton", { name: "Remove Comment" }
, new go.Binding("visible", "documentation", function (v) { return (v.length !== 0); }) // THIS BINDING DOESN'T WORK PROPERLY
//, new go.Binding("visible", "key", function (v){ return myDiagram.findNodeForKey(v).part.findObject('buttonDocumentation').visible; } )
, $(go.TextBlock, "Remove Comment", { margin: 4 }), { click: removeComment }));
var entityTemplate =
$(go.Node, "Auto", { resizable: true, minSize: new go.Size(100, 100) },
$(go.Shape, { figure: "Rectangle", strokeWidth: 2, stretch: go.GraphObject.Fill, fill: "lightgray" }),
$(go.TextBlock,
{
name: "textBlockTitle"
, cursor: "default"
, textAlign: "center"
, margin: 10
, minSize: new go.Size(100, 100)
, stretch: go.GraphObject.Fill
, font: "18px sans-serif"
, editable: true
}
, new go.Binding("text", "title").makeTwoWay()
),
$(go.TextBlock // Text block for the node documentation, this is always hidden because I have a custom editor
, {
name: "textBlockDocumentation"
, height: 500, width: 200 // this line should be irrelevant but if I remove, the location changes when the editor is invoked
, textEditor: customEditor
, visible: false
}
, new go.Binding("text", "documentation").makeTwoWay()
),
$("Button"
, {
name: "buttonDocumentation"
, alignment: new go.Spot(0, 1, 6, -6)
, alignmentFocus: go.Spot.BottomLeft
, "ButtonBorder.fill": "transparent"
, "ButtonBorder.strokeWidth": 0
, "ButtonBorder.margin": 0
, click: editDoc
, visible: false
}
, new go.Binding("visible", "documentation", function (v) { return v.length > 0; })
, $(go.Shape, // the button picture
{
width: 16, height: 22, strokeWidth: 2
, geometryString: "M0,0 h14 L22,8 v20 h-22 z M5,14 h11.5 M5,18 h11.5 M5,22 h11.5 M14,0 v8h8"
}
)
)
, { contextMenu: $("ContextMenu", contextMenuButtonArray) }
);
myDiagram.nodeTemplate = entityTemplate;
var nodeDataArray = [
{ "key": "Business Partner", "title": "Business Partner", "documentation": "<h1>Business Partner</h1>\n<h3>Definition:</h3>\n<p style=\"padding-left: 40px;\">enter definition here...</p>" },
{ "key": "Cost Centre", "title": "Cost Centre" }
];
myDiagram.model = new go.GraphLinksModel(nodeDataArray, []);
}
window.addEventListener('DOMContentLoaded', init);
</script>
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px blue; width:1000px; height:1000px"></div>
<div id="documentContainer" style="position: absolute; display: none; z-index: 1000;">
<textarea id="documentEditor" style="height: 300px; width:500px"></textarea>
</div>
</body>
</html>