I have a jsFiddle of JointJS code that gives me what I want: https://jsfiddle.net/RaymondMH/s46uyq8v/156/
However, when I try to make use of this code in my actual application, none of the functions defined in the ElementView.extend()
call ever get called, and the Element does not get created like I want it to.
This is how I put it in my code:
import { Injectable } from '@angular/core';
import * as joint from '@clientio/rappid';
import * as _ from 'lodash';
import * as V from 'jquery';
@Injectable({
providedIn: 'root'
})
export class SchematicElementService {
private SystemElement: any;
constructor() {
}
public GetNodeElement(): { node: joint.dia.Element, embedId: number } {
let element: joint.dia.Element;
element = new this.SystemElement();
element
.prop('numberOfComponents', 4)
.position(150, 150);
return { node: element, embedId: null };
}
public InitializeDefinitions() {
let componentOutlineRect = {
x: 0, y: 0, width: 215, height: 24,
fill: '#fafafa', stroke: '#000000', 'stroke-width': '0.1'
}
let componentText = {
x: 0, y: 0,
fill: 'black',
'font-size': 13, 'font-family': 'Arial', 'font-weight': 'normal'
}
let componentStatusText = {
x: 14, y: 18,
'font-size': 13, 'font-family': 'Arial', 'font-weight': '600',
fill: 'green'
}
let componentIconRect = {
x: 34, y: 5, width: 16, height: 16,
fill: '#565656'
}
this.SystemElement = joint.dia.Element.extend({
defaults: joint.util.defaultsDeep({
type: 'custom.Element',
outlineColor: 'black',
numberOfComponents: 1,
size: {
width: 215,
height: 24
},
attrs: {
body: {
...componentOutlineRect,
fill: '#bebebe'
},
bodytitle: {
...componentText
},
components: {
...componentOutlineRect,
y: 24,
}
}
}),
markup: [
{ tagName: 'rect', selector: 'body' },
{ tagName: 'text', selector: 'bodytitle' },
{ tagName: 'rect', selector: 'components' },
{ tagName: 'text', selector: 'componentStatus' },
{ tagName: 'rect', selector: 'componentIcons' },
{ tagName: 'text', selector: 'text' }
]
});
this.SystemElement.ElementView = joint.dia.ElementView.extend(
{
events: {
'dblclick': 'onDblClick',
'click .components': 'onComponentClick'
},
init: function () {
console.log('************* init() function called!');
var model = this.model;
this.listenTo(model, [
'change:fillColor',
'change:outlineColor',
].join(' '), this.update);
this.listenTo(model, 'change:numberOfComponents', this.render);
},
render: function () {
console.log('**************** render function called!');
var markup = this.constructor.markup;
var body = this.vBody = markup.body.clone();
var bodyTitle = this.vBodyTitle = markup.bodytitle.clone().text('A - Static Structural');
var components = this.vComponents = [];
var componentStatus = this.vComponentStatus = [];
var componentIcons = this.vComponentIcons = [];
var componentLabels = this.vComponentLabels = [];
for (var i = 0, n = this.model.prop('numberOfComponents'); i < n; i++) {
components.push(markup.components.clone());
componentStatus.push(markup.componentStatus.clone().text('✓'));
componentIcons.push(markup.componentIcons.clone());
componentLabels.push(markup.text.clone().text('Component ' + (i + 1)));
}
this.vel.empty().append(
_.flatten([
body,
components
])
);
this.translate();
this.update();
},
update: function () {
this.updateBody();
this.updateComponents();
},
updateBody: function () {
var model = this.model;
var bodyAttributes = {
...componentOutlineRect,
fill: '#aaaaaa',
};
this.vBody.attr(bodyAttributes);
var bodyTitle = this.vBodyTitle;
bodyTitle.attr({
...componentText,
'font-weight': '600',
transform: 'translate(14,16)'
});
this.vel.append(bodyTitle);
},
updateComponents: function () {
console.log('************** updateComponents called!');
var model = this.model;
var numberOfComponents = model.prop('numberOfComponents');
var vComponents = this.vComponents;
for (var i = 0; i < numberOfComponents; i++) {
vComponents[i].attr({
...componentOutlineRect,
y: 24 * (i + 1),
'data-index': i
});
}
var vComponentStatus = this.vComponentStatus;
for (var i = 0; i < numberOfComponents; i++) {
var vStatus = vComponentStatus[i];
var tx = 14;
var ty = 24 * (i + 1);
vStatus.attr({
...componentStatusText,
transform: 'translate(' + tx + ',' + ty + ')'
});
this.vel.append(vStatus);
}
var vComponentIcons = this.vComponentIcons;
for (var i = 0; i < numberOfComponents; i++) {
var vIcon = vComponentIcons[i];
vIcon.attr({
...componentIconRect,
x: 34,
y: 24 * (i + 1) + 4
});
this.vel.append(vIcon);
}
var vComponentLabels = this.vComponentLabels;
for (var i = 0; i < numberOfComponents; i++) {
var vText = vComponentLabels[i];
var tx = 58;
var ty = 24 * (i + 2) - 8;
vText.attr({
...componentText,
transform: 'translate(' + tx + ',' + ty + ')'
});
this.vel.append(vText);
}
},
onComponentClick: function (evt) {
var index = +V(evt.target).attr('data-index');
var model = this.model;
var numberOfComponents = model.prop('numberOfComponents');
var vComponents = this.vComponents;
for (var i = 0; i < numberOfComponents; i++) {
vComponents[i].attr({
fill: i === index ? '#dddddd' : "#fafafa"
});
}
},
onDblClick: function () {
this.model.prop('faded', !this.model.prop('faded'));
}
});
// SEE COMMENTS BELOW REGARDING THIS COMMENTED CODE:
//}, {
// markup: {
// body: V('rect').addClass('body'),
// bodytitle: V('text').addClass('bodytitle'),
// components: V('rect').addClass('components'),
// componentStatus: V('text').addClass('componentStatus'),
// componentIcons: V('rect').addClass('componentIcons'),
// text: V('text').addClass('text')
// }
//});
}
}
The Element is initialized like this:
ngOnInit(): void {
this.schematicElementService.InitializeDefinitions();
this.graph = new joint.dia.Graph();
this.paper = new joint.dia.Paper({
model: this.graph,
cellViewNamespace: joint.shapes.basic,
el: $('#paper'),
width: 2400,
height: 2400,
origin: { x: 600, y: 600 },
gridSize: 1,
// interactive: { addLinkFromMagnet: false },
linkPinning: false
});
}
The element is added to the graph like this:
let nodeData = this.schematicElementService.GetNodeElement();
this.graph.addCell(nodeData.node);
In a way different from the jsFiddle, the Markup is placed in the Element.extend()
function rather than in the ElementView.extend()
(as shown in the commented code of the ElementView.extend()
).
Implemented as above, I get a couple 'boxes' appear, but since the functions in the ElementView are not used, it does not process things as I expect it to. I.e. there is no text and no multiple boxes because these are processed in the functions.
When I remove the markup from the Element.extend()
and put it in the ElementView.extend()
(i.e. uncomment the commented code above), I get the following error when processing this:
ERROR Error: dia.ElementView: markup required
at child.renderMarkup (rappid.js:19452)
at child.render (rappid.js:19483)
at child.protoProps.render (rappid.js:18246)
at child.confirmUpdate (rappid.js:19372)
at child.updateView (rappid.js:29615)
at child.updateViewsBatch (rappid.js:29812)
at child.updateViews (rappid.js:29668)
at child.requestViewUpdate (rappid.js:29539)
at child.renderView (rappid.js:30305)
at child.onCellAdded (rappid.js:29261)
None of the console.log()
calls that I added to the functions EVER gets written to console, so none of those functions ever get called. Defining my custom element in the Joint data structure, as in joint.shapes.custom = {}
does not make a difference. I have the same problem using either of the JointJS or the Rappid libraries.
Does anyone see what I'm doing wrong here? I cannot figure out how to make this work.
Thanks in advance for any assistance you can provide.
I'm not sure if this will work in your case, but you could try specifying the view to use when initialising the paper, with the
elementView
option:Also, I'm not sure if the element view should be a child of the element. In my case I put the element view in a separate variable.