JointJS - Custom Element View

An example how to create a JointJS element view with custom rendering/updating.

by Roman Bruckner

HTML

<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/1.0.3/joint.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/1.0.3/joint.min.css">
<!-- JointJS Fiddle -->
<div id="paper"></div>

CSS

#paper {
  display: inline-block;
  border: 1px solid gray;
}

JavaScript

var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
  el: $('#paper'),
  width: 600,
  height: 600,
  gridSize: 20,
  model: graph
});

joint.shapes.custom = {};
joint.shapes.custom.Element = joint.dia.Element.extend({
  defaults: _.defaultsDeep({
    type: 'custom.Element',
    fillColor: 'red',
    outlineColor: 'black',
    activeTab: 0,
    numberOfTabs: 1,
    faded: false
  })
});


joint.shapes.custom.ElementView = joint.dia.ElementView.extend({

  events: {
    'dblclick': 'onDblClick',
    'click .tab': 'onTabClick'
  },

  init: function() {
    var model = this.model;
    this.listenTo(model, [
      'change:activeTab',
      'change:fillColor',
      'change:outlineColor',
    ].join(' '), this.update);
    this.listenTo(model, 'change:faded', this.toggleFade);
    this.listenTo(model, 'change:numberOfTabs', this.render);
  },

  render: function() {      
      var rootVEl = this.vel;
      rootVEl.empty();
			var edges = this.model.get('edges');
      edges.forEach(function(edge) {
      	var path = V('path').attr('d', `M ${edge.start.x} ${edge.start.y} ${edge.end.x} ${edge.end.y}`);
        path.appendTo(rootVEl);
      });
  },

  update: function() {
    this.updateBody();
    this.updateText();
    this.updateTabs();
    this.toggleFade();
  },

  updateBody: function() {
    var model = this.model;
    var bodyAttributes = {
      width: model.prop('size/width'),
      height: model.prop('size/height'),
      fill: model.prop('fillColor'),
      stroke: model.prop('outlineColor')
    };
    this.vBody.attr(bodyAttributes);
  },

  updateTabs: function() {
    var model = this.model;
    var numberOfTabs = model.prop('numberOfTabs');
    var length = model.prop('size/width') / numberOfTabs;
    var activeTab = model.prop('activeTab');
    var vTabs = this.vTabs;
    for (var i = 0; i < numberOfTabs; i++) {
      var isActive = (activeTab === i);
      var offset = +isActive;
      vTabs[i].attr({
        width: length - 2 *...