Yew: SVGs have size 0x0

Created on 19 Jul 2018  路  9Comments  路  Source: yewstack/yew

I'm using the latest git version of yew, my main component just renders this:

        html! {
            <svg width="500", height="400", viewBox="0 0 800 500", preserveAspectRatio="none",>
                <rect x="0", y="0", width="400", height="500", fill="none", stroke="yellow", stroke-width="2", />
                <rect x="0", y="0", width="800", height="500", style="fill: none; stroke: black; stroke-width: 2;", />
                <rect x="0", y="0", width="300", height="100", style="fill: blue;", />
                <rect x="10", y="200", width="300", height="100", style="fill: red;", />
            </svg>
        }

Here you can see it working in a fiddle.
But when I render it with yew, the svg has width 0 and height 0, and when I use chrome dev tools to edit the width and height, I see that all the rect's also have size 0x0.

Btw, when I right-click on the svg node in the "Elements" view in dev tools, then "Edit as HTML", then add a blank line at the end (to make it cause a re-render), then hit ctrl-enter to save, it renders the SVG with its correct size, like in the fiddle!

Why isn't it rendered with correct size by yew? :)

Most helpful comment

Any news to this? What would be a possible way to make !htmlwork with svg? I am new to rust+yew and want to demonstrate it's usage by a small data logging app where i definitely want to use svg+events on it (onmouseover etc.).

All 9 comments

Sorry for the low effort reply, but I was able to get svgs working with the following code:

// load the svg file into the compiled binary via include_str!()
const LOADING_SVG: &'static str = include_str!("../inlined_assets/Loading.svg"); 

struct LoadingIcon (&'static str);
impl<U> Renderable<U> for LoadingIcon
    where
        U: Component
{
    fn view(&self) -> Html<U> {
        let js_svg = js! {
            var div = document.createElement("div");
            div.innerHTML = @{self.0.to_string()};
            return div;
        };
        let node = Node::try_from(js_svg).expect("convert js_svg");
        let vnode = VNode::VRef(node);
        vnode
    }
}

As far as I'm aware, the html! macro doesn't work well with svg, instead you need to do the above to get it to work. Maybe @DenisKolodin could explain more.

Yes, I was also doing the above and it worked, but I thought it should also work with the way I wrote it with tags in html!{}, right?
Or am I doing anything wrong in the way I'm writing the svg tags there?

I'm having the same problem. Using the js! macro does not work for my case since I want to change the fill color dynamically.

@tiberiusferreira I'm generating the whole SVG dynamically, with a rect for every midi note in the midi clip I'm rendering. Just use a function to compute the SVG String before you embed it in the js!{} (and pass the relevant data (e.g. fill color) as args) :)
(Or just inline that computation if it's readable enough.)

@Boscop This is the way I use svg. Just thought I'd share.

use stdweb::web::{INode, Node, IElement, Element};
use stdweb::unstable::TryFrom;
use yew::virtual_dom::VNode;

impl Renderable<Model> for Model {
    fn view(&self) -> Html<Self> {
       // This could be moved into a function
        let svg = Element::try_from(js! {
            return document.createElementNS("http://www.w3.org/2000/svg", "svg");
        }).unwrap();

        svg.set_attribute("style", "border: 1px solid black").unwrap();
        svg.set_attribute("width", "600").unwrap();
        svg.set_attribute("height", "250").unwrap();

        let circle = Element::try_from(js! {
            return document.createElementNS("http://www.w3.org/2000/svg", "circle");
        }).unwrap();

        circle.set_attribute("cx", "50").unwrap();
        circle.set_attribute("cy", "50").unwrap();
        circle.set_attribute("r", "50").unwrap();

        svg.append_child(&circle);

        html! {
            <div>
                {VNode::VRef(Node::from(svg))}
            </div>
        }
    }
}

This is how I'm currently using it:

impl Renderable<Model> for Model {
    fn view(&self) -> Html<Self> {
        let svg = self.generate_svg_as_string();
        let js_svg = js! {
            var div = document.createElement("div");
            div.innerHTML = @{svg};
            div.style.width = "100%";
            div.style.height = "100%";
            return div;
        };
        let node = Node::try_from(js_svg).expect("convert js_svg");
        let vnode = VNode::VRef(node);
        html! {
            <div class="song_view",>
                <div style="position: relative;",>
                    <div class="song_name",>{ &self.song_name }</div>
                </div>
                { vnode }
            </div>
        }
    }
}

image

Any news to this? What would be a possible way to make !htmlwork with svg? I am new to rust+yew and want to demonstrate it's usage by a small data logging app where i definitely want to use svg+events on it (onmouseover etc.).

These hacks are a bit awkward and it seems like it breaks the dom reconciliation stuff. I have a function that uses document.createElementNS("http://www.w3.org/2000/svg", "svg") to create an svg element and return it as an yew::Html<_>, but the svg element in the dom is destroyed and recreated every time the component changes even though the svg doesn't.

I assume I can work around this by having a component for svgs so yew can tell that the svg component hasn't changed and not ask for an update; but even if that works this is like bandaging a bandage. :(

Was this page helpful?
0 / 5 - 0 ratings

Related issues

djahandarie picture djahandarie  路  3Comments

agausmann picture agausmann  路  3Comments

thienpow picture thienpow  路  3Comments

wldcordeiro picture wldcordeiro  路  4Comments

alun picture alun  路  4Comments