title: How do you apply Smart Routing on links with ports on JointJS?
body: <p>I am trying to apply smart routing of links with the use of ports using JointJS. This <a href="https://resources.jointjs.com/demos/routing" rel="nofollow noreferrer">documentation</a> 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?</p>
  <p>Here's my code: <a href="https://jsfiddle.net/q1mn95k6/40/" rel="nofollow noreferrer">JSFiddle</a>.</p>
  <p><strong>HTML:</strong></p>
  <pre><code>&lt;html&gt;
    &lt;body&gt;
      &lt;button id=&quot;btnAdd&quot;&gt;Add Table&lt;/button&gt;
      &lt;div id=&quot;dbLookupCanvas&quot;&gt;&lt;/div&gt;
    &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  <p><strong>JS</strong></p>
  <pre><code>$(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;
    });
  
    $(&quot;#dbLookupCanvas&quot;)
      .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: &quot;radomData1&quot;},
      {columnName: &quot;radomData2&quot;}
    ];
    
    if (xCoord == undefined &amp;&amp; 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) =&gt; {
      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 &lt; ii; i++) {
      var currentElement = elements[i];
      currentElement.attr('body/stroke', 'black');
    }
  
    var links = paper.model.getLinks();
    for (var j = 0, jj = links.length; j &lt; jj; j++) {
      var currentLink = links[j];
      currentLink.attr('line/stroke', 'black');
      currentLink.label(0, {
        attrs: {
          body: {
            stroke: 'black'
          }
        }
      });
    }
  }
  </code></pre>
  <p>Any help would be appreciated. Thanks!</p>