Get element SVG child as text in Vaadin

Viewed 22

I have an SVG element created using javascript inside a div with a specific ID. My primary goal would be to get the SVG image to let the user download it.

So my idea, which may absolutely be wrong, was to access the Div element (I'm using Vaadin 23), with container.getElement(), get the child element, which is my SVG and do svgElement.getOuterHTML() to obtain the string and save it somewhere. I can see the element and I know it's there, but when I print my container element from vaadin it doesn't have any children.

My situation:

<div class="chart-container" id="chart-div-02250ca9-3b1b-4d09-aeb2-f38c4c797fc9">
    <div>
        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="group" style="width: 100%; height: 100%; overflow: visible;">
        ...
        </svg>
    </div>
</div>

What I get from content.getElement().getOuterHTML(), where content is my div with the ID:

<div id="chart-div-02250ca9-3b1b-4d09-aeb2-f38c4c797fc9" class="chart-container">
</div>

What am I missing?

If this is the expected behaviour, how would you go about getting the SVG element? I tried with JS and then returing the result to Java but it was a real mess with Futures and all than synchronous stuff.

EDIT (adding clarification on final goal): The final goal is as follows: I want to add a feature where the user can download the rendered SVG as an image. But the thing is that I "build" the SVG at runtime, so I can't save it somewhere before the execution.

1 Answers

Vaadin does not automatically sync child elements created client side with JavaScript or the template content to the server. This is simply because adding such implementation would be really problematic from performance point of view, while it is very rarely needed.

What you can do, is to implement your component as LitTemplate. That allows you to inject selected elements from the template using @Id annotation

@JsModule("./my-template.ts")
@Tag("my-template")
public class MyTemplate extends LitTemplate {

@Id("svg")
Element svgElement;

...
}

While the "frontend/my-template.ts" is something like

import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('my-template')
export class MyTemplate extends LitElement {

render() {
   return html`
<div class="chart-container" id="chart-div-02250ca9-3b1b-4d09-aeb2-f38c4c797fc9">
    <div>
        <svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="group" style="width: 100%; height: 100%; overflow: visible;">
        ...
        </svg>
    </div>
</div>';
}
}

Note however, there is no deeper support for Svg elements in Vaadin, so I am not sure what you can achieve with this.

So eventually the question is, why you need it? And answer to that question may reveal other avenues for you.

Edit: As it turned out that you want to programmatically compose SVG and then download it, the answer to the original question wont help. Another kind of methods needs to be used.

One possible avenue is to compose SVG server side using String fragments. Here is an example of random polyline. With this approach you have the SVG data in server side all the time, and you can hold it in a String, which you can download easily.

Html createChart(String color) {
    Random random = new Random();
    List<Integer> data = random.ints(300, -100, 100).boxed()
            .collect(Collectors.toList());
    String svg = "<div><svg class=\"uk-animation-stroke\" style=\"width: 100%; height: 100%; --uk-animation-stroke: 100000;\" preserveAspectRatio=\"none\" viewBox=\"0 -100 600 200\"><polyline points=\"";
    int index = 0;
    for (int number : data) {
        svg += index + "," + number + " ";
        index += 2;
    }
    svg += "\" style=\"stroke-width: 1;fill:none;stroke:" + color
            + "\"></polyline></svg></div>";
    Html chart = new Html(svg);
    chart.getElement().getStyle().set("height", "100px");
    return chart;
}
Related