JointJS - Attributes Binding

by Roman Bruckner

HTML

<html>
  <body>
    <div id="paper"></div>
    <script src="https://cdn.jsdelivr.net/npm/@joint/[email protected]/dist/joint.min.js"></script>
  </body>
</html>

CSS

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

JavaScript

class MyShape extends joint.dia.Element {
  defaults() {
    return {
      ...super.defaults,
      type: 'MyShape',
      color: 'gray',
      name: 'My Shape',
      number: 0,
      size: {
        width: 100,
        height: 80,
      },
      attrs: {
        body: {
          width: 'calc(w)',
          height: 'calc(h)',
          stroke: '#333333',
          strokeWidth: 2,
        },
        label: {
          text: 'Image',
          textVerticalAnchor: 'middle',
          textAnchor: 'middle',
          x: 'calc(0.5*w)',
          y: 'calc(0.5*h)',
          fontSize: 14,
          fontFamily: 'sans-serif',
          fill: '#ffffff',
        },
      },
    };
  }

  preinitialize() {
    this.markup = [
      {
        tagName: 'rect',
        selector: 'body',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ];
  }

  initialize(...args) {
    super.initialize(...args);
    this.on('change', ({ changed }, opt) => {
      if ('color' in changed || 'name' in changed || 'number' in changed) {
        this.updateBindings(opt);
      }
    });
    this.updateBindings();
  }

  updateBindings(opt) {
    this.attr(
      {
        body: {
          fill: this.get('color'),
        },
        label: {
          text: `${this.get('name')} ${this.get('number')}`,
        },
      },
      opt,
    );
  }

  toJSON() {
    const json = super.toJSON();
    // presentation attributes are controlled via
    // top level attributes only (color, name, number)
    delete json.attrs;
    // element is never rotated
    delete json.angle;
    // size will never change
    delete json.size;
    return json;
  }
}

const shapes = { MyShape };

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

const paper = new joint.dia.Paper({
  el: document.getElementById('paper'),
  width: 800,
  height: 800,
  model:...