XML not updating/saving in React Modeler Functional Component (React/TS)

I am trying to update/save the XML via the modeler.saveXML() function when the user updates the bpmn diagram but anytime I grab the xml it is always the original XML file I am using and never what xml equivalent if what is on the screen/user created. I feel like I have tried just about everything and am running out of ideas. I think it is because I have to create the modeler component in use effect, if I try to do it at the beginning of the react functional component the page has an error, so it always getting the original bpmnModeler component’s xml.

I have also tried using modeler.on() to detect a user change, which it does but then why I try to save the xml and look at it it is still the same. I have tried creating the modeler object in useState but it causes the app to crash when I do so.

Any help getting me going in the right direction would be greatly appreciated as I have been stuck on this for the past 3 days.

I have highlighted the more important parts. Here is my code:

const BpmnModelerComponent = () => {
    let modeler: any = null
    // const [modeler, setModeler] = useState(new BpmnModeler({
    //     container: '#bpmnview',
    //     keyboard: {
    //         bindTo: window
    //     }
    // }))

    const [xmlModalIsOpen, setXmlModalIsOpen] = useState(false);
    const [svgModalIsOpen, setSvgModalIsOpen] = useState(false)
    const [xmlText, setXmlText] = useState('')

    const openXmlModal = () => {
        setXmlModalIsOpen(true);
    }

    const closeXmlModal = () => {
        setXmlModalIsOpen(false);
    }

    const exportArtifacts = (modeler: any) => {
        saveModelerXml(modeler);
        console.log('xml saved')
    }

    const openXMLPanel = (modeler: any) => {
        openXmlModal()
        saveModelerXml(modeler)
    }

    const saveModelerXml = async (modeler: any) => {
        try {
            const result = await modeler.saveXML();
            const { xml } = result;
            getXml(result)
            console.log(xml);
          } catch (err) {
            console.log(err);
          }
    }

  **const getXml = (model: any) => {
        const { xml } = model
        console.log('xml = ' + xml)
        setXml(xml)
    }

    const setXml = (xml: string) => {
        setXmlText(xml)
        console.log(xmlText)
    }

    const downloadXml = async (modeler: any, xml: string) => {
        encodeURIComponent(xmlText)
    }

  **useEffect(() => {
        console.log('use effect')
        let newModeler = new BpmnModeler({
            container: '#bpmnview',
            keyboard: {
                bindTo: window
            }
        })
        newBpmnDiagram(modeler)
    })

  useEffect(() => {
        'here'
        modeler.on('commandStack.changed', () => {
            console.log('user changed something')
            // modeler.moddle.tomXML(modeler.definitions, { format: true }, function (err, updatedXML) {
            //     console.log('xml stuff')
            // })
            // modeler.saveXML({ format: true }, function(err, updatedXML) {
            //     console.log(updatedXML)
            // })
            saveModelerXml(modeler)
          });
        }, [])

    const newBpmnDiagram = (modeler: any) => {
        openBpmnDiagram(modeler, emptyBpmn)
    }

    const openBpmnDiagram = async (modeler: any, xml: any) => {
        try {
            const result = await modeler.importXML(xml);
            const { warnings } = result;
            console.log(warnings);
          } catch (err) {
            console.log(err.message, err.warnings);
          }
            let canvas = modeler.get('canvas')

            canvas.zoom('fit-viewport')
    }
    
        return(
                <div id="bpmncontainer" style={{height: '80%'}}>
                    <div id="bpmnview" style={{ width: '100%', height: '120vh', float: 'left', overflowX: 'auto' }}></div>
                    <Button variant='outlined' style={buttonXML} onClick={() => {openXMLPanel(modeler)}}>XML</Button>
                    <Button variant='outlined' style={buttonSVG} >SVG</Button>
                    <Button variant='outlined' style={buttonSave} onClick={() => {saveModelerXml(modeler)}}>Save XML</Button>
                    <Modal
                    open={xmlModalIsOpen}
                    onClose={closeXmlModal}
                    aria-labelledby="modal-modal-title"
                    aria-describedby="modal-modal-description"
                >
                    <Box sx={style}>
                        <h3 style={{ position: 'absolute', color: 'black', alignContent: 'flex-start', top: '5%', left: '5%'}}>XML</h3>
                        <Typography id="modal-modal-description" sx={{ position: 'absolute', top: '10%', left: '5%', color: 'black', width: '100%' }}>
                            {xmlText}
                        </Typography>
                        <Button variant='contained' style={downloadButtonXML} onClick={() => {downloadXml(modeler, xmlText)}}>Download XML</Button>
                    </Box>
                </Modal>
                </div>
                
        )
};
const setXml = (xml: string) => {
   setXmlText(xml)
   console.log(xmlText)

}

Note that setState in React is async. The new value for xmlText will be available on the next re-render of the Component. Try to log result from the saveXML call directly.

Here is an example of how we implemented debug mode, which prints the XML with each change:

1 Like

Wow thank you so much. That modeler.on function you posted actually fixed it. I tried that multiple times before I posted this too although the syntax was a little different so not exactly sure what changed.

For anyone who also has this problem I moved that modeler.on function back into useEffect where I am also instantiating the modeler object.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.