如何验证 mxgraph 中单元格的未连接边
How to validate for un-connected edges to cell in mxgraph
嗨,我遇到 validation
的问题,因为 未连接 edges
到 mxgraph
中的单元格
下图会告诉你我的期望:
问题:每当我按下验证按钮未连接edges
和cells
必须用红色突出显示。
完整视图Codepen:https://codepen.io/eabangalore/pen/pmELpL?editors=1100
注意:请参阅 CODEPEN LINK (https://codepen.io/eabangalore/pen/pmELpL?editors=1100),因为下面的代码不工作
完整代码:
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
function setGraphData(){
var graphState ={"tagName":"mxGraphModel","children":[{"tagName":"root","children":[{"tagName":"mxCell","attributes":{"id":"0"}},{"tagName":"mxCell","attributes":{"id":"1","parent":"0"}},{"tagName":"mxCell","attributes":{"id":"2","value":"A","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"271.56251525878906","y":"82.44792175292969","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"3","value":"B","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"678.2291717529297","y":"106.89236450195312","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"4","value":"Bangalore","parent":"1","edge":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"0.0511","y":"-20","relative":"1","as":"geometry"},"children":[{"tagName":"mxPoint","attributes":{"x":"370.06251525878906","y":"109.95338610053051","as":"sourcePoint"}},{"tagName":"mxPoint","attributes":{"x":"676.7291717529297","y":"128.3869001543523","as":"targetPoint"}},{"tagName":"mxPoint","attributes":{"as":"offset"}}]}]},{"tagName":"mxCell","attributes":{"id":"5","value":"C","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"1013.7847747802734","y":"83.55902862548828","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"6","parent":"1","source":"3","target":"5","edge":"1"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"7","value":"Not Connected","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"552","y":"267.640625","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"8","value":"Edge Not connected must highlight in red color","edge":"1","parent":"1","source":"5"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"},"children":[{"tagName":"mxPoint","attributes":{"x":"740","y":"260","as":"targetPoint"}}]}]}]}]};
localStorage.setItem('graphState',JSON.stringify(graphState));
}
function html2json(html){
if(html.nodeType==3){
return {
"tagName":"#text",
"content":html.textContent
}
}
var element = {
"tagName":html.tagName
};
if(html.getAttributeNames().length>0){
element.attributes = html.getAttributeNames().reduce(
function(acc,at){acc[at]=html.getAttribute(at); return acc;},
{}
);
}
if(html.childNodes.length>0){
element.children = Array.from(html.childNodes)
.filter(
function(el){
return el.nodeType!=3
||el.textContent.trim().length>0
})
.map(function(el){return html2json(el);});
}
return element;
}
function json2html(json){
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node){
if(jsonNode.attributes){
Object.keys(jsonNode.attributes).map(
function(name){
node.setAttribute(name,jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode,node){
if(jsonNode.children){
jsonNode.children.map(
function(jsonChildNode){
json2htmlNode(jsonChildNode,node);
}
);
}
}
var json2htmlNode = function(jsonNode,parent){
if(jsonNode.tagName=="#text"){
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode,node);
addChildren(jsonNode,node);
parent.appendChild(node);
}
addAttributes(json,xmlDoc.firstElementChild);
addChildren(json,xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
function main()
{
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS)
{
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
var graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style)
{
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function()
{
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if(window.localStorage.graphState){
var doc = json2html(JSON.parse(localStorage.graphState));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change',function(){
var codec = new mxCodec();
window.localStorage.graphState = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image)
{
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y)
{
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell)
{
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt)
{
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt)
{
if (img.enabled == false)
{
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
</script>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();" >
</body>
</html>
请帮我提前谢谢!!!
编辑:根据评论,要验证未连接到父单元或其分支的单元,我们可以使用递归函数检查每个连接的边从父单元开始,依此类推。
由于字符数限制,原代码片段被删除。
.not_connected * {
color: red;
font-color: red;
stroke: red;
stroke-color: red;
}
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();">
<button style="position:absolute; left:300px;padding:8px 40px;background:orangered;color:#fff;outline:none;border:none;z-index:100;" id="validate_btn">Validate</button>
</body>
</html>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
var graphStateJSON;
var graph;
function setGraphData() {
var graphState = {
"tagName": "mxGraphModel",
"children": [{
"tagName": "root",
"children": [{
"tagName": "mxCell",
"attributes": {
"id": "0"
}
}, {
"tagName": "mxCell",
"attributes": {
"id": "1",
"parent": "0"
}
}, {
"tagName": "mxCell",
"attributes": {
"id": "2",
"value": "A",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "460",
"y": "80",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "3",
"value": "C",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "460",
"y": "190",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "4",
"edge": "1",
"parent": "1",
"source": "2",
"target": "3"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "5",
"value": "B",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "280",
"y": "190",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "6",
"value": "D",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "663",
"y": "193",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "7",
"edge": "1",
"parent": "1",
"source": "2",
"target": "5"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "8",
"edge": "1",
"parent": "1",
"source": "2",
"target": "6"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "9",
"value": "E",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "660",
"y": "260",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "10",
"edge": "1",
"parent": "1",
"source": "6",
"target": "9"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "11",
"value": "F",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "280",
"y": "260",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "12",
"value": "G",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "459",
"y": "257",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "13",
"edge": "1",
"parent": "1",
"source": "11",
"target": "12"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}]
}]
};
graphStateJSON = JSON.stringify(graphState);
}
function html2json(html) {
if (html.nodeType == 3) {
return {
"tagName": "#text",
"content": html.textContent
}
}
var element = {
"tagName": html.tagName
};
if (html.getAttributeNames().length > 0) {
element.attributes = html.getAttributeNames().reduce(
function(acc, at) {
acc[at] = html.getAttribute(at);
return acc;
}, {}
);
}
if (html.childNodes.length > 0) {
element.children = Array.from(html.childNodes)
.filter(
function(el) {
return el.nodeType != 3 ||
el.textContent.trim().length > 0
})
.map(function(el) {
return html2json(el);
});
}
return element;
}
function json2html(json) {
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node) {
if (jsonNode.attributes) {
Object.keys(jsonNode.attributes).map(
function(name) {
node.setAttribute(name, jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode, node) {
if (jsonNode.children) {
jsonNode.children.map(
function(jsonChildNode) {
json2htmlNode(jsonChildNode, node);
}
);
}
}
var json2htmlNode = function(jsonNode, parent) {
if (jsonNode.tagName == "#text") {
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode, node);
addChildren(jsonNode, node);
parent.appendChild(node);
}
addAttributes(json, xmlDoc.firstElementChild);
addChildren(json, xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
var graph;
var graphView;
var notConnectedCells = [];
var parentCellId = "2";
function main() {
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported()) {
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
} else {
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS) {
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style) {
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function() {
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if (graphStateJSON) {
var doc = json2html(JSON.parse(graphStateJSON));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change', function() {
var codec = new mxCodec();
graphStateJSON = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image) {
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y) {
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell) {
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt) {
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt) {
if (img.enabled == false) {
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
function Validate(mxCell){
let isConnected = true;
// check each cell that each edge connected to
for(let i=0;i<mxCell.getEdgeCount();i++){
let edge = mxCell.getEdgeAt(i);
if(edge.target === null) continue; // no target
if(mxCell.getId() === edge.target.getId()) continue; // target is mxCell itself
isConnected = edge.source !== null && edge.target !== null;
if(isConnected){
// remove source cell if found and so on
let sourceIndex = notConnectedCells.findIndex(c=>c.id === edge.source.getId());
if(sourceIndex !== -1) notConnectedCells.splice(sourceIndex,1);
let targetIndex = notConnectedCells.findIndex(c=>c.id === edge.target.getId());
if(targetIndex !== -1) notConnectedCells.splice(targetIndex,1);
let edgeIndex = notConnectedCells.findIndex(c=>c.id === edge.getId());
if(edgeIndex !== -1) notConnectedCells.splice(edgeIndex,1);
// check next cell and its edges
Validate(edge.target);
}
}
}
function ResetColor(state){
state.shape.node.classList.remove("not_connected");
if(state.text)
state.text.node.classList.remove("not_connected");
}
function SetNotConnectedColor(state){
for(let i=0;i<notConnectedCells.length;i++){
let mxCell = notConnectedCells[i];
let state = graphView.getState(mxCell);
state.shape.node.classList.add("not_connected");
if(state.text)
state.text.node.classList.add("not_connected");
}
}
document.querySelector("#validate_btn").addEventListener("click", function() {
let cells = graph.getModel().cells;
graphView = graph.getView();
notConnectedCells.length = 0;
// create an array of cells and reset the color
for(let key in cells){
if(!cells.hasOwnProperty(key)) continue;
let mxCell = cells[key];
if(!mxCell.isVertex() && !mxCell.isEdge()) continue;
notConnectedCells.push(mxCell);
let state = graphView.getState(mxCell);
ResetColor(state);
}
// starts with the parent cell
let parentCell = notConnectedCells.find(c=>c.id === parentCellId);
Validate(parentCell);
SetNotConnectedColor();
})
</script>
首先,将graph
设置为全局变量。 (var graph = new mxGraph(container, model);
)
然后,从 graph
获取所有 cells
(作为对象)和 graphView
并迭代所有单元格以检查它是否已连接。
function Validate(){
let cells = graph.getModel().cells;
let graphView = graph.getView();
for(let key in cells){
if(!cells.hasOwnProperty(key)) continue;
let mxCell = cells[key];
if(!mxCell.isVertex() && !mxCell.isEdge()) continue;
let state = graphView.getState(mxCell);
ResetColor(state);
let notConnected = true;
if(mxCell.isVertex()){
for(let i=0;i<mxCell.getEdgeCount();i++){
let edge = mxCell.getEdgeAt(i);
// if any of an edge connected to both source and target, it's connected
if(edge.source !== null && edge.target !== null){
notConnected = false;
break;
}
}
}
else { // mxCell.isEdge()
notConnected = mxCell.source === null || mxCell.target === null;
}
if(notConnected) SetNotConnectedColor(state);
}
}
function ResetColor(state){
state.shape.node.classList.remove("not_connected");
if(state.text)
state.text.node.classList.remove("not_connected");
}
function SetNotConnectedColor(state){
state.shape.node.classList.add("not_connected");
if(state.text)
state.text.node.classList.add("not_connected");
}
document.querySelector("#validate_btn").addEventListener("click", function(){
Validate();
})
And here's the localStorage version code if you want(codepen)
嗨,我遇到 validation
的问题,因为 未连接 edges
到 mxgraph
下图会告诉你我的期望:
问题:每当我按下验证按钮未连接edges
和cells
必须用红色突出显示。
完整视图Codepen:https://codepen.io/eabangalore/pen/pmELpL?editors=1100
注意:请参阅 CODEPEN LINK (https://codepen.io/eabangalore/pen/pmELpL?editors=1100),因为下面的代码不工作
完整代码:
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
function setGraphData(){
var graphState ={"tagName":"mxGraphModel","children":[{"tagName":"root","children":[{"tagName":"mxCell","attributes":{"id":"0"}},{"tagName":"mxCell","attributes":{"id":"1","parent":"0"}},{"tagName":"mxCell","attributes":{"id":"2","value":"A","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"271.56251525878906","y":"82.44792175292969","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"3","value":"B","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"678.2291717529297","y":"106.89236450195312","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"4","value":"Bangalore","parent":"1","edge":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"0.0511","y":"-20","relative":"1","as":"geometry"},"children":[{"tagName":"mxPoint","attributes":{"x":"370.06251525878906","y":"109.95338610053051","as":"sourcePoint"}},{"tagName":"mxPoint","attributes":{"x":"676.7291717529297","y":"128.3869001543523","as":"targetPoint"}},{"tagName":"mxPoint","attributes":{"as":"offset"}}]}]},{"tagName":"mxCell","attributes":{"id":"5","value":"C","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"1013.7847747802734","y":"83.55902862548828","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"6","parent":"1","source":"3","target":"5","edge":"1"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"7","value":"Not Connected","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"552","y":"267.640625","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"8","value":"Edge Not connected must highlight in red color","edge":"1","parent":"1","source":"5"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"},"children":[{"tagName":"mxPoint","attributes":{"x":"740","y":"260","as":"targetPoint"}}]}]}]}]};
localStorage.setItem('graphState',JSON.stringify(graphState));
}
function html2json(html){
if(html.nodeType==3){
return {
"tagName":"#text",
"content":html.textContent
}
}
var element = {
"tagName":html.tagName
};
if(html.getAttributeNames().length>0){
element.attributes = html.getAttributeNames().reduce(
function(acc,at){acc[at]=html.getAttribute(at); return acc;},
{}
);
}
if(html.childNodes.length>0){
element.children = Array.from(html.childNodes)
.filter(
function(el){
return el.nodeType!=3
||el.textContent.trim().length>0
})
.map(function(el){return html2json(el);});
}
return element;
}
function json2html(json){
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node){
if(jsonNode.attributes){
Object.keys(jsonNode.attributes).map(
function(name){
node.setAttribute(name,jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode,node){
if(jsonNode.children){
jsonNode.children.map(
function(jsonChildNode){
json2htmlNode(jsonChildNode,node);
}
);
}
}
var json2htmlNode = function(jsonNode,parent){
if(jsonNode.tagName=="#text"){
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode,node);
addChildren(jsonNode,node);
parent.appendChild(node);
}
addAttributes(json,xmlDoc.firstElementChild);
addChildren(json,xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
function main()
{
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS)
{
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
var graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style)
{
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function()
{
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if(window.localStorage.graphState){
var doc = json2html(JSON.parse(localStorage.graphState));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change',function(){
var codec = new mxCodec();
window.localStorage.graphState = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image)
{
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y)
{
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell)
{
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt)
{
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt)
{
if (img.enabled == false)
{
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
</script>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();" >
</body>
</html>
请帮我提前谢谢!!!
编辑:根据评论,要验证未连接到父单元或其分支的单元,我们可以使用递归函数检查每个连接的边从父单元开始,依此类推。
由于字符数限制,原代码片段被删除。
.not_connected * {
color: red;
font-color: red;
stroke: red;
stroke-color: red;
}
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();">
<button style="position:absolute; left:300px;padding:8px 40px;background:orangered;color:#fff;outline:none;border:none;z-index:100;" id="validate_btn">Validate</button>
</body>
</html>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
var graphStateJSON;
var graph;
function setGraphData() {
var graphState = {
"tagName": "mxGraphModel",
"children": [{
"tagName": "root",
"children": [{
"tagName": "mxCell",
"attributes": {
"id": "0"
}
}, {
"tagName": "mxCell",
"attributes": {
"id": "1",
"parent": "0"
}
}, {
"tagName": "mxCell",
"attributes": {
"id": "2",
"value": "A",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "460",
"y": "80",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "3",
"value": "C",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "460",
"y": "190",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "4",
"edge": "1",
"parent": "1",
"source": "2",
"target": "3"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "5",
"value": "B",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "280",
"y": "190",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "6",
"value": "D",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "663",
"y": "193",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "7",
"edge": "1",
"parent": "1",
"source": "2",
"target": "5"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "8",
"edge": "1",
"parent": "1",
"source": "2",
"target": "6"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "9",
"value": "E",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "660",
"y": "260",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "10",
"edge": "1",
"parent": "1",
"source": "6",
"target": "9"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "11",
"value": "F",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "280",
"y": "260",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "12",
"value": "G",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "459",
"y": "257",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "13",
"edge": "1",
"parent": "1",
"source": "11",
"target": "12"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}]
}]
};
graphStateJSON = JSON.stringify(graphState);
}
function html2json(html) {
if (html.nodeType == 3) {
return {
"tagName": "#text",
"content": html.textContent
}
}
var element = {
"tagName": html.tagName
};
if (html.getAttributeNames().length > 0) {
element.attributes = html.getAttributeNames().reduce(
function(acc, at) {
acc[at] = html.getAttribute(at);
return acc;
}, {}
);
}
if (html.childNodes.length > 0) {
element.children = Array.from(html.childNodes)
.filter(
function(el) {
return el.nodeType != 3 ||
el.textContent.trim().length > 0
})
.map(function(el) {
return html2json(el);
});
}
return element;
}
function json2html(json) {
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node) {
if (jsonNode.attributes) {
Object.keys(jsonNode.attributes).map(
function(name) {
node.setAttribute(name, jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode, node) {
if (jsonNode.children) {
jsonNode.children.map(
function(jsonChildNode) {
json2htmlNode(jsonChildNode, node);
}
);
}
}
var json2htmlNode = function(jsonNode, parent) {
if (jsonNode.tagName == "#text") {
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode, node);
addChildren(jsonNode, node);
parent.appendChild(node);
}
addAttributes(json, xmlDoc.firstElementChild);
addChildren(json, xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
var graph;
var graphView;
var notConnectedCells = [];
var parentCellId = "2";
function main() {
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported()) {
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
} else {
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS) {
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style) {
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function() {
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if (graphStateJSON) {
var doc = json2html(JSON.parse(graphStateJSON));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change', function() {
var codec = new mxCodec();
graphStateJSON = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image) {
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y) {
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell) {
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt) {
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt) {
if (img.enabled == false) {
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
function Validate(mxCell){
let isConnected = true;
// check each cell that each edge connected to
for(let i=0;i<mxCell.getEdgeCount();i++){
let edge = mxCell.getEdgeAt(i);
if(edge.target === null) continue; // no target
if(mxCell.getId() === edge.target.getId()) continue; // target is mxCell itself
isConnected = edge.source !== null && edge.target !== null;
if(isConnected){
// remove source cell if found and so on
let sourceIndex = notConnectedCells.findIndex(c=>c.id === edge.source.getId());
if(sourceIndex !== -1) notConnectedCells.splice(sourceIndex,1);
let targetIndex = notConnectedCells.findIndex(c=>c.id === edge.target.getId());
if(targetIndex !== -1) notConnectedCells.splice(targetIndex,1);
let edgeIndex = notConnectedCells.findIndex(c=>c.id === edge.getId());
if(edgeIndex !== -1) notConnectedCells.splice(edgeIndex,1);
// check next cell and its edges
Validate(edge.target);
}
}
}
function ResetColor(state){
state.shape.node.classList.remove("not_connected");
if(state.text)
state.text.node.classList.remove("not_connected");
}
function SetNotConnectedColor(state){
for(let i=0;i<notConnectedCells.length;i++){
let mxCell = notConnectedCells[i];
let state = graphView.getState(mxCell);
state.shape.node.classList.add("not_connected");
if(state.text)
state.text.node.classList.add("not_connected");
}
}
document.querySelector("#validate_btn").addEventListener("click", function() {
let cells = graph.getModel().cells;
graphView = graph.getView();
notConnectedCells.length = 0;
// create an array of cells and reset the color
for(let key in cells){
if(!cells.hasOwnProperty(key)) continue;
let mxCell = cells[key];
if(!mxCell.isVertex() && !mxCell.isEdge()) continue;
notConnectedCells.push(mxCell);
let state = graphView.getState(mxCell);
ResetColor(state);
}
// starts with the parent cell
let parentCell = notConnectedCells.find(c=>c.id === parentCellId);
Validate(parentCell);
SetNotConnectedColor();
})
</script>
首先,将graph
设置为全局变量。 (var graph = new mxGraph(container, model);
)
然后,从 graph
获取所有 cells
(作为对象)和 graphView
并迭代所有单元格以检查它是否已连接。
function Validate(){
let cells = graph.getModel().cells;
let graphView = graph.getView();
for(let key in cells){
if(!cells.hasOwnProperty(key)) continue;
let mxCell = cells[key];
if(!mxCell.isVertex() && !mxCell.isEdge()) continue;
let state = graphView.getState(mxCell);
ResetColor(state);
let notConnected = true;
if(mxCell.isVertex()){
for(let i=0;i<mxCell.getEdgeCount();i++){
let edge = mxCell.getEdgeAt(i);
// if any of an edge connected to both source and target, it's connected
if(edge.source !== null && edge.target !== null){
notConnected = false;
break;
}
}
}
else { // mxCell.isEdge()
notConnected = mxCell.source === null || mxCell.target === null;
}
if(notConnected) SetNotConnectedColor(state);
}
}
function ResetColor(state){
state.shape.node.classList.remove("not_connected");
if(state.text)
state.text.node.classList.remove("not_connected");
}
function SetNotConnectedColor(state){
state.shape.node.classList.add("not_connected");
if(state.text)
state.text.node.classList.add("not_connected");
}
document.querySelector("#validate_btn").addEventListener("click", function(){
Validate();
})
And here's the localStorage version code if you want(codepen)