create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
874 lines (814 loc) • 32.9 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="../assets/css/style.css">
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta itemprop="name" content="Link Labels" />
<meta property="og:title" content="Link Labels" />
<meta name="twitter:title" content="Link Labels" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/fp/defaultCard.png" />
<meta property="og:url" content="https://gojs.net/latest/intro/linkLabels.html" />
<meta property="twitter:url" content="https://gojs.net/latest/intro/linkLabels.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Link Labels | GoJS
</title>
<link rel="stylesheet" href="../assets/css/prism.css"/>
</head>
<script>
window.diagrams = [];
window.goCode = function (pre, w, h, parentid, animation) {
window
.diagrams
.push([pre, w, h, parentid, animation]);
}
</script>
<body>
<nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200">
<div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4">
<a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo"
href="../">
GoJS
</a>
<div class="relative">
<button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div id="topnavList" class="hidden md:block">
<div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold
md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit">
<a href="../learn/">Learn</a>
<a href="../samples/">Samples</a>
<a href="../intro/">Intro</a>
<a href="../api/">API</a>
<a href="../download.html">Download</a>
<a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a>
<a id="tc" href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
<a id="tb" href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</div>
</div>
</div>
</div>
</nav>
<script>
window.addEventListener("DOMContentLoaded", function () {
// topnav
var topButton = document.getElementById("topnavButton");
var topnavList = document.getElementById("topnavList");
if (topButton && topnavList) {
topButton.addEventListener("click", function (e) {
topnavList
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) {
topButton.click();
}
});
// set active <a> element
var url = window
.location
.href
.toLowerCase();
var aTags = topnavList.getElementsByTagName('a');
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.endsWith('.html'))
lowerhref = lowerhref.slice(0, -5);
if (url.startsWith(lowerhref)) {
aTags[i]
.classList
.add('active');
break;
}
}
}
});
</script>
<div class="sticky top-0 left-0 z-10 px-2 w-full bg-white border-b border-b-gray-200 md:hidden">
<button id="sidenavButton" class="flex p-2 text-gray-900 bg-inherit shadow-none items-center text-sm font-semibold hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block mr-2" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Menu</span>
</button>
</div>
<script>
window.addEventListener("DOMContentLoaded", function () {
// sidenav
var sideButton = document.getElementById("sidenavButton");
var sidenav = document.getElementById("sidenav");
if (sideButton && sidenav) {
sideButton.addEventListener("click", function (e) {
sidenav
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!sidenav.classList.contains("hidden") && !e.target.closest("#sidenavList")) {
sideButton.click();
}
});
}
});
</script>
<div class="flex flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
<aside id="sidenav"
class="hidden fixed top-0 left-0 z-10 w-full bg-black/10 min-h-screen max-h-screen overflow-x-hidden overflow-y-auto shrink-0
md:block md:sticky md:w-52 md:min-h-0 md:bg-inherit md:border-r md:border-r-gray-200 md:overscroll-auto">
<nav id="sidenavList" class="flex flex-col bg-white w-52 min-h-screen pl-2 pt-4 pb-24 md:w-full md:min-h-0">
<a href="index.html">Basics</a>
<a href="buildingObjects.html">Building Parts</a>
<a href="usingModels.html">Using Models</a>
<a href="dataBinding.html">Data Binding</a>
<a href="react.html">GoJS with React</a>
<a href="svelte.html">GoJS with Svelte</a>
<a href="angular.html">GoJS with Angular</a>
<a href="textBlocks.html">TextBlocks</a>
<a href="shapes.html">Shapes</a>
<a href="pictures.html">Pictures</a>
<a href="panels.html">Panels</a>
<a href="tablePanels.html">Table Panels</a>
<a href="brush.html">Brushes</a>
<a href="sizing.html">Sizing Objects</a>
<a href="itemArrays.html">Item Arrays</a>
<a href="changedEvents.html">Changed Events</a>
<a href="transactions.html">Transactions</a>
<a href="viewport.html">Coordinates</a>
<a href="initialView.html">Initial View</a>
<a href="collections.html">Collections</a>
<a href="links.html">Links</a>
<a href="linkLabels.html">Link Labels</a>
<a href="connectionPoints.html">Link Points</a>
<a href="ports.html">Ports</a>
<a href="nodes.html">Nodes</a>
<a href="typings.html">Typings</a>
<a href="debugging.html">Debugging</a>
<a href="layouts.html">Layouts</a>
<a href="routers.html">Routers</a>
<a href="trees.html">Trees</a>
<a href="subtrees.html">SubTrees</a>
<a href="groups.html">Groups</a>
<a href="subgraphs.html">SubGraphs</a>
<a href="sizedGroups.html">Sized Groups</a>
<a href="selection.html">Selection</a>
<a href="highlighting.html">Highlighting</a>
<a href="theming.html">Theming</a>
<a href="tooltips.html">ToolTips</a>
<a href="contextMenus.html">Context Menus</a>
<a href="events.html">Diagram Events</a>
<a href="tools.html">Tools</a>
<a href="commands.html">Commands</a>
<a href="accessibility.html">Accessibility</a>
<a href="buttons.html">Buttons</a>
<a href="permissions.html">Permissions</a>
<a href="validation.html">Validation</a>
<a href="animation.html">Animation</a>
<a href="HTMLInteraction.html">HTML Interaction</a>
<a href="layers.html">Layers & Z-ordering</a>
<a href="palette.html">Palette</a>
<a href="overview.html">Overview</a>
<a href="replacingDeleting.html">Replacing and Deleting</a>
<a href="templateMaps.html">Template Maps</a>
<a href="legends.html">Legends and Titles</a>
<a href="extensions.html">Extensions</a>
<a href="geometry.html">Geometry Strings</a>
<a href="grids.html">Grid Patterns</a>
<a href="graduatedPanels.html">Graduated Panels</a>
<a href="SVGContext.html">Rendering to SVG</a>
<a href="makingSVG.html">Snapshot to SVG</a>
<a href="makingImages.html">Diagram Images</a>
<a href="printing.html">Printing</a>
<a href="serverSideImages.html">Server-side Images</a>
<a href="nodeScript.html">GoJS in Node.js</a>
<a href="testing.html">Testing</a>
<a href="performance.html">Performance</a>
<a href="platforms.html">Platforms</a>
<a href="deployment.html">Deployment</a>
</nav>
</aside>
<script>
var navList = document.getElementById('sidenavList');
if (navList !== null) {
var url = window.location.href;
var lindex = url.lastIndexOf('/');
url = url
.slice(lindex + 1)
.toLowerCase();
var aTags = navList.getElementsByTagName('a');
var currentindex = -1;
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.indexOf('/' + url) !== -1) {
currentindex = i;
aTags[i]
.classList
.add('active');
break;
}
}
}
</script>
<div class="px-4 pb-16 w-full overflow-hidden prose">
<h1>Labels on Links</h1>
<p>
It is common to add annotations or decorations on a link, particularly text.
</p>
<h2 id="SimpleLinkLabels">Simple Link labels</h2>
<p>
By default if you add a <a>GraphObject</a> to a <a>Link</a>, it will be positioned at the middle of the link.
In this example, we just add a <a>TextBlock</a> to the link and bind its <a>TextBlock.text</a> property to the link data's "text" property.
</p>
<pre class="lang-js" id="simple"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(), // this is the link shape (the line)
new go.Shape({ toArrow: "Standard" }), // this is an arrowhead
new go.TextBlock() // this is a Link label
.bind("text")
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta", text: "a label" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('simple', 600, 200);
</script>
<p>
Note that clicking on the text label results in selection of the whole Link.
</p>
<p>
Although it is commonplace to use a <a>TextBlock</a> as the link label, it can be any <a>GraphObject</a> such as a <a>Shape</a>
or an arbitrarily complex <a>Panel</a>.
Here is a simple Panel label:
</p>
<pre class="lang-js" id="labels"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.Panel("Auto") // this whole Panel is a link label
.add(
new go.Shape("TenPointedStar", { fill: "yellow", stroke: "gray" }),
new go.TextBlock({ margin: 3 })
.bind("text")
)
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta", text: "hello!" } // added information for link label
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('labels', 600, 200);
</script>
<p>
This also works if the link is orthogonally routed or bezier-curved.
</p>
<pre class="lang-js" id="simpleOrtho"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock( { margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link({ routing: go.Routing.Orthogonal })
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock({ textAlign: "center" }) // centered multi-line text
.bind("text", "text")
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta", text: "a label\non an\northo link" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('simpleOrtho', 600, 200);
</script>
<p>
Although positioning the label at the middle of the link is the default behavior, you can set <a>GraphObject</a> properties
that start with "segment" to specify exactly where and how to arrange the object along the route of the link.
</p>
<h2 id="LinkLabelSegmentIndexAndSegmentFraction">Link label segmentIndex and segmentFraction</h2>
<p>
Set the <a>GraphObject.segmentIndex</a> property in order to specify which segment of the link route the object should be on.
Set the <a>GraphObject.segmentFraction</a> property to control how far the object should be,
as a fraction from the start of the segment (zero) to the end of the segment (one).
</p>
<p>
When setting the <a>GraphObject.segmentIndex</a> property to NaN, the fraction will be calculated
along the entire link route instead of a particular segment.
</p>
<p>
In the case of a link that comes from a node with no <a>GraphObject.fromSpot</a> (i.e. <a>Spot.None</a>)
and goes to a node with no <a>GraphObject.toSpot</a>, there may be only one segment in the link, segment number zero.
</p>
<pre class="lang-js" id="fraction"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock("from", { segmentIndex: 0, segmentFraction: 0.2 }),
new go.TextBlock("mid", { segmentIndex: 0, segmentFraction: 0.5 }),
new go.TextBlock("to", { segmentIndex: 0, segmentFraction: 0.8 })
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('fraction', 600, 200);
</script>
<p>
In the case of a link that has many segments in it, you will want to specify different segment numbers.
Orthogonal links, for example, typically have 6 points in the route, which means five segments numbered from 0 to 4.
</p>
<pre class="lang-js" id="fractionOrtho"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link({ routing: go.Routing.Orthogonal })
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock("from", { segmentIndex: 1, segmentFraction: 0.5 }),
new go.TextBlock("mid", { segmentIndex: 2, segmentFraction: 0.5 }),
new go.TextBlock("to", { segmentIndex: 3, segmentFraction: 0.5 })
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('fractionOrtho', 600, 200);
</script>
<p>
However, you can also count segments backwards from the "to" end of the link. -1 is the last segment, -2 is the next to last, etc.
When you use a negative segment index, the segment fraction goes from 0 closest to the "to" end to 1
for the end of that segment that is farthest back along the route from the "to" end.
Thus a segmentIndex of -1 with a segmentFraction of 0 is the very end point of the link route.
A segmentIndex of -1 with a segmentFraction of 1 is the same point as segmentIndex -2 and segmentFraction 0.
</p>
<p>
For labels that belong near the "to" end of a link, you will normally use negative values for <a>GraphObject.segmentIndex</a>.
This convention works better when the number of segments in a link is unknown or may vary.
</p>
<p>
Lastly, one can specify a segmentIndex of NaN to have the fraction calculated along the entire link route instead of just a particular segment.
</p>
<pre class="lang-js" id="fractionNoIndex"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link({ curve: go.Curve.Bezier })
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock("1/3", { segmentIndex: NaN, segmentFraction: 0.33 }), // label at 1/3 of link length
new go.TextBlock("2/3", { segmentIndex: NaN, segmentFraction: 0.67 }) // label at 2/3 of link length
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('fractionNoIndex', 600, 200);
</script>
<h2 id="LinkLabelSegmentOffsetAndAlignmentFocus">Link label segmentOffset and alignmentFocus</h2>
<p>
There are two ways of making small adjustments to the position of a label object given a particular point on a link segment
specified by the segment index and fractional distance.
</p>
<p>
The <a>GraphObject.segmentOffset</a> property controls where to position the object relative to the point on a link segment
determined by the <a>GraphObject.segmentIndex</a> and <a>GraphObject.segmentFraction</a> properties.
The offset is not a simple offset of the point -- it is rotated according to the angle of that link segment.
A positive value for the Y offset moves the label element towards the right side of the link, as seen going in the
direction of the link.
Naturally a negative value for the Y offset moves it towards the left side.
</p>
<pre class="lang-js" id="offset"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock("left", { segmentOffset: new go.Point(0, -10) }),
new go.TextBlock("right", { segmentOffset: new go.Point(0, 10) })
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('offset', 600, 200);
</script>
<p>
If you drag one node around in a circle around the other one, you will see how the "left" and "right" labels are positioned.
</p>
<p>
Another way to change the effective offset is by changing the spot in the object that is being positioned relative to the link segment point.
You can do that by setting the <a>GraphObject.alignmentFocus</a>, which as you have seen above defaults to <a>Spot.Center</a>.
(<a>GraphObject.alignmentFocus</a> is also used by other <a>Panel</a> types, which is why its name does not start with "segment".)
</p>
<pre class="lang-js" id="alignmentFocus"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock("left", { alignmentFocus: new go.Spot(1, 0.5, 3, 0) }),
new go.TextBlock("right", { alignmentFocus: new go.Spot(0, 0.5, -3, 0) })
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('alignmentFocus', 600, 200);
</script>
<p>
Yet you may instead want to control the angle of the individual labels based on the angle of the link segment.
</p>
<h2 id="LinkLabelSegmentOrientation">Link label segmentOrientation</h2>
<p>
The <a>GraphObject.segmentOrientation</a> property controls the angle of the label object relative to the angle of the link segment.
There are several possible values that you can use.
The default orientation is <a>Orientation.None</a>, meaning no rotation at all.
<a>Orientation.Along</a> is commonly used to have the object always rotated at the same angle as the link segment.
<a>Orientation.Upright</a> is like "OrientAlong", but is often used when there is text in the label, to make it easier to read.
</p>
<pre class="lang-js" id="orient"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock("left",
{ segmentOffset: new go.Point(0, -10),
segmentOrientation: go.Orientation.Upright }),
new go.TextBlock("middle",
{ segmentOffset: new go.Point(0, 0),
segmentOrientation: go.Orientation.Upright }),
new go.TextBlock("right",
{ segmentOffset: new go.Point(0, 10),
segmentOrientation: go.Orientation.Upright })
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('orient', 600, 200);
</script>
<p>
Now if you move a node around you will always be able to read the label texts, and yet each label stays on its intended side of the link,
as seen going in the direction of the link.
</p>
<p>
This points out a difference between a segmentIndex/segmentFraction pair of 0/1 and 1/0.
Although they both refer to the same point, the angle associated with the first pair is the angle of the first segment (segment 0),
whereas the angle associated with the second pair is the angle of the second segment.
</p>
<h2 id="LinkLabelsNearEnds">Link labels near the ends</h2>
<p>
For labels that are near either end of a link, it may be convenient to set the <a>GraphObject.segmentOffset</a> to Point(NaN, NaN).
This causes the offset to be half the width and half the height of the label object.
</p>
<pre class="lang-js" id="nearEnds"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(),
new go.Shape({ toArrow: "Standard" }),
new go.TextBlock("from",
{ segmentIndex: 0, segmentOffset: new go.Point(NaN, NaN),
segmentOrientation: go.Orientation.Upright }),
new go.TextBlock("to",
{ segmentIndex: -1, segmentOffset: new go.Point(NaN, NaN),
segmentOrientation: go.Orientation.Upright })
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('nearEnds', 600, 200);
</script>
<h2 id="Arrowheads">Arrowheads</h2>
<p>
Now that you know more about the <a>GraphObject</a> "segment..." properties for controlling the position and angle of objects in a <a>Link</a>,
it is easy to explain how arrowheads are defined.
Arrowheads are just labels: <a>Shape</a>s that are initialized in a convenient manner.
</p>
<p>
You can see a copy of all of the built-in arrowhead definitions in this file: <a href="../extensions/Arrowheads.js">Arrowheads.js</a>.
</p>
<p>
Here are the equivalent settings for initializing an arrowhead <a>Shape</a> by setting <a>Shape.toArrow</a> to "Standard".
</p>
<pre class="lang-js" id="arrowheads"><code>
diagram.nodeTemplate =
new go.Node("Auto")
.bind("location", "loc", go.Point.parse)
.add(
new go.Shape("RoundedRectangle", { fill: "lightgray" }),
new go.TextBlock({ margin: 5 })
.bind("text", "key")
);
diagram.linkTemplate =
new go.Link()
.add(
new go.Shape(),
new go.Shape(
// the following are the same as { toArrow: "Standard" }:
{ segmentIndex: -1,
segmentOrientation: go.Orientation.Along,
alignmentFocus: go.Spot.Right,
geometry: go.Geometry.parse("F1 m0 0 l8 4 -8 4 2 -4 z") })
);
const nodeDataArray = [
{ key: "Alpha", loc: "0 0" },
{ key: "Beta", loc: "200 50" }
];
const linkDataArray = [
{ from: "Alpha", to: "Beta" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
</code></pre>
<script>
goCode('arrowheads', 600, 200);
</script>
</div>
</div>
<footer class="bg-white text-gray-900 border-t border-t-gray-200">
<div class="w-full max-w-screen-lg mx-auto px-4 py-6">
<p id="version" class="text-xs text-gray-900 m-0"></p>
<div class="text-sm px-0 mb-4 grid grid-cols-2 sm:grid-cols-3 gap-y-10">
<div>
<h2 class="text-base font-semibold text-nwoods-primary">GoJS</h2>
<ul class="list-none space-y-4 md:space-y-1 px-0">
<li>
<a href="../samples/index.html">Samples</a>
</li>
<li>
<a href="../learn/index.html">Learn</a>
</li>
<li>
<a href="../intro/index.html">Intro</a>
</li>
<li>
<a href="../api/index.html">API</a>
</li>
<li>
<a href="../changelog.html">Changelog</a>
</li>
<li>
<a href="https://github.com/NorthwoodsSoftware/GoJS" target="_blank" rel="noopener">GitHub</a>
</li>
</ul>
</div>
<div>
<h2 class="text-base font-semibold text-nwoods-primary">Support</h2>
<ul class="list-none space-y-4 md:space-y-1 px-0">
<li>
<a href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
</li>
<li>
<a href="https://forum.nwoods.com/c/gojs" target="_blank" rel="noopener">Forum</a>
</li>
<li>
<a href="https://nwoods.com/app/activate.aspx?sku=gojs" target="_blank" rel="noopener">Activate</a>
</li>
<li>
<a href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</li>
<li>
<a href="https://nwoods.com/register.html" target="_blank" rel="noopener">Register</a>
</li>
</ul>
</div>
<div>
<h2 class="text-base font-semibold text-nwoods-primary">Company</h2>
<ul class="list-none space-y-4 md:space-y-1 px-0">
<li>
<a target="_blank" href="https://nwoods.com" target="_blank" rel="noopener">Northwoods</a>
</li>
<li>
<a target="_blank" href="https://nwoods.com/about.html" target="_blank" rel="noopener">About Us</a>
</li>
<li>
<a target="_blank" href="https://nwoods.com/contact.html" target="_blank" rel="noopener">Contact Us</a>
</li>
<li>
<a target="_blank" href="https://nwoods.com/consulting.html" target="_blank" rel="noopener">Consulting</a>
</li>
<li>
<a target="_blank" href="https://twitter.com/northwoodsgo" target="_blank" rel="noopener">Twitter</a>
</li>
</ul>
</div>
</div>
<p class="text-sm text-gray-900 md:mb-6">
Copyright 1998-2025 <a href="https://nwoods.com">Northwoods Software</a>
</p>
</div>
</footer>
</body>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-S5QK8VSK84"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-S5QK8VSK84');
var getOutboundLink = function (url, label) {
gtag('event', 'click', {
'event_category': 'outbound',
'event_label': label,
'transport_type': 'beacon'
});
}
const params = new URL(document.location).searchParams
let a = params.get('a');
if (a) localStorage.setItem('a', a);
a = localStorage.getItem('a');
if (a) {
const links = [...document.body.getElementsByTagName("a")].filter((l) => l.href.includes('nwoods.com'));
for (const l of links) {
const url = new URL(l.href);
url.searchParams.set('a', a);
l.href = url;
}
}
</script>
<script src="../assets/js/prism.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script>
<script src="../extensions/Figures.js"></script>
<script src="../assets/js/goDoc.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
if (window.go)
document
.getElementById('version')
.textContent = "GoJS version " + go.version;
if (window.goDoc)
window.goDoc();
var d = window.diagrams;
for (var i = 0; i < d.length; i++) {
var dargs = d[i];
goCodeExecute(dargs[0], dargs[1], dargs[2], dargs[3], dargs[4]);
}
if (window.extra)
window.extra();
}
);
</script>
</html>