RichTextEditor syncs full Delta on every typing pause, ignoring ValueChangeMode

Hi all, we’ve run into something with RichTextEditor and would like to check whether it’s intended.

RichTextEditor seems to send its whole Delta value to the server after every pause in typing, even when the application only uses the HTML value and never calls asDelta(). With inline images the Delta is large, and on a slow connection the UI waits for minutes. Three questions:

  1. Is the Delta meant to be synchronised on every change when the application only uses the HTML value?
  2. Is there a supported way to turn the Delta sync off, or to make it follow the value-change mode?
  3. Is there anything else we should set, or a different way to use the component, that avoids this?

setValueChangeMode(...) makes no difference to it (table below). We see it in 24.7.3 and 24.9.13.

Measured (a 0.75 MB body with 10 inline images, typing 40 characters at normal speed, then clicking a button):

Set-up Requests Uploaded
Default (no value-change mode set) 9 6.77 MB
ValueChangeMode.ON_BLUR 9 6.77 MB
ValueChangeMode.EAGER 9 12.03 MB

With the upload throttled to 80 KB/s, the first sync (770 KB) took 9.7 s. While it was uploading, 7 more queued and went with the button click as one 6.2 MB request, which took 78 s. The UI was unresponsive until it finished. Setting the value from the server with asHtml().setValue(...) also sends one full copy of the Delta back, before anything is typed.

Minimal example (about 0.94 MB of images: 9 requests and 8.27 MB for 40 characters; at 80 KB/s, the last request was 7.5 MB and took 95 s):

@Route("")
public class RteView extends VerticalLayout {

    public RteView() throws IOException {
        RichTextEditor editor = new RichTextEditor();
        StringBuilder html = new StringBuilder("<p>Type here</p>");
        for (int i = 0; i < 10; i++) {
            html.append("<p><img src=\"data:image/png;base64,")
                    .append(noisePng(200, 120)).append("\"></p>");
        }
        editor.asHtml().setValue(html.toString());
        Button send = new Button("Send",
                e -> Notification.show("HTML length: " + editor.getHtmlValue().length()));
        add(editor, send);
    }

    private static String noisePng(int w, int h) throws IOException {
        BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Random r = new Random();
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                img.setRGB(x, y, r.nextInt(0xFFFFFF));
            }
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(img, "png", out);
        return Base64.getEncoder().encodeToString(out.toByteArray());
    }
}

Open the browser’s developer tools on the Network tab, filtered to uidl, and type a few words with pauses. Each pause sends an mSync of value (the full Delta, images included) with a value-changed event.

Vaadin 24.9.13, Java 17, Spring Boot 3.5.0, Chrome.

Thanks in advance for any pointers.

Thanks for the report. This is not really intentional and mostly due to a flawed implementation to make the delta value sync work. I opened a PR that should address it: fix: sync RTE Delta value only when asDelta is used by sissbruecker · Pull Request #10623 · vaadin/flow-components · GitHub

It also seems weird that the same attached images would be sent over the network again and again in an application that actually uses the delta values. Is that also a bug that should be addressed somehow?

Thanks Sascha, that’s great, and thanks for the quick PR. We only use the HTML value, so that change covers our case.

We’re on Vaadin 24 (24.9.x). Is there any chance of the fix being backported to 24?

In the meantime we’re using this workaround, which stops the Delta sync on the client:

editor.addAttachListener(e → editor.getElement().executeJs(
“this.addEventListener(‘value-changed’, ev => ev.stopImmediatePropagation(), {capture: true})”));

With it, the same test as above (40 characters, then the button) goes from 9 requests and 6.77 MB to 1 request and 0.75 MB: only the HTML is sent, on blur, and the button click still gets the full text. Two questions about it:

  1. Is it safe on 24, or does anything in the component rely on the server receiving the Delta?
  2. Once we’re on a version with your fix, should we take it out?

@Leif, we only use the HTML value, so we haven’t measured the Delta case. One thing we did notice: the HTML value carries the inline images too, so whenever the HTML is synced the images go with it.

Ok, I misread the description there. Seems like it would be even more worthwhile to avoid syncing inline images back over and over in that case…

I can take a look at back-porting, I’d assume not much has changed in that area but I’m not sure. I assume you have extended maintenance? Otherwise you would not be able to use newer patch versions.

The workaround should not be necessary afterwards.