JointJS:不同对象的多个模板
JointJS: Multiple Templates for different Objects
我实际上正在开发一种可视化数据流的工具。我有不同的类型
应该使用的元素,并且希望每个元素(对象)都有一个自己的 html 模板。有人知道这是否可行以及如何实现吗?
非常感谢您的帮助,非常感谢!
尊敬的问候,
拉斐尔
这是我的代码(抱歉弄乱了):
(function() {
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 650, height: 400, gridSize: 1, model: graph });
// Create a system element.
// -----------------------------------------------------------------------------
joint.shapes.html = {};
joint.shapes.html.Element = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><rect/></g><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port<%= id %>"><circle/></g>',
defaults: joint.util.deepSupplement({
type: 'html.Element',
size: { width: 100, height: 80 },
inPorts: [],
outPorts: [],
attrs: {
'.': { magnet: false },
rect: {
stroke: 'none', 'fill-opacity': 0, width: 170, height: 250,
},
circle: {
r: 6, //circle radius
magnet: true,
stroke: 'black'
},
'.inPorts circle': { fill: 'green', magnet: 'passive', type: 'input'},
'.outPorts circle': { fill: 'red', type: 'output'}
}
}, joint.shapes.basic.Generic.prototype.defaults),
getPortAttrs: function (portName, index, total, selector, type) {
var attrs = {};
var portClass = 'port' + index;
var portSelector = selector + '>.' + portClass;
var portCircleSelector = portSelector + '>circle';
attrs[portCircleSelector] = { port: { id: portName || _.uniqueId(type), type: type } };
attrs[portSelector] = { ref: 'rect', 'ref-y': (index + 0.5) * (1 / total) };
if (selector === '.outPorts') { attrs[portSelector]['ref-dx'] = 0; }
return attrs;
}
}));
// -------------------------------------------------------------------------------------------------------------------------------------
// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------------------------------------------------------------------
joint.shapes.html.ElementView = joint.dia.ElementView.extend({
objectname: "System",
template: [
'<div class="html-element" id=>',
'<button class="delete">x</button>',
'<button class="add">+</button>',
'<div class="head">',
'<h3>',
'System',
'</h3>',
'<input type="text" class="systemComment" placeholder = "Add a comment to this System"/>',
'</div> </br>',
'</div>'
].join(''),
//::: Start initialize function :::
initialize: function() {
_.bindAll(this, 'updateBox');
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
this.$box = $(_.template(this.template)());
// Prevent paper from handling pointerdown.
this.$box.find('input,select').on('mousedown click', function(evt) {
evt.stopPropagation();
});
// This is an example of reacting on the input change and storing the input data in the cell model.
this.$box.find('input').on('change', _.bind(function(evt) {
this.model.set('input', $(evt.target).val());
}, this));
this.$box.find('select').on('change', _.bind(function(evt) {
this.model.set('select', $(evt.target).val());
}, this));
this.$box.find('select').val(this.model.get('select'));
this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
$('.add').on('click', function(){
//we select the box clone it and insert it after the box
$(".html-element").clone().appendTo("#paper");
});
// Update the box position whenever the underlying model changes.
this.model.on('change', this.updateBox, this);
// Remove the box when the model gets removed from the graph.
this.model.on('remove', this.removeBox, this);
this.updateBox();
},
//::: End initialize function :::
//::: Start render function :::
render: function() {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.paper.$el.prepend(this.$box);
// this.paper.$el.mousemove(this.onMouseMove.bind(this)), this.paper.$el.mouseup(this.onMouseUp.bind(this));
this.updateBox();
return this;
},
//::: End render function :::
//::: Start renderPortst function :::
renderPorts: function () {
var $inPorts = this.$('.inPorts').empty();
var $outPorts = this.$('.outPorts').empty();
var portTemplate = _.template(this.model.portMarkup);
_.each(_.filter(this.model.ports, function (p) { return p.type === 'in' }), function (port, index) {
$inPorts.append(V(portTemplate({ id: index, port: port })).node);
});
_.each(_.filter(this.model.ports, function (p) { return p.type === 'out' }), function (port, index) {
$outPorts.append(V(portTemplate({ id: index, port: port })).node);
});
},
//::: End renderPortst function :::
//::: Start update function
update: function () {
// First render ports so that `attrs` can be applied to those newly created DOM elements
// in `ElementView.prototype.update()`.
this.renderPorts();
joint.dia.ElementView.prototype.update.apply(this, arguments);
},
//::: End update function :::
//::: Start updateBox function
updateBox: function() {
// Set the position and dimension of the box so that it covers the JointJS element.
var bbox = this.model.getBBox();
// Example of updating the HTML with a data stored in the cell model.
// paper.on('blank:pointerdown', function(evt, x, y) { this.$box.find('textarea').toBack(); });
this.model.on('cell:pointerclick', function(evt, x, y) { this.$box.find('textarea').toFront(); });
this.$box.css({ width: bbox.width, height: bbox.height, left: bbox.x + 15, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)' });
},
//::: End updateBox function :::
//::: Start removeBox function :::
removeBox: function(evt) {
this.$box.remove();
}
//::: End removeBox function :::
});
// -------------------------------------------------------------------------------------------------------------------------------------
// Create JointJS elements and add them to the graph as usual.
// -------------------------------------------------------------------------------------------------------------------------------------
var system = new joint.shapes.html.Element({
position: { x: 80, y: 80 },
size: { width: 240, height: 180 },
outPorts:['systemOut'],
inPorts: ['systemIn'],
});
var dbo = new joint.shapes.html.Element({
position: { x: 120, y: 210 },
size: { width: 240, height: 180 },
outPorts:['dboOut'],
inPorts: ['dboIn'],
})
// -------------------------------------------------------------------------------------------------------------------------------------
//Adding all to the graph
graph.addCells([system, dbo]);
}())
您可以将模板定义放在模型上,然后您可以使用自定义模板实例化元素,如下所示:
new joint.shapes.html.Element({
template: [
'<div class="my-html-element">',
'<div><input data-attribute="myinput" type="checkbox"/></div>',
'<div><input data-attribute="myinput" type="checkbox"/></div>',
'<div><input data-attribute="myinput" type="checkbox"/></div>',
'</div>'
].join(''),
});
这是一个完整的例子:https://jsfiddle.net/vtalas/pruz7h9w/
请注意,自 Joint v1.0 以来,有一个新的 API 可以更轻松地操作端口(它也应用于上面的演示中)。
我实际上正在开发一种可视化数据流的工具。我有不同的类型 应该使用的元素,并且希望每个元素(对象)都有一个自己的 html 模板。有人知道这是否可行以及如何实现吗?
非常感谢您的帮助,非常感谢!
尊敬的问候,
拉斐尔
这是我的代码(抱歉弄乱了):
(function() {
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#paper'), width: 650, height: 400, gridSize: 1, model: graph });
// Create a system element.
// -----------------------------------------------------------------------------
joint.shapes.html = {};
joint.shapes.html.Element = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable"><g class="scalable"><rect/></g><g class="inPorts"/><g class="outPorts"/></g>',
portMarkup: '<g class="port<%= id %>"><circle/></g>',
defaults: joint.util.deepSupplement({
type: 'html.Element',
size: { width: 100, height: 80 },
inPorts: [],
outPorts: [],
attrs: {
'.': { magnet: false },
rect: {
stroke: 'none', 'fill-opacity': 0, width: 170, height: 250,
},
circle: {
r: 6, //circle radius
magnet: true,
stroke: 'black'
},
'.inPorts circle': { fill: 'green', magnet: 'passive', type: 'input'},
'.outPorts circle': { fill: 'red', type: 'output'}
}
}, joint.shapes.basic.Generic.prototype.defaults),
getPortAttrs: function (portName, index, total, selector, type) {
var attrs = {};
var portClass = 'port' + index;
var portSelector = selector + '>.' + portClass;
var portCircleSelector = portSelector + '>circle';
attrs[portCircleSelector] = { port: { id: portName || _.uniqueId(type), type: type } };
attrs[portSelector] = { ref: 'rect', 'ref-y': (index + 0.5) * (1 / total) };
if (selector === '.outPorts') { attrs[portSelector]['ref-dx'] = 0; }
return attrs;
}
}));
// -------------------------------------------------------------------------------------------------------------------------------------
// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------------------------------------------------------------------
joint.shapes.html.ElementView = joint.dia.ElementView.extend({
objectname: "System",
template: [
'<div class="html-element" id=>',
'<button class="delete">x</button>',
'<button class="add">+</button>',
'<div class="head">',
'<h3>',
'System',
'</h3>',
'<input type="text" class="systemComment" placeholder = "Add a comment to this System"/>',
'</div> </br>',
'</div>'
].join(''),
//::: Start initialize function :::
initialize: function() {
_.bindAll(this, 'updateBox');
joint.dia.ElementView.prototype.initialize.apply(this, arguments);
this.$box = $(_.template(this.template)());
// Prevent paper from handling pointerdown.
this.$box.find('input,select').on('mousedown click', function(evt) {
evt.stopPropagation();
});
// This is an example of reacting on the input change and storing the input data in the cell model.
this.$box.find('input').on('change', _.bind(function(evt) {
this.model.set('input', $(evt.target).val());
}, this));
this.$box.find('select').on('change', _.bind(function(evt) {
this.model.set('select', $(evt.target).val());
}, this));
this.$box.find('select').val(this.model.get('select'));
this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
$('.add').on('click', function(){
//we select the box clone it and insert it after the box
$(".html-element").clone().appendTo("#paper");
});
// Update the box position whenever the underlying model changes.
this.model.on('change', this.updateBox, this);
// Remove the box when the model gets removed from the graph.
this.model.on('remove', this.removeBox, this);
this.updateBox();
},
//::: End initialize function :::
//::: Start render function :::
render: function() {
joint.dia.ElementView.prototype.render.apply(this, arguments);
this.paper.$el.prepend(this.$box);
// this.paper.$el.mousemove(this.onMouseMove.bind(this)), this.paper.$el.mouseup(this.onMouseUp.bind(this));
this.updateBox();
return this;
},
//::: End render function :::
//::: Start renderPortst function :::
renderPorts: function () {
var $inPorts = this.$('.inPorts').empty();
var $outPorts = this.$('.outPorts').empty();
var portTemplate = _.template(this.model.portMarkup);
_.each(_.filter(this.model.ports, function (p) { return p.type === 'in' }), function (port, index) {
$inPorts.append(V(portTemplate({ id: index, port: port })).node);
});
_.each(_.filter(this.model.ports, function (p) { return p.type === 'out' }), function (port, index) {
$outPorts.append(V(portTemplate({ id: index, port: port })).node);
});
},
//::: End renderPortst function :::
//::: Start update function
update: function () {
// First render ports so that `attrs` can be applied to those newly created DOM elements
// in `ElementView.prototype.update()`.
this.renderPorts();
joint.dia.ElementView.prototype.update.apply(this, arguments);
},
//::: End update function :::
//::: Start updateBox function
updateBox: function() {
// Set the position and dimension of the box so that it covers the JointJS element.
var bbox = this.model.getBBox();
// Example of updating the HTML with a data stored in the cell model.
// paper.on('blank:pointerdown', function(evt, x, y) { this.$box.find('textarea').toBack(); });
this.model.on('cell:pointerclick', function(evt, x, y) { this.$box.find('textarea').toFront(); });
this.$box.css({ width: bbox.width, height: bbox.height, left: bbox.x + 15, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)' });
},
//::: End updateBox function :::
//::: Start removeBox function :::
removeBox: function(evt) {
this.$box.remove();
}
//::: End removeBox function :::
});
// -------------------------------------------------------------------------------------------------------------------------------------
// Create JointJS elements and add them to the graph as usual.
// -------------------------------------------------------------------------------------------------------------------------------------
var system = new joint.shapes.html.Element({
position: { x: 80, y: 80 },
size: { width: 240, height: 180 },
outPorts:['systemOut'],
inPorts: ['systemIn'],
});
var dbo = new joint.shapes.html.Element({
position: { x: 120, y: 210 },
size: { width: 240, height: 180 },
outPorts:['dboOut'],
inPorts: ['dboIn'],
})
// -------------------------------------------------------------------------------------------------------------------------------------
//Adding all to the graph
graph.addCells([system, dbo]);
}())
您可以将模板定义放在模型上,然后您可以使用自定义模板实例化元素,如下所示:
new joint.shapes.html.Element({
template: [
'<div class="my-html-element">',
'<div><input data-attribute="myinput" type="checkbox"/></div>',
'<div><input data-attribute="myinput" type="checkbox"/></div>',
'<div><input data-attribute="myinput" type="checkbox"/></div>',
'</div>'
].join(''),
});
这是一个完整的例子:https://jsfiddle.net/vtalas/pruz7h9w/
请注意,自 Joint v1.0 以来,有一个新的 API 可以更轻松地操作端口(它也应用于上面的演示中)。