We have created a design based very closely on your flowchart sample.The flowchart sample DOES have the behavior that I want.
I am having a couple small problems that I can't seem to resolve.
Problem 1: When link is added, the link is redrawn with different ports. Very annoying.
Problem 2: The ports on the Condition node (Diamond shape) appear inside the shape, not on the edges.
Below is the JS code from my page:
function init() { // init for these samples -- you don't need to call this
var $ = go.GraphObject.make; // for conciseness in defining templates
//IsReadOnly = parentWindow.IsReadOnly;
switch (qsMode){
case 0:
IsReadOnly = true;
IsDisabled = true;
break;
case 1:
IsReadOnly = true;
IsDisabled = false;
break;
case 2:
IsReadOnly = false;
IsDisabled = false;
break;
default:
break;
}
if ((hdnGoJSData.value != "")) {
(jQuery)("#mySavedModel").val(hdnGoJSData.value);
}
goDiagram =
$(go.Diagram, "myDiagram", // must name or refer to the DIV HTML element
{
initialContentAlignment : go.Spot.TopCenter,
allowDrop: true, // must be true to accept drops from the Palette
// isReadOnly: IsReadOnly,
"LinkDrawn": showLinkLabel, // this DiagramEvent listener is defined below
"LinkRelinked": showLinkLabel,
"undoManager.isEnabled": true // enable undo & redo
});
//Call the ajax to redraw the runtime diagram from the server values on initial load
goDiagram.addDiagramListener("InitialLayoutCompleted", function (ev){
var itmestatusjson = document.getElementById("<%= WorkflowItemStatus.ClientID %>");
SetWorkflowItemState(itmestatusjson.value);
});
goDiagram.addDiagramListener("ObjectContextClicked", function (ev) {
if (qsMode != 1) {
//set popup title, ev.subject.le is inner text
var selectedNode = goDiagram.selection.iterator.first();
var selectedNodeText = selectedNode.Qe.text;
if (selectedNodeText === 'undefined') {
return false;
}
// Only allow action items and decision items to show a popup in design mode
var selectedNodeCategory = selectedNode.Qe.category;
if (selectedNodeCategory === 'Start' || selectedNodeCategory === 'End' || selectedNodeCategory === 'Link' || selectedNodeCategory === 'Comment') {
return false;
}
// Set the title text (for both design and runtime popups)
(jQuery)("#lblNodeText").text(selectedNodeText);
(jQuery)("#lblNodeText2").text(selectedNodeText);
(jQuery)("#lblConditionNodeText").text(selectedNodeText);
(jQuery)("#lblComplianceNodeText").text(selectedNodeText);
if (selectedNodeCategory) {
OnSingleClick(selectedNodeCategory);
}
}
return false;
});
goDiagram.addDiagramListener("ObjectSingleClicked", function (ev) {
if (qsMode == 1) {
//set popup title, ev.subject.le is inner text
var selectedNode = goDiagram.selection.iterator.first();
var selectedNodeText = selectedNode.Qe.text;
if (selectedNodeText === 'undefined') {
return false;
}
// Only allow action items and decision items to show a popup in design mode
var selectedNodeCategory = selectedNode.Qe.category;
if (selectedNodeCategory === 'Start' || selectedNodeCategory === 'End' || selectedNodeCategory === 'Link' || selectedNodeCategory === 'Comment') {
return false;
}
// Set the title text (for both design and runtime popups)
(jQuery)("#lblNodeText").text(selectedNodeText);
(jQuery)("#lblNodeText2").text(selectedNodeText);
(jQuery)("#lblConditionNodeText").text(selectedNodeText);
(jQuery)("#lblComplianceNodeText").text(selectedNodeText);
var selectedNodeKey = selectedNode.Qe.key;
if (selectedNodeCategory) {
var statusItem = null;
for (var k = 0; k < itemsarray.Items.Item.length; k++) {
if (itemsarray.Items.Item[k].Key == selectedNodeKey) {
statusItem = itemsarray.Items.Item[k];
break;
}
}
if (statusItem.IsDisabled != "1") {
OnSingleClick(selectedNodeCategory);
}
}
}
return false;
});
function nodeStyle() {
return [
// The Node.location comes from the "loc" property of the node data,
// converted by the Point.parse static method.
// If the Node.location is changed, it updates the "loc" property of the node data,
// converting back using the Point.stringify static method.
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
{
// the Node.location is at the center of each node
locationSpot: go.Spot.Center,
//isShadowed: true,
//shadowColor: "#888",
// handle mouse enter/leave events to show/hide the ports
mouseEnter: function (e, obj) { showPorts(obj.part, true); },
mouseLeave: function (e, obj) { showPorts(obj.part, false); },
}
];
}
// Define a function for creating a "port" that is normally transparent.
// The "name" is used as the GraphObject.portId, the "spot" is used to control how links connect
// and where the port is positioned on the node, and the boolean "output" and "input" arguments
// control whether the user can draw links from or to the port.
function makePort(name, spot, output, input) {
// the port is basically just a small circle that has a white stroke when it is made visible
return $(go.Shape, "Circle",
{
fill: "transparent",
stroke: null, // this is changed to "white" in the showPorts function
desiredSize: new go.Size(8, 8),
alignment: spot, alignmentFocus: spot, // align the port on the main Shape
portId: name, // declare this object to be a "port"
fromSpot: spot, toSpot: spot, // declare where links may connect at this port
fromLinkable: output, toLinkable: input, // declare whether the user may draw links to/from here
cursor: "pointer" // show a different cursor to indicate potential link point
});
}
// define the Node templates for regular nodes
var lightText = 'whitesmoke';
goDiagram.nodeTemplateMap.add("", // the default category
$(go.Node, "Spot", nodeStyle(),
// the main object is a Panel that surrounds a TextBlock with a rectangular Shape
$(go.Panel, "Auto",
$(go.Shape, "Rectangle",
{ fill: "#00A9C9", stroke: null},
new go.Binding("figure", "figure"),
new go.Binding("fill","fill").makeTwoWay()),
$(go.TextBlock,
{ name: "TEXTBLOCK",
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: lightText,
margin: 8,
maxSize: new go.Size(160, NaN),
wrap: go.TextBlock.WrapFit,
editable: true
},
new go.Binding("text", "text").makeTwoWay())
),
// four named ports, one on each side:
makePort("T", go.Spot.Top, false, true),
makePort("L", go.Spot.Left, true, true),
makePort("R", go.Spot.Right, true, true),
makePort("B", go.Spot.Bottom, true, false)
));
goDiagram.nodeTemplateMap.add("Start",
$(go.Node, "Spot", nodeStyle(),
$(go.Panel, "Auto",
$(go.Shape, "Circle",
{ minSize: new go.Size(40, 60), fill: "#79C900", stroke: null}),
$(go.TextBlock, "Start",
{ margin: 5, font: "bold 11pt Helvetica, Arial, sans-serif", stroke: lightText },
new go.Binding("text", "text").makeTwoWay())
),
// three named ports, one on each side except the top, all output only:
makePort("L", go.Spot.Left, true, false),
makePort("R", go.Spot.Right, true, false),
makePort("B", go.Spot.Bottom, true, false)
));
goDiagram.nodeTemplateMap.add("End",
$(go.Node, "Spot", nodeStyle(),
$(go.Panel, "Auto",
$(go.Shape, "Circle",
{ minSize: new go.Size(40, 60), fill: "#DC3C00", stroke: null} ),
$(go.TextBlock, "End",
{ margin: 5, font: "bold 11pt Helvetica, Arial, sans-serif", stroke: lightText }, new go.Binding("text", "text").makeTwoWay())
),
// three named ports, one on each side except the bottom, all input only:
makePort("T", go.Spot.Top, false, true),
makePort("L", go.Spot.Left, false, true),
makePort("R", go.Spot.Right, false, true)
));
goDiagram.nodeTemplateMap.add("Action",
$(go.Node, "Auto", nodeStyle(),
$(go.Shape, "File",
{ fill: "#EFFAB4", stroke: null},
new go.Binding("fill","fill").makeTwoWay()),
$(go.TextBlock,
{ name: "TEXTBLOCK",
margin: 5,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: '#454545'
},
new go.Binding("text", "text").makeTwoWay()),
makePort("T", go.Spot.Top, false, true),
makePort("L", go.Spot.Left, true, false),
makePort("R", go.Spot.Right, true, false),
makePort("B", go.Spot.Bottom, true, false)
));
goDiagram.nodeTemplateMap.add("Condition",
$(go.Node, "Auto", nodeStyle(),
$(go.Shape, "Diamond",
{ fill: "#70706F", stroke: null},
new go.Binding("fill","fill").makeTwoWay()),
$(go.TextBlock,
{ name: "TEXTBLOCK",
margin: 5,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: 'whitesmoke'
},
new go.Binding("text", "text").makeTwoWay()),
makePort("T", go.Spot.Top, false, true),
makePort("L", go.Spot.Left, true, true),
makePort("R", go.Spot.Right, true, true),
makePort("B", go.Spot.Bottom, true, false)
));
goDiagram.nodeTemplateMap.add("Compliance",
$(go.Node, "Auto", nodeStyle(),
$(go.Shape, "ManualOperation",
{ fill: "#009900", stroke: null},
new go.Binding("fill","fill").makeTwoWay()),
$(go.TextBlock,
{ name: "TEXTBLOCK",
margin: 5,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: 'whitesmoke'
},
new go.Binding("text", "text").makeTwoWay()),
makePort("T", go.Spot.Top, true, true),
makePort("B", go.Spot.Bottom, true, true) //Two ports, only required
));
goDiagram.nodeTemplateMap.add("Comment",
$(go.Node, "Auto", nodeStyle(),
$(go.Shape, "File",
{ fill: "#EFF28E", stroke: null }),
$(go.TextBlock,
{ name: "TEXTBLOCK",
margin: 5,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: '#454545'
},
new go.Binding("text", "text").makeTwoWay())
// no ports, because no links are allowed to connect with a comment
));
// replace the default Link template in the linkTemplateMap
goDiagram.linkTemplate =
$(go.Link, // the whole link panel
{
routing: go.Link.Orthogonal,
curve: go.Link.JumpOver,
corner: 5,
toShortLength: 4,
relinkableFrom: true,
relinkableTo: true,
//reshapable: true,
mouseEnter: function(e, link) { link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)"; },
mouseLeave: function(e, link) { link.findObject("HIGHLIGHT").stroke = "transparent"; }
},
new go.Binding("points").makeTwoWay(),
$(go.Shape, // the highlight shape, normally transparent
{ isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT" }),
$(go.Shape, // the link path shape
{ isPanelMain: true, stroke: "gray", strokeWidth: 2, name: "LINK" },
new go.Binding("stroke", "stroke").makeTwoWay()),
$(go.Shape, // the arrowhead
{ toArrow: "standard", stroke: null, fill: "gray" }),
$(go.Panel, "Auto", // the link label, normally not visible
{ visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5 },
new go.Binding("visible", "visible").makeTwoWay(),
$(go.Shape, "RoundedRectangle", // the label shape
{ fill: "#F8F8F8", stroke: null }),
$(go.TextBlock, "Enter Choice", // the label
{
name: "TEXTBLOCK",
textAlign: "center",
font: "10pt helvetica, arial, sans-serif",
stroke: "#333333",
editable: true,
},
new go.Binding("text", "text").makeTwoWay(),
new go.Binding("stroke", "stroke").makeTwoWay())
)
);
// Make link labels visible if coming out of a "conditional" node.
// This listener is called by the "LinkDrawn" and "LinkRelinked" DiagramEvents.
function showLinkLabel(e) {
var label = e.subject.findObject("LABEL");
if (label !== null) {
label.visible = (e.subject.fromNode.data.category === "Condition");
}
}
// temporary links used by LinkingTool and RelinkingTool are also orthogonal:
goDiagram.toolManager.linkingTool.temporaryLink.routing = go.Link.Orthogonal;
goDiagram.toolManager.relinkingTool.temporaryLink.routing = go.Link.Orthogonal;
load(); // load an initial diagram from some JSON text
// initialize the Palette that is on the left side of the page
myPalette =
$(go.Palette, "myPalette", // must name or refer to the DIV HTML element
{
nodeTemplateMap: goDiagram.nodeTemplateMap, // share the templates used by goDiagram
model: new go.GraphLinksModel([ // specify the contents of the Palette
{ category: "Start", text: "<%= GetLocalResourceObject("gojsStartTagResource1")%>" },
{ category: "ActionNode", text: "<%=GetLocalResourceObject("gojsActionTagResource1")%>", color: "#1E90FF" },
{ category: "Condition", text: "<%=GetLocalResourceObject("gojsConditionTagResource1")%>", figure: "Diamond" },
{ category: "Comment", text: "<%= GetLocalResourceObject("gojsCommentTagResource1")%>", figure: "RoundedRectangle" },
{ category: "Compliance", text: "<%= GetLocalResourceObject("gojsComplianceResource1")%>", figure: "ManualEntry" },
{ category: "End", text: "<%= GetLocalResourceObject("gojsEndTagResource1")%>" }
])
});
} // init END
// Make all ports on a node visible when the mouse is over the node
function showPorts(node, show) {
var diagram = node.diagram;
if (!diagram || diagram.isReadOnly || !diagram.allowLink) return;
node.ports.each(function (port) {
port.stroke = (show ? "white" : null);
});
}
// Show the diagram's model in JSON format that the user may edit
function load() {
goDiagram.model = go.Model.fromJson((jQuery)("#mySavedModel").val());
if (IsDisabled) // thsi doesn't appear to be working with goJS
{
goDiagram.isDisabled = true;
}
goDiagram.isReadOnly = IsReadOnly;
}