寻求Angular中使用JSON渲染mxGraph图形的实现方案
在Angular中结合mxGraph实现JSON图形序列化与渲染
我正尝试在Angular应用中结合mxGraph使用JSON来序列化图形并赋予其功能,这应该是在Angular中实现图形序列化与功能拓展的优质方案,但一直没找到从Angular渲染JSON格式图形的最简示例。
在原生JavaScript的mxGraph示例中,仅通过JSON结构即可渲染图形:
示例JSON结构
{ "graph": [ { "value": { "name": "Daffy Duck" }, "geometry": { "x": 90, "y": 60, "width": 120, "height": 30, "relative": false, "TRANSLATE_CONTROL_POINTS": true, "alternateBounds": null, "sourcePoint": null, "targetPoint": null, "points": null, "offset": null }, "id": "2", "vertex": true, "connectable": true, "parent": "1", "source": null, "target": null, "edges": [ { "value": "Edge", "geometry": { "x": 0, "y": 0, "width": 0, "height": 0, "relative": true }, "id": "4", "edge": true, "parent": "1", "source": "2", "target": "3", "mxObjectId": "mxCell#7" } ], "mxObjectId": "mxCell#6" }, { "value": { "name": "Bugs Bunny" }, "geometry": { "x": 1020, "y": 60, "width": 120, "height": 30, "relative": false, "TRANSLATE_CONTROL_POINTS": true, "alternateBounds": null, "sourcePoint": null, "targetPoint": null, "points": null, "offset": null }, "id": "3", "vertex": true, "connectable": true, "parent": "1", "source": null, "target": null, "edges": [ { "value": "Edge", "geometry": { "x": 0, "y": 0, "width": 0, "height": 0, "relative": true }, "id": "4", "edge": true, "parent": "1", "source": "2", "target": "3", "mxObjectId": "mxCell#7" } ], "mxObjectId": "mxCell#8" }, { "value": "Edge", "geometry": { "x": 0, "y": 0, "width": 0, "height": 0, "relative": true }, "id": "4", "edge": true, "parent": "1", "source": "2", "target": "3", "mxObjectId": "mxCell#7" }, { "value": { "name": "Elmer Fudd" }, "geometry": { "x": 90, "y": 220, "width": 120, "height": 30, "relative": false, "TRANSLATE_CONTROL_POINTS": true, "alternateBounds": null, "sourcePoint": null, "targetPoint": null, "points": null, "offset": null }, "id": "5", "vertex": true, "connectable": true, "parent": "1", "source": null, "target": null, "edges": [ { "value": "Edge", "geometry": { "x": 0, "y": 0, "width": 0, "height": 0, "relative": true }, "id": "7", "edge": true, "parent": "1", "source": "5", "target": "6", "mxObjectId": "mxCell#10" } ], "mxObjectId": "mxCell#9" }, { "value": { "name": "Tasmanian Devil" }, "geometry": { "x": 1020, "y": 220, "width": 120, "height": 30, "relative": false, "TRANSLATE_CONTROL_POINTS": true, "alternateBounds": null, "sourcePoint": null, "targetPoint": null, "points": null, "offset": null }, "id": "6", "vertex": true, "connectable": true, "parent": "1", "source": null, "target": null, "edges": [ { "value": "Edge", "geometry": { "x": 0, "y": 0, "width": 0, "height": 0, "relative": true }, "id": "7", "edge": true, "parent": "1", "source": "5", "target": "6", "mxObjectId": "mxCell#10" } ], "mxObjectId": "mxCell#11" }, { "value": "Edge", "geometry": { "x": 0, "y": 0, "width": 0, "height": 0, "relative": true }, "id": "7", "edge": true, "parent": "1", "source": "5", "target": "6", "mxObjectId": "mxCell#10" } ] }
原生JS渲染代码
/****************************************************************** Demo ******************************************************************/ const graphX = new GraphX(document.getElementById('graphContainer')); document.getElementById('buttons').appendChild(mxUtils.button('From JSON', () => { const dataModel = JSON.parse(document.getElementById('from').innerHTML); graphX.render(dataModel); })); document.getElementById('buttons').appendChild(mxUtils.button('To JSON', () => { const jsonNodes = graphX.getJsonModel(); document.getElementById('to').innerHTML = `<pre>${syntaxHighlight(stringifyWithoutCircular(jsonNodes))}</pre>`; }));
完整原生JS核心代码
/****************************************************************** Core ******************************************************************/ class JsonCodec extends mxObjectCodec { constructor() { super((value)=>{}); } encode(value) { const xmlDoc = mxUtils.createXmlDocument(); const newObject = xmlDoc.createElement("Object"); for(let prop in value) { newObject.setAttribute(prop, value[prop]); } return newObject; } decode(model) { return Object.keys(model.cells).map( (iCell)=>{ const currentCell = model.getCell(iCell); return (currentCell.value !== undefined)? currentCell : null; } ).filter((item)=> (item !== null)); } } class GraphX { constructor(container){ if (!mxClient.isBrowserSupported()) { return mxUtils.error('Browser is not supported!', 200, false); } mxEvent.disableContextMenu(container); this._graph = new mxGraph(container); this._graph.setConnectable(true); this._graph.setAllowDanglingEdges(false); new mxRubberband(this._graph); // Enables rubberband selection this.labelDisplayOveride(); this.styling(); } labelDisplayOveride() { // Overrides method to provide a cell label in the display this._graph.convertValueToString = (cell)=> { if (mxUtils.isNode(cell.value)) { if (cell.value.nodeName.toLowerCase() === 'object') { const name = cell.getAttribute('name', ''); return name; } } return ''; }; } styling() { // Creates the default style for vertices let style = []; style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE; style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter; style[mxConstants.STYLE_STROKECOLOR] = 'gray'; style[mxConstants.STYLE_ROUNDED] = true; style[mxConstants.STYLE_FILLCOLOR] = '#EEEEEE'; style[mxConstants.STYLE_GRADIENTCOLOR] = 'white'; style[mxConstants.STYLE_FONTCOLOR] = 'black'; style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER; style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE; style[mxConstants.STYLE_FONTSIZE] = '12'; style[mxConstants.STYLE_FONTSTYLE] = 1; this._graph.getStylesheet().putDefaultVertexStyle(style); // Creates the default style for edges style = this._graph.getStylesheet().getDefaultEdgeStyle(); style[mxConstants.STYLE_STROKECOLOR] = '#0C0C0C'; style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white'; style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector; style[mxConstants.STYLE_ROUNDED] = true; style[mxConstants.STYLE_FONTCOLOR] = 'black'; style[mxConstants.STYLE_FONTSIZE] = '10'; this._graph.getStylesheet().putDefaultEdgeStyle(style); } getJsonModel(){ const encoder = new JsonCodec(); const jsonModel = encoder.decode(this._graph.getModel()); return { "graph": jsonModel } } render(dataModel) { const jsonEncoder = new JsonCodec(); this._vertices = {}; this._dataModel = dataModel; const parent = this._graph.getDefaultParent(); this._graph.getModel().beginUpdate(); // Adds cells to the model in a single step try { this._dataModel.graph.map( (node)=> { if(node.value) { if(typeof node.value === "object") { const xmlNode = jsonEncoder.encode(node.value); this._vertices[node.id] = this._graph.insertVertex(parent, null, xmlNode, node.geometry.x, node.geometry.y, node.geometry.width, node.geometry.height); } else if(node.value === "Edge") { this._graph.insertEdge(parent, null, 'Edge', this._vertices[node.source], this._vertices[node.target]) } } } ); } finally { this._graph.getModel().endUpdate(); // Updates the display } } } /****************************************************************** Demo ******************************************************************/ const graphX = new GraphX(document.getElementById('graphContainer')); document.getElementById('buttons').appendChild(mxUtils.button('From JSON', () => { const dataModel = JSON.parse(document.getElementById('from').innerHTML); graphX.render(dataModel); })); document.getElementById('buttons').appendChild(mxUtils.button('To JSON', () => { const jsonNodes = graphX.getJsonModel(); document.getElementById('to').innerHTML = `<pre>${syntaxHighlight(stringifyWithoutCircular(jsonNodes))}</pre>`; })); /****************************************** Utils ********************************************/ function stringifyWithoutCircular(json){ return JSON.stringify( json, ( key, value) => { if((key === 'parent' || key == 'source' || key == 'target') && value !== null) { return value.id; } else if(key === 'value' && value !== null && value.localName) { let results = {}; Object.keys(value.attributes).forEach( (attrKey)=>{ const attribute = value.attributes[attrKey]; results[attribute.nodeName] = attribute.nodeValue; } ) return results; } return value; }, 4 ); } function syntaxHighlight(json) { json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*)?:?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { var cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '<span class="' + cls + '">' + match + '</span>'; }); }
请问有没有人知道如何在Angular中实现该功能?我希望能整理相关文档分享给大家。
非常感谢。愿原力与你同在。
注:mxHierarchicalLayout.js类可用于创建图形的层级模型。
我会继续尝试并分享所学内容,若有相关思路,恳请不吝赐教。
内容的提问来源于stack exchange,提问作者Owl
相关产品推荐
相关产品推荐

