Search code examples
javascriptmappinglinerelationshipdiagram

How to draw a multi stack, many to many relationship diagram, something like in the attached picture, using JavaScript?


enter image description here

I want to draw a relationship diagram (something like in the above picture) using JavaScript. I have tried many js libraries like D3, GoJs, JsPlumb, etc, but nothing has worked out for my requirement. Please find below the additional requirements.

  1. The boxes (e.g Business Architecture > Process > Resolve Complaint) may have up to four inner levels.
  2. Any box can be connected (internal - with-in the group and external - with the nodes in the other groups) with any box(s)
  3. Relationship can be uni and bi directional (one side arrow and double side arrow) and should have relationship type (e.g process).
  4. Every box (in all levels) should have four clickable hot-spots
  5. Can be saved as template for future modification
  6. Data feed is JSON

Solution

  • This solution is created in GoJS.

    Here's the result. Sorry, I didn't feel up to entering all of the data from your screenshot, so I only did the top two groups. And I didn't bother styling stuff nicely, either. nested relationships drawn using GoJS

    And here's the complete page, including the diagram's model data in JSON format:

    <!DOCTYPE html>
    <html>
    <head>
    <title>Simple Nested Relationships in GoJS</title>
    <!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
    <meta charset="UTF-8">
    <script src="go.js"></script>
    <script id="code">
      function init() {
        var $ = go.GraphObject.make;
    
        myDiagram =
          $(go.Diagram, "myDiagramDiv",
            {
              initialContentAlignment: go.Spot.Center,
              "undoManager.isEnabled": true
            });
    
        function makePort(id, spot) {
          return $(go.Shape,
            {
              fill: "transparent", strokeWidth: 0,
              width: 10, height: 10,
              alignment: spot, alignmentFocus: spot,
              portId: id,
              fromLinkable: true, toLinkable: true, cursor: "pointer"
            });
        }
    
        myDiagram.nodeTemplate =
          $(go.Node, "Spot",
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            $(go.Panel, "Auto",
              $(go.Shape, "RoundedRectangle",
                { fill: "white" },
                new go.Binding("fill", "color"),
                new go.Binding("figure"),
                new go.Binding("angle", "figure", function(f) { return f === "Hexagon" ? 90 : 0; })),
              $(go.TextBlock,
                { margin: 4, editable: true, maxSize: new go.Size(150, NaN), textAlign: "center" },
                new go.Binding("text").makeTwoWay())
            ),
            makePort("T", go.Spot.Top),
            makePort("R", go.Spot.Right),
            makePort("B", go.Spot.Bottom),
            makePort("L", go.Spot.Left)
          );
    
        myDiagram.groupTemplate =
          $(go.Group, "Spot",
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            $(go.Panel, "Auto",
              $(go.Shape,
                { fill: "whitesmoke" },
                new go.Binding("fill", "color"),
                new go.Binding("stroke", "color", go.Brush.darken)),
              $(go.Panel, "Vertical",
                $(go.TextBlock,
                  { margin: 4, editable: true },
                  new go.Binding("text").makeTwoWay()),
                $(go.Placeholder, { padding: 10 })
              )
            ),
            makePort("T", go.Spot.Top),
            makePort("R", go.Spot.Right),
            makePort("B", go.Spot.Bottom),
            makePort("L", go.Spot.Left)
          );
    
        myDiagram.linkTemplate =
          $(go.Link,
            $(go.Shape, { strokeWidth: 2 }),
            $(go.Shape, { fromArrow: "Standard", visible: false },
              new go.Binding("visible", "from")),
            $(go.Shape, { toArrow: "Standard" }),
            $(go.TextBlock,
              { font: "11px sans-serif", editable: true },
              new go.Binding("text").makeTwoWay())
          );
    
        myDiagram.model = go.Model.fromJson(
          '{ "class": "go.GraphLinksModel",\
            "linkFromPortIdProperty": "fp",\
            "linkToPortIdProperty": "tp",\
            "nodeDataArray": [\
              { "key": 1, "text": "Strategic Planning", "color": "lightgreen", "isGroup": true, "loc": "10.5 22" },\
              { "key": 11, "text": "Business Policy", "color": "rgba(182, 255, 180, 1)", "isGroup": true, "group": 1, "loc": "21 116" },\
              { "key": 111, "text": "Adhere to FSA Guidelines", "color": "lightgreen", "figure": "Hexagon", "group": 11, "loc": "31 126" },\
              { "key": 12, "text": "Strategy", "color": "rgba(182, 255, 180, 1)", "isGroup": true, "group": 1, "loc": "162 55" },\
              { "key": 121, "text": "Adopt Customer\\nComplaints Process", "color": "lightskyblue", "group": 12, "loc": "172 65" },\
              { "key": 122, "text": "Mission and\\nVision", "color": "plum", "group": 12, "loc": "274 136" },\
              { "key": 123, "text": "Improve Customer Satisfaction", "color": "lightskyblue", "group": 12, "loc": "177 206" },\
              { "key": 124, "text": "Decisions made wlil Maximize Benefit to the Enterprise", "color": "lightskyblue", "group": 12, "loc": "304 203" },\
              { "key": 2, "text": "Business Architecture", "color": "silver", "isGroup": true, "loc": "503 21" },\
              { "key": 21, "text": "Process", "color": "lightgray", "isGroup": true, "group": 2, "loc": "514 54" },\
              { "key": 211, "text": "Complaints\\n Handling", "color": "lightgreen", "figure":"Hexagon", "group": 21, "loc": "546 35" },\
              { "key": 212, "text": "Resolve \\nComplaint", "color": "lightskyblue", "group": 21, "loc": "679 64" },\
              { "key": 213, "text": "Complaints\\n Handling", "color": "lightskyblue", "group": 21, "loc": "550 145" },\
              { "key": 22, "text": "Organization", "color": "lightgray", "isGroup": true, "group": 2, "loc": "842 55" },\
              { "key": 221, "text": "Complaints \\nCall Handler", "color": "lightskyblue", "group": 22, "loc": "852 65.6" },\
              { "key": -18, "text": "Complaints \\nClient", "color": "lightskyblue", "group": 22, "loc": "854 142" },\
              { "key": -19, "text": "Complaints \\nManager", "color": "lightskyblue", "group": 22, "loc": "852 225" },\
              { "key": 23, "text": "Business Service", "color": "lightgray", "isGroup": true, "group": 2, "loc": "514 217" },\
              { "key": 231, "text": "Manage \\nComplaints", "color": "lightskyblue", "group": 23, "loc": "550 247" }\
            ],\
              "linkDataArray": [\
                { "from": 111, "to": 123, "fp": "R", "tp": "L" },\
                { "from": 121, "to": 123, "fp": "B", "tp": "T", "text": "Process" },\
                { "from": 121, "to": 122, "fp": "B", "tp": "T", "text": "Process" },\
                { "from": 121, "to": 211, "fp": "R", "tp": "L", "text": "Process" },\
                { "from": 121, "to": 231, "fp": "R", "tp": "L", "text": "Process" },\
                { "from": 211, "to": 213, "fp": "B", "tp": "T", "text": "Process type:\\nProcess" },\
                { "from": 231, "to": 213, "fp": "T", "tp": "B", "text": "Process" },\
                { "from": 231, "to": 212, "fp": "T", "tp": "B" },\
                { "from": 212, "to": -19, "fp": "B", "tp": "L", "text": "Process" },\
                { "from": 212, "to": -18, "fp": "B", "tp": "L", "text": "Process" },\
                { "from": 212, "to": 221, "fp": "R", "tp": "L", "text": "Process" }\
              ]\
          }'
        );
      }
    </script>
    </head>
    <body onload="init()">
      <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
    </body>
    </html>