How to incorporate D3 svg elements into mithril.js?

Tags: , , ,



I have been working with a framework using mithril.js and wish to add this script that draws an interactive tree into a component on my website.

So far, I just placed the JS code inside the script tags into my application, and I know that normally, the DOM object generated is obtained by calling svg.node(). But I know I can’t return that from my object’s view method, and instead need to return something of the form m(...). I tried looking at sources like this one but couldn’t find a solution to my problem in them. Is there a known way for incorporating D3 SVG graphics into mihtril.js?

If you wanted to look at the code I have right now:

export default class TreeModal extends Modal {
    content() {

        var treeData = ... // some data

        ... // Other code; irrelevant to this question
        
        var svg = d3.select("body").append("svg")
            .attr("width", width + margin.right + margin.left)
            .attr("height", height + margin.top + margin.bottom).append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
        
        // Some other code adding more functionality

        // At this point, svg contains the SVG object which I wish to display
        return svg.node(); // Raises ILLEGAL CONSTRUCTOR error; how to adapt this for Mithril.js?
    }
}

Thanks in advance for all the help!

UPDATE: It seems like what a Modal is is crucial to my problem, because the API I use actually asks me to implement the content() method in any subclasses of Modal. I looked in Modal.js, and the relevant bit is:

export default class Modal {
    view() {
        return (
        <div>
            ...
            {this.content()}
            ...
        </div>
        )
    }
}

Ideally, the solution would not have to override the view() method of Modal and only would include changes to content() in TreeModal.

Answer

It is really hard to write correct code without knowing the implementation or documentation of the base Modal class. To work around the API you have mentioned we can render a div in content with a random id that we use later to find it after it is placed into the DOM tree. Then inject the SVG into that div and use D3 as you normally would. I’m not sure if D3 needs cleanup but that would be done in onremove, again make sure to call the parents onremove if necessary.

I use JSX in content but I wasn’t able to test its format.

export default class TreeModal extends Modal {
    constructor() {
        // @TODO: Not sure what Modal's constructor looks like.
        super();
        // Create a random DOM id we share between content() 
        //and oncreate().
        this.svgContainerId = 'svg_container_' + 
            Math.floor(Math.random()*1000000000) + 1;
    }
    oncreate(vnode) {
        // @TODO: Check if Modal implements this method or not.
        // super.oncreate(vnode);

        var treeData = {} // some data

        ... // Other code; irrelevant to this question
        
        // USE our predefined id to lookup the div rendered in content
        // and inject our SVG into that container.
        var svg = d3.select(document.getElementById(this.svgContainerId)).append("svg")
            .attr("width", width + margin.right + margin.left)
            .attr("height", height + margin.top + margin.bottom).append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
        
        // Some other code adding more functionality
    }
    content() {
        // render a plain DIV with our predefined id that never changes.
        // this JSX is untested
        return (<div id="{this.svgContainerId}"/>);
    }
}


Source: stackoverflow