简体   繁体   中英

vaadin web-component in raw html

vaadin 23.

We are storing html in a database which we need to render within a vaadin component.

The html includes a web component 'text-block'

The text-block can contain nested children:

<p>
<text-block page="PackageSearch" block="empty-search-Sign up for free" class="gimps"> 
    <div class="glimps-tips" id="glimps-tips">
       <h3>Sign up for free</h3> 
       Create a watch and get notifications when a new package version is released.<br>   
    </div>
  </text-block>
<p>

The problem is that when the <text-block> is rendered in the browser the the child nodes are rendered as siblings rather than child nodes:

<p>
<text-block page="PackageSearch" block="empty-search-Sign up for free" class="gimps"> 
</text-block>
<div class="glimps-tips" id="glimps-tips">
   <h3>Sign up for free</h3> 
   Create a watch and get notifications when a new package version is released.<br>   
</div>
</p>

I've tracked the problem down to the fact that the Vaadin Element class uses Jsoup which doesn't like web-components so as part of its 'validation' it re-orders the components.

I believe that vaadin is using `Element.getOuterHtml()` to do the final rendering of the html which calls:
 public String getOuterHTML() { return ElementUtil.toJsoup(new Document(""), this).outerHtml(); }
I've tried creating a class RawHtml:
 import com.vaadin.flow.component.html.Div; public class RawHtml extends Div { private static final long serialVersionUID = 1L; public RawHtml(String html) { getElement().setProperty("innerHTML", html); } public String html() { return getElement().getProperty("innerHTML"); } @Override public String toString() { return html(); } }

I then use this class to add the html to a VerticalLayout

 var body = new VerticalLayout(); body.add(new RawHtml(< html with textblock and nested div as above>);

The result is the same. The div is moved outside of the text-block.

My theory is that vaadin does a final rendering of the html using jsoup before sending it to the browser.

How do I get around this.

Edit: updated question in response to Leif suggestion.

Edit 2:

I've now traced the html back up through the vaadin stack to the call in UidlRequestHandler.commitJsonResponse.

Fragment passed to the json argument to commitJsonResponse has the div correctly nested within the text block:

 {"node":267,"type":"put","key":"innerHTML","feat":1, "value":"<div> <span><p> <a href=\"/Blog#link-in-page\">hi</a> <text-block page=\"PackageSearch\" block=\"empty-search-Sign up for free\" class=\"gimps\">\n <div>\n <div class=\"glimps-tips\" id=\"glimps-tips\">\n <h3>Sign up for free</h3> Create a watch and get notifications when a new package version is released.<br> Publish your own private packages.<br> Access your API documentation online.<br> Share packages with your team.<br> Be more productive<br>\n </div>\n </div>\n </text-block>

So it now looks like my problem isn't server side but rather client side.

Here is a screen capture of what the browser is generating:

在此处输入图像描述

I can also see that the browser has received the correct html:

在此处输入图像描述

So the re-ordering must be happening on the browser.

Does vaadin do any manipulation of the html when adding it to the DOM? This feels unlikely.

It seems to suggest that the browser is doing the re-ordering which would suggest that my web component implementation is wrong?

Edit 3: So my next thought was that my web-component was broken. To validate it I use the this test site: https://stackblitz.com/edit/nested-slots-subkxj?file=my-element.js,index.html,package.json

Entered my web component and proved that it works correctly (the div renders in the slot).

Here is my web component:

 import { html, LitElement } from 'lit'; export class TextBlock extends LitElement { render() { return html` <div class='me'> <slot name="one"></slot> </div> `; } } customElements.define('text-block', TextBlock);

And here is how it was rendered:

在此处输入图像描述

You're not stating which Vaadin component you're using for rendering the HTML content, so I will assume it's the built-in Html component. This component uses JSoup to parse the HTML to be able to set the root tag of the HTML as its own tag name instead wrapping it in another element and then set the rest of the HTML as its own content.

If you want to have an HTML snippet passed to the browser as-is, then you can do that by creating you own wrapper component that sets the raw HTML string as its innerHTML property through the Element API.

public class RawHtml extends Div {
  public void setHtml(String html) {
    getElement().setProperty("innerHTML", html);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM