Building Parts with GraphObjects

The following pages will discuss the basic kinds of objects you can use to build Parts. These pages build up a diagram by explicitly creating and adding nodes and links. Later pages will show how to build diagrams using models rather than adding Parts directly.

The Building Blocks of GoJS

GoJS Diagrams display top-level Parts and Part subclasses: Nodes, Links, and Groups.

Parts are Panels, which can hold any number of Shapes, Pictures, TextBlocks, or other, nested Panels. All together, these are subclasses of GraphObject.

Nodes and Links are usually composed of many GraphObjects, including several Panels that may be nested.

Building with Code

A very simple Node might consist of a Shape and a TextBlock. You could build such a visual tree of GraphObjects using code. The following code produces the following "live" diagram, so you can click on the node to select it and then drag it around.


  const node = new go.Node("Auto");
  const shape = new go.Shape();
  shape.figure = "RoundedRectangle";
  shape.fill = "lightblue";
  shape.strokeWidth = 3;
  node.add(shape);
  const textblock = new go.TextBlock();
  textblock.text = "Hello!";
  textblock.margin = 5;
  node.add(textblock);
  diagram.add(node);

Although building a node in this manner will work, as the nodes get more complicated the code will become more complicated to read and to maintain. Fortunately GoJS has a better way to make Parts out of GraphObjects.

Furthermore, later sections will discuss how Nodes and Links should be created automatically using models, templates, and data-binding. Until that time, these pages will create Nodes explicitly and add them to Diagrams directly.

Building with Chaining Functions

Starting in GoJS 2.2, many GraphObject and Diagram methods return the object instance, such as Panel.add and GraphObject.bind. These methods can be chained to build Parts.

Every GraphObject constructor optionally takes a common setting as its first argument. These are:

Every GraphObject additionally takes an optional constructor argument that is a JavaScript object, which can be used to set any object properties. For example, instead of writing:


const shape = new go.Shape();
shape.figure = "RoundedRectangle";
shape.fill = "lightblue";
shape.strokeWidth = 3;

We could write this:


const shape = new go.Shape("RoundedRectangle", {
    fill: "lightblue",
    strokeWidth: 3
  });

The first argument is the Shape.figure, the second argument is an object for setting other Shape properties. If you use TypeScript and the npm gojs package, this code will also be type-checked for you.

To rewrite the code block in the previous section, that had a Node with one Shape and one TextBlock added, we would write this:


// Create a node and .add a Shape and TextBlock to it
myDiagram.add(
  new go.Node("Auto").add(
    new go.Shape("RoundedRectangle",
        { fill: "lightblue", strokeWidth: 3 }),
    new go.TextBlock("Hello!",
        { margin: 5 })
  )
);

Unlike the GraphObject.make methodology below, this code is typed at compile time or in your IDE, if using TypeScript.

Building with GraphObject.make

Unlike building with .add, GraphObject.make is not well type-checked when using TypeScript definitions. However, it is more flexible, and many of our samples use it.

GoJS defines a static function, GraphObject.make, that is very useful in constructing GraphObjects without having to think of and keep track of temporary variable names. This static function also supports building objects in a nested fashion, where the indentation gives you a clue about depth in the visual tree, unlike the simple linear code shown initially on this page.

GraphObject.make is a function whose first argument must be a class type, typically a subclass of GraphObject.

Additional arguments to GraphObject.make may be of several types:

We can rewrite the code above with go.GraphObject.make to produce exactly the same results:


  const $ = go.GraphObject.make;
  diagram.add(
    $(go.Node, "Auto",
      $(go.Shape, "RoundedRectangle",
        { fill: "lightblue", strokeWidth: 3 }),
      $(go.TextBlock, "Hello!",
        { margin: 5 })
    ));

Notice how we set the Panel.type, Shape.figure, and TextBlock.text properties by just using the string value.

The use of $ as an abbreviation for go.GraphObject.make is so handy that we will assume its use from now on. Having the call to go.GraphObject.make be minimized into a single character helps remove clutter from the code and lets the indentation match the nesting of GraphObjects in the visual tree that is being constructed.

Some other JavaScript libraries automatically define "$" to be a handy-to-type function name, assuming that they are the only library that matters. But you cannot have the same symbol have two different meanings at the same time in the same scope. So you may want to choose to use a different short name, such as "$$" or "GO" when using GoJS. The GoJS documentation and samples make use of "$" because it makes the resulting code most clear and compact.

Another advantage of using GraphObject.make is that it will make sure at run-time that any properties that you set are defined properties on the class. If you have a typo in the name of the property, it will throw an error, for which you can see a message in the console log.

All of this initialization using GraphObject.make is still JavaScript code, so we can call functions and easily share objects such as brushes:


  const violetbrush = $(go.Brush, "Linear", { 0.0: "Violet", 1.0: "Lavender" });

  diagram.add(
    $(go.Node, "Auto",
      $(go.Shape, "RoundedRectangle", { fill: violetbrush }),
      $(go.TextBlock, "Hello!", { margin: 5 })
    ));

  diagram.add(
    new go.Node("Auto")
      .add(
        new go.Shape("Ellipse", { fill: violetbrush }),
        new go.TextBlock("Goodbye!", { margin: 5 })
      ));

Brushes and Geometry objects may be shared, but GraphObjects may not be shared.

The Diagram Constructor

The Diagram constructor uses Diagram.setProperties to process the initialization object to a Diagram constructor. You can use property names that are strings consisting of two identifiers separated by a period. The name before the period is used as the name of a property on the Diagram or on the Diagram.toolManager that returns an object whose property is to be set. The name after the period is the name of the property that is set. Note that because there is an embedded period, JavaScript property syntax requires that you use quotes.

You can also declare DiagramEvent listeners, as if calling Diagram.addDiagramListener, by pretending to set a Diagram property that is actually the name of a DiagramEvent. Because all DiagramEvents have names that are capitalized, the names will not conflict with any Diagram property names.


  const myDiagram = new go.Diagram("myDiagramDiv",  // must name or refer to the DIV HTML element
      {
        // don't initialize some properties until after a new model has been loaded
        "InitialLayoutCompleted": loadDiagramProperties,  // a DiagramEvent listener

        // have mouse wheel events zoom in and out instead of scroll up and down
        "toolManager.mouseWheelBehavior": go.WheelMode.Zoom,

        // specify a data object to copy for each new Node that is created by clicking
        "clickCreatingTool.archetypeNodeData": { text: "new node" }
      });

  // the DiagramEvent listener for "InitialLayoutCompleted"
  function loadDiagramProperties(e) { . . . }

Here is a diagram that includes comments about the GraphObjects used to build some example nodes and links, using a number of GoJS concepts:

The following pages will provide more details about the basic building block classes, TextBlock, Shape, and Picture, and about ways of aggregating them with the Panel class.