Search code examples
javascriptjqueryjointjs

How do you apply Smart Routing on links with ports on JointJS?


I am trying to apply smart routing of links with the use of ports using JointJS. This documentation shows the one I am trying to achieve. The example on the docs though shows only the programmatic way of adding Link from point A to point B. How do you do this with the use of ports?

Here's my code: JSFiddle.

HTML:

<html>
  <body>
    <button id="btnAdd">Add Table</button>
    <div id="dbLookupCanvas"></div>
  </body>
</html>

JS

$(document).ready(function() {
  $('#btnAdd').on('click', function() {
    AddTable();
  });

  InitializeCanvas();
  
  // Adding of two sample tables on first load
  AddTable(50, 50);
    AddTable(250, 50);
});

var graph;
var paper
var selectedElement;
var namespace;

function InitializeCanvas() {
  let canvasContainer = $('#dbLookupCanvas').parent();

  namespace = joint.shapes;

  graph = new joint.dia.Graph({}, {
    cellNamespace: namespace
  });

  paper = new joint.dia.Paper({
    el: document.getElementById('dbLookupCanvas'),
    model: graph,
    width: canvasContainer.width(),
    height: 500,
    gridSize: 10,
    drawGrid: true,
    cellViewNamespace: namespace,
    validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
      return (magnetS !== magnetT);
    },
    snapLinks: {
      radius: 20
    }
  });

  //Dragging navigation on canvas
  var dragStartPosition;
  paper.on('blank:pointerdown',
    function(event, x, y) {
      dragStartPosition = {
        x: x,
        y: y
      };
    }
  );

  paper.on('cell:pointerup blank:pointerup', function(cellView, x, y) {
    dragStartPosition = null;
  });

  $("#dbLookupCanvas")
    .mousemove(function(event) {
      if (dragStartPosition)
        paper.translate(
          event.offsetX - dragStartPosition.x,
          event.offsetY - dragStartPosition.y);
    });

  // Remove links not connected to anything
  paper.model.on('batch:stop', function() {
    var links = paper.model.getLinks();
    _.each(links, function(link) {
      var source = link.get('source');
      var target = link.get('target');
      if (source.id === undefined || target.id === undefined) {
        link.remove();
      }
    });
  });

  paper.on('cell:pointerdown', function(elementView) {
    resetAll(this);
    let isElement = elementView.model.isElement();

    if (isElement) {
      var currentElement = elementView.model;
      currentElement.attr('body/stroke', 'orange');
      selectedElement = elementView.model;
    } else
      selectedElement = null;
  });

  paper.on('blank:pointerdown', function(elementView) {
    resetAll(this);
  });

  $('#dbLookupCanvas')
    .attr('tabindex', 0)
    .on('mouseover', function() {
      this.focus();
    })
    .on('keydown', function(e) {
      if (e.keyCode == 46)
        if (selectedElement) selectedElement.remove();
    });
}

function AddTable(xCoord = undefined, yCoord = undefined) {
  // This is a sample database data here
  let data = [
    {columnName: "radomData1"},
    {columnName: "radomData2"}
  ];
  
  if (xCoord == undefined && yCoord == undefined)
  {
    xCoord = 50;
    yCoord = 50;
  } 

  const rect = new joint.shapes.standard.Rectangle({
    position: {
      x: xCoord,
      y: yCoord
    },
    size: {
      width: 150,
      height: 200
    },
    ports: {
      groups: {
        'a': {},
        'b': {}
      }
    }
  });

  $.each(data, (i, v) => {
    const port = {
      group: 'a',
      args: {}, // Extra arguments for the port layout function, see `layout.Port` section
      label: {
        position: {
          name: 'right',
          args: {
            y: 6
          } // Extra arguments for the label layout function, see `layout.PortLabel` section
        },
        markup: [{
          tagName: 'text',
          selector: 'label'
        }]
      },
      attrs: {
        body: {
          magnet: true,
          width: 16,
          height: 16,
          x: -8,
          y: -4,
          stroke: 'red',
          fill: 'gray'
        },
        label: {
          text: v.columnName,
          fill: 'black'
        }
      },
      markup: [{
        tagName: 'rect',
        selector: 'body'
      }]
    };

    rect.addPort(port);
  });

  rect.resize(150, data.length * 40);

  graph.addCell(rect);
}

function resetAll(paper) {
  paper.drawBackground({
    color: 'white'
  });

  var elements = paper.model.getElements();
  for (var i = 0, ii = elements.length; i < ii; i++) {
    var currentElement = elements[i];
    currentElement.attr('body/stroke', 'black');
  }

  var links = paper.model.getLinks();
  for (var j = 0, jj = links.length; j < jj; j++) {
    var currentLink = links[j];
    currentLink.attr('line/stroke', 'black');
    currentLink.label(0, {
      attrs: {
        body: {
          stroke: 'black'
        }
      }
    });
  }
}

Any help would be appreciated. Thanks!


Solution

  • The default link created when you draw a link from a port is joint.dia.Link.

    To change this you can use the defaultLink paper option, and configure the router you would like. defaultLink documentation reference

    const paper = new joint.dia.Paper({
        el: document.getElementById('dbLookupCanvas'),
        model: graph,
        width: canvasContainer.width(),
        height: 500,
        gridSize: 10,
        drawGrid: true,
        cellViewNamespace: namespace,
        validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
          return (magnetS !== magnetT);
        },
        snapLinks: {
          radius: 20
        },
        defaultLink: () => new joint.shapes.standard.Link({
          router: { name: 'manhattan' },
          connector: { name: 'rounded' },
        })
      });
    

    You could also provide several default options in the paper.

    defaultLink: () => new joint.shapes.standard.Link(),
    defaultRouter: { name: 'manhattan' },
    defaultConnector: { name: 'rounded' }