javascriptreactjsxmlmxgraph

React / mxGraph: How to render Graph from external source, with functional react component?


I am a beginner here, and trying to wrap my head around mxGraph library. My end-game is to read mxGraphModel from external source and render that visible with mxGraph -library (https://jgraph.github.io/mxgraph/), with functional react component.

But as a stepping stone I am trying to get this example working (https://jgraph.github.io/mxgraph/docs/js-api/files/io/mxCodec-js.html) in the component.

I am also aware that this library is not under maintenance - and there could be alternatives.

I managed to get Hello World -examples working, so Imports and Rendering is working nicely, but I need to read that definition from external XML Variable, or File - not define them invidually.

This is what I currently have.

import React, { useEffect, useCallback } from 'react';
import { StyledTestComponent} from '../styles/TestComponent.styled';
import mxgraph from 'mxgraph';

const {
    mxGraph,
    mxGraphModel,
    // mxDragSource,
    // mxUndoManager,
    // mxCompactTreeLayout,
    // mxGraphHandler,
    // mxGuide,
    // mxEdgeHandler,
    // mxEdgeStyle,
    // mxConstraintHandler,
    // mxEvent,
    // mxImage,
    // mxConstants,
    // mxKeyHandler,
    // mxRubberband,
    // mxPerimeter,
    mxUtils,
    // mxVertexHandler,
    mxClient,
    mxCodec,
} = mxgraph();
  
const DiagramRenderer = () => {
    useEffect(() => {
        console.log('Hello from useEffect');
    }, []);

    const xml =
        '<root><mxCell id="2" value="Hello," vertex="1"><mxGeometry x="20" y="20" width="80" height="30" as="geometry"/></mxCell><mxCell id="3" value="World!" vertex="1"><mxGeometry x="200" y="150" width="80" height="30" as="geometry"/></mxCell><mxCell id="4" value="" edge="1" source="2" target="3"><mxGeometry relative="1" as="geometry"/></mxCell></root>';

    if (!mxClient.isBrowserSupported()) {
        mxUtils.error('Browser is not supported!', 200, false);
    }

    let graph = null;

    const containerRef = useCallback((container) => {
        if (container !== null) {
            let model = new mxGraphModel();
            graph = new mxGraph(container, model); 
            // const parent = graph.getDefaultParent();
            let doc = mxUtils.parseXml(xml); 
            let codec = new mxCodec(doc);

            codec.decode(doc.documentElement, graph.getModel());

            graph.getModel().beginUpdate();

            try {
                //     const v1 = graph.insertVertex(
                //         parent,
                //         null,
                //         'Hello',
                //         100,
                //         100,
                //         300,
                //         100
                //     );
                //     const v2 = graph.insertVertex(
                //         parent,
                //         null,
                //         'World',
                //         500,
                //         100,
                //         300,
                //         100
                //     );
                //     graph.insertEdge(parent, null, '', v1, v2);

                var elt = doc.documentElement.firstChild;
                var cells = [];

                while (elt !== null) {
                    cells.push(codec.decodeCell(elt));
                    elt = elt.nextSibling;
                }

                graph.addCells(cells);
            } finally {
                graph.getModel().endUpdate();
            }
        }
    }, []);

    return (
        <StyledTestComponent>
            <div ref={containerRef}></div>
        </StyledTestComponent>
    );
};

export default TestComponent;

When logging Graph here, it seems that I have the correct stuff (?) there, and if I am correct - only issue is to draw this visible, but I am not completely sure.

enter image description here

Any tip is greatly appreciated!


Solution

  • I managed to get this working, according to this: mxCodec doesn't decode xml correctly - I added

    window['mxGraphModel'] = mxGraphModel;
    window['mxGeometry'] = mxGeometry;
    

    before parse methods and updated my imports

    import mxgraph from 'mxgraph';

    const {
        mxGraph,
        mxGraphModel,
        // mxDragSource,
        // mxUndoManager,
        // mxCompactTreeLayout,
        // mxGraphHandler,
        // mxGuide,
        // mxEdgeHandler,
        // mxEdgeStyle,
        // mxConstraintHandler,
        // mxEvent,
        // mxImage,
        // mxConstants,
        // mxKeyHandler,
        // mxRubberband,
        // mxPerimeter,
        mxGeometry,
        mxUtils,
        // mxVertexHandler,
        mxClient,
        mxCodec,
    } = mxgraph();
    

    So this is the whole functional component, which reads XML and renders that visible to the screen using mxGraph.

    This is the whole solution:

    import React, { useEffect, useCallback } from 'react';
    import { StyledTestComponent } from '../styles/TestComponent.styled';
    import mxgraph from 'mxgraph';
    
    const {
        mxGraph,
        mxGraphModel,
        mxGeometry,
        mxUtils,
        mxClient,
        mxCodec,
    } = mxgraph();
       
    const TestComponent = () => {
        useEffect(() => {
            console.log('Hello from useEffect');
        }, []);
        
        const xml =
            '<root><mxCell id="2" value="Hello," vertex="1"><mxGeometry x="20" y="20" width="80" height="30" as="geometry"/></mxCell><mxCell id="3" value="World!" vertex="1"><mxGeometry x="200" y="150" width="80" height="30" as="geometry"/></mxCell><mxCell id="4" value="" edge="1" source="2" target="3"><mxGeometry relative="1" as="geometry"/></mxCell></root>';
    
        if (!mxClient.isBrowserSupported()) {
            mxUtils.error('Browser is not supported!', 200, false);
        }
    
        let graph = null;
    
        const containerRef = useCallback((container) => {
            if (container !== null) {
                let model = new mxGraphModel();
                graph = new mxGraph(container, model);
                window['mxGraphModel'] = mxGraphModel;
                window['mxGeometry'] = mxGeometry;
                let doc = mxUtils.parseXml(xml); 
                let codec = new mxCodec(doc);
                codec.decode(doc.documentElement, graph.getModel());
                graph.getModel().beginUpdate();
    
                try {
                    var elt = doc.documentElement.firstChild;
                    var cells = [];
                    while (elt !== null) {
                        cells.push(codec.decodeCell(elt));
                        elt = elt.nextSibling;
                    }
                    graph.addCells(cells);
                } finally {
                    graph.getModel().endUpdate();
                }
            }
        }, []);
    
        return (
            <StyledTestComponent>
                <div ref={containerRef}></div>
            </StyledTestComponent>
        );
    };
    
    export default TestComponent;