Search code examples
javascriptgojs

GoJs Is it possible to have multipe layouts in one diagram


I want to have a diagram which has vertical and horizontal layout in the same diagram. How can I achieve this in gojs?

enter image description here


Solution

  • There are many ways in which you can achieve your goal. I can think of three off-hand. In decreasing order of work:

    • Create a custom Layout that does what you want. This is the most general and can be the most compatible with the data structures that you have used with your two existing diagrams that you want to combine.
    • In your case you might be able to use the TableLayout that is in the extensions directory: http://gojs.net/latest/extensions/Table.html. You probably could continue to use Groups, but you would set their Group.layout to null so that they are completely ignored when the layout is performed.
    • Put everything in one of your existing diagrams into a Group and put everything from your other existing diagram into another Group. The Diagram.layout of the first would be the Group.layout of the first group, and the Diagram.layout of the second diagram would be the Group.layout of the second group.

    Note that each Diagram can have exactly one Model (the Diagram.model), so for all three techniques you would need to add all of the data into a single model without getting references between them confused. That means you need to make sure the keys for the nodes are all unique.

    Here's an example of how you can put two separate diagrams into a single diagram using the third technique. I'll start with two copies of the Minimal sample, http://gojs.net/latest/samples/minimal.html, where the only change is that one has a ForceDirectedLayout and the other one has a LayeredDigraphLayout. So one will be defined:

    myDiagram = $(go.Diagram, "myDiagramDiv",
                  {
                    initialContentAlignment: go.Spot.Center,
                    layout: $(go.ForceDirectedLayout),
                    "undoManager.isEnabled": true
                  });
    

    and the other will be defined:

    myDiagram = $(go.Diagram, "myDiagramDiv",
                  {
                    initialContentAlignment: go.Spot.Center,
                    layout: $(go.LayeredDigraphLayout),
                    "undoManager.isEnabled": true
                  });
    

    But otherwise these two diagrams are exactly like the Minimal sample.

    Initially each model of Minimal is created by:

    myDiagram.model = new go.GraphLinksModel(
    [
      { key: "Alpha", color: "lightblue" },
      { key: "Beta", color: "orange" },
      { key: "Gamma", color: "lightgreen" },
      { key: "Delta", color: "pink" }
    ],
    [
      { from: "Alpha", to: "Beta" },
      { from: "Alpha", to: "Gamma" },
      { from: "Beta", to: "Beta" },
      { from: "Gamma", to: "Delta" },
      { from: "Delta", to: "Alpha" }
    ]);
    

    So, we first need to create a combined model that is the two models added together. One way to put them together is:

    myDiagram.model = new go.GraphLinksModel(
    [
      { key: "Alpha", color: "lightblue" },
      { key: "Beta", color: "orange" },
      { key: "Gamma", color: "lightgreen" },
      { key: "Delta", color: "pink" },
      { key: "Alpha2", color: "lightblue" },
      { key: "Beta2", color: "orange" },
      { key: "Gamma2", color: "lightgreen" },
      { key: "Delta2", color: "pink" }
    ],
    [
      { from: "Alpha", to: "Beta" },
      { from: "Alpha", to: "Gamma" },
      { from: "Beta", to: "Beta" },
      { from: "Gamma", to: "Delta" },
      { from: "Delta", to: "Alpha" },
      { from: "Alpha2", to: "Beta2" },
      { from: "Alpha2", to: "Gamma2" },
      { from: "Beta2", to: "Beta2" },
      { from: "Gamma2", to: "Delta2" },
      { from: "Delta2", to: "Alpha2" }
    ]);
    

    Again, I'll mention that this is work you would need to do no matter which technique you used. Presumably you have already done this and are just wondering how to handle two layouts.

    The third technique I suggested uses Groups to encapsulate what had originally been in a whole Diagram. So let us add two groups to the model and assign each of the original nodes to the corresponding group:

    myDiagram.model = new go.GraphLinksModel(
    [
      { key: "FD", isGroup: true, category: "FD" },  // NEW
      { key: "LD", isGroup: true, category: "LD" },  // NEW
      { key: "Alpha", color: "lightblue", group: "FD" },
      { key: "Beta", color: "orange", group: "FD" },
      { key: "Gamma", color: "lightgreen", group: "FD" },
      { key: "Delta", color: "pink", group: "FD" },
      { key: "Alpha2", color: "lightblue", group: "LD" },
      { key: "Beta2", color: "orange", group: "LD" },
      { key: "Gamma2", color: "lightgreen", group: "LD" },
      { key: "Delta2", color: "pink", group: "LD" }
    ],
    [
      { from: "Alpha", to: "Beta" },
      { from: "Alpha", to: "Gamma" },
      { from: "Beta", to: "Beta" },
      { from: "Gamma", to: "Delta" },
      { from: "Delta", to: "Alpha" },
      { from: "Alpha2", to: "Beta2" },
      { from: "Alpha2", to: "Gamma2" },
      { from: "Beta2", to: "Beta2" },
      { from: "Gamma2", to: "Delta2" },
      { from: "Delta2", to: "Alpha2" }
    ]);
    

    Now we just need to define each group/category/template:

    myDiagram.groupTemplateMap.add("FD",
      $(go.Group, "Auto",
        { layout: $(go.ForceDirectedLayout) },
        $(go.Shape, { fill: "white", stroke: "lightgray" }),
        $(go.Placeholder, { padding: 10 })
      ));
    
    myDiagram.groupTemplateMap.add("LD",
      $(go.Group, "Auto",
        { layout: $(go.LayeredDigraphLayout) },
        $(go.Shape, { fill: "white", stroke: "lightgray" }),
        $(go.Placeholder, { padding: 10 })
      ));
    

    For the purposes of this demonstration the details of the visual appearance of each kind of group does not matter, just as the details of the appearances of the nodes and links do not matter. What does matter to you is that one group template uses one layout and the other uses a different one, there are two group data objects, and all the node data is assigned to the appropriate group.

    In this case each group template is being used as a singleton, but perhaps your requirements will result in using more than one of any or all of the group templates.

    Now you just need to specify the Diagram.layout to control how the two groups are arranged relative to each other. Perhaps the simplest would be to use a GridLayout:

    myDiagram = $(go.Diagram, "myDiagramDiv",
                  {
                    initialContentAlignment: go.Spot.Center,
                    layout: $(go.GridLayout, { wrappingColumn: 1 }),
                    "undoManager.isEnabled": true
                  });
    

    You can of course customize the layout in whatever manner you need, including using a completely different or custom layout.

    Here's the complete code. For brevity I have removed a bunch of comments from the original Minimal sample:

    <!DOCTYPE html>
    <html>
    <head>
    <title>Combining 2 Diagrams with Different Layouts</title>
    <!-- Copyright 1998-2016 by Northwoods Software Corporation. -->
    <meta charset="UTF-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gojs/1.6.5/go.js"></script>
    <script id="code">
      function init() {
        var $ = go.GraphObject.make;  // for conciseness in defining templates
    
        myDiagram = $(go.Diagram, "myDiagramDiv",
                      {
                        initialContentAlignment: go.Spot.Center,
                        layout: $(go.GridLayout, { wrappingColumn: 1 }),
                        "undoManager.isEnabled": true
                      });
    
        myDiagram.nodeTemplate =
          $(go.Node, "Auto",
            $(go.Shape, "RoundedRectangle",
              new go.Binding("fill", "color")),
            $(go.TextBlock, { margin: 3 },
              new go.Binding("text", "key"))
          );
    
        myDiagram.groupTemplateMap.add("FD",
          $(go.Group, "Auto",
            { layout: $(go.ForceDirectedLayout) },
            $(go.Shape, { fill: "white", stroke: "lightgray" }),
            $(go.Placeholder, { padding: 10 })
          ));
    
        myDiagram.groupTemplateMap.add("LD",
          $(go.Group, "Auto",
            { layout: $(go.LayeredDigraphLayout) },
            $(go.Shape, { fill: "white", stroke: "lightgray" }),
            $(go.Placeholder, { padding: 10 })
          ));
    
        myDiagram.model = new go.GraphLinksModel(
        [
          { key: "FD", isGroup: true, category: "FD" },
          { key: "LD", isGroup: true, category: "LD" },
          { key: "Alpha", color: "lightblue", group: "FD" },
          { key: "Beta", color: "orange", group: "FD" },
          { key: "Gamma", color: "lightgreen", group: "FD" },
          { key: "Delta", color: "pink", group: "FD" },
          { key: "Alpha2", color: "lightblue", group: "LD" },
          { key: "Beta2", color: "orange", group: "LD" },
          { key: "Gamma2", color: "lightgreen", group: "LD" },
          { key: "Delta2", color: "pink", group: "LD" }
        ],
        [
          { from: "Alpha", to: "Beta" },
          { from: "Alpha", to: "Gamma" },
          { from: "Beta", to: "Beta" },
          { from: "Gamma", to: "Delta" },
          { from: "Delta", to: "Alpha" },
          { from: "Alpha2", to: "Beta2" },
          { from: "Alpha2", to: "Gamma2" },
          { from: "Beta2", to: "Beta2" },
          { from: "Gamma2", to: "Delta2" },
          { from: "Delta2", to: "Alpha2" }
        ]);
      }
    </script>
    </head>
    <body onload="init();">
    <div id="sample">
      <div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:600px"></div>
    </div>
    </body>
    </html>