Enhance Vaadin 14: Automatically adjust TextArea size when window is resized

Using Vaadin 14.1.19 in a project called "My Starter Project," I attempted to create a TextArea that supports multiple lines. Initially, everything seemed fine, but upon resizing the TextArea, it failed to adjust the number of visible lines. Here is the code snippet:

package com.packagename.myapp;
import org.springframework.beans.factory.annotation.Autowired;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.PWA;

@Route(layout = DesktopLayout.class)
@PWA(name = "Project Base for Vaadin Flow with Spring", shortName = "Project Base")
public class MainView extends VerticalLayout {
    public MainView(@Autowired MessageBean bean) {
        String loremIpsum = "Lorem ipsum dolor sit amet, [....].";
        
        TextArea readOnlyTA = new TextArea();
        readOnlyTA.setLabel("Read-only");
        readOnlyTA.setWidth("1500px");
        readOnlyTA.setMaxWidth("80vw");
        readOnlyTA.setValue(loremIpsum);
        readOnlyTA.setReadOnly(true);
        add(readOnlyTA);

        TextArea readWriteTA = new TextArea();
        readWriteTA.setLabel("normal");
        readWriteTA.setWidth("1500px");
        readWriteTA.setMaxWidth("80vw");
        readWriteTA.setValue(loremIpsum);
        add(readWriteTA);

        Div readOnlyDiv = new Div();
        readOnlyDiv.setWidth("1500px");
        readOnlyDiv.setMaxWidth("80vw");
        readOnlyDiv.add(loremIpsum);
        add(readOnlyDiv);
    }
}

Upon initially opening the view with a wide window, it appears as expected:

https://i.sstatic.net/c7XB8.png

However, resizing the window results in only the beginning of the text being readable within the TextArea components, without scroll functionality.

https://i.sstatic.net/hdr6S.png

Strangely, only the DIV resizes as anticipated.

Is there a way to ensure that Vaadin's TextArea adapts to window resizes?

Answer №1

It appears that a bug has been identified and reported at this GitHub link

To address this issue, one workaround is to establish a window resize listener that executes textArea._updateHeight(); on the client side to prompt the height adjustment. Alternatively, utilizing a ResizeObserver could be an option, bearing in mind its browser support may have limitations as indicated here.

Here's a simple illustration to implement a resize listener workaround using Flow:

textArea.getElement().executeJs(
    "window.addEventListener('resize', function() { $0._updateHeight(); });",
    textArea.getElement());

It is advisable to envelop this resize handler within a basic debouncer to prevent frequent execution of _updateHeight() during resizing events and evade possible performance concerns.

Another approach would involve:

UI.getCurrent().getPage().addBrowserWindowResizeListener(event -> {
    textArea.getElement().executeJs("this._updateHeight();");
});

The presence of any built-in debouncing functionality in addBrowserWindowResizeListener is uncertain but it should ideally reduce server round trips triggered by the resizing activity.

Edit:

A more universal technique entails creating a new component that extends Vaadin's TextArea, leveraging ResizeObserver in compliant browsers alongside a fallback mechanism involving setInterval for other browsers.

import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.textfield.TextArea;

public class CustomTextArea extends TextArea {
    private boolean initDone = false;

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        super.onAttach(attachEvent);
        if (!initDone) {
            // debounce method borrowed from: https://davidwalsh.name/essential-javascript-functions
            getElement().executeJs(
                    "const debounce = function(func, wait, immediate) {" +
                    "  var timeout;" +
                    "  return function() {" +
                    "    var context = this, args = arguments;" +
                    "    var later = function() {" +
                    "      timeout = null;" +
                    "      if (!immediate) func.apply(context, args);" +
                    "    };" +
                    "    var callNow = immediate && !timeout;" +
                    "    clearTimeout(timeout);" +
                    "    timeout = setTimeout(later, wait);" +
                    "    if (callNow) func.apply(context, args);" +
                    "  };" +
                    "};" +
                    "const textArea = $0;" +
                    "const updateTextAreaHeight = function() { textArea._updateHeight(); };" +
                    "const debounceTimeout = 50;" +
                    "const intervalTimeout = 500;" +
                    "" +
                    "if (window.ResizeObserver) {" +
                    "  let textAreaDebouncer;" +
                    "  const resizeObserver = new ResizeObserver(debounce(updateTextAreaHeight, debounceTimeout));" +
                    "  resizeObserver.observe(textArea);" +
                    "} else {" +
                    "  let textAreaWidth = textArea.clientWidth;" +
                    "  window.setInterval(function() {" +
                    "    if (textAreaWidth !== textArea.clientWidth) {" +
                    "      updateTextAreaHeight();" +
                    "      textAreaWidth = textArea.clientWidth;" +
                    "    }" +
                    "  }, intervalTimeout);" +
                    "}", getElement());
            initDone = true;
        }
    }
}

Simply substitute CustomTextArea for TextArea to implement this solution successfully.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Illumination scope for directional lights in three.js

In the world of three.js, calculating shadows for directional lights involves setting a range based on a bounding box that extends from the light source. This means that if I want to limit how far shadows are rendered, I need to adjust the bounding box geo ...

What is the best method for testing routes implemented with the application router in NextJS? My go-to testing tool for this is vitest

Is it possible to test routes with vitest on Next.js version 14.1.0? I have been unable to find any information on this topic. I am looking for suggestions on how to implement these tests in my project, similar to the way I did with Jest and React Router ...

When creating routes in Express 4.* using node.js, it is essential to use the root directory

Completely new to the world of node.js, I find myself navigating through an outdated and partially functioning course on udemy.com. In previous modules, I managed to successfully receive content through routes like app.get('/vegetables',functio ...

Adjust the size of an IFRAME to eliminate scrollbars from appearing

My webpage features an iframe that needs to be resized dynamically each time its contents change in order to eliminate scrollbars. The content inside the iframe frequently changes without altering the URL. I desire for all the content within the frame to b ...

Preventing the Spread of JavaScript Promises

Consider a scenario where there is a promise chain structured as shown below. The goal is to prevent func3 or func4 from being called when func2 is invoked. AsyncFunction() .then(func1, func2) .then(func3, func4) Currently, throwing an error in func2 res ...

Generate fresh JavaScript objects with customized properties

My goal is to use Javascript and JQuery to automatically create a new object with properties provided by the user when they fill out an HTML form. I have a constructor named "object" for this purpose. function object (prop1, prop2, prop3) { this.p ...

Managing Emails with Vue and Firestore

I am facing an issue with updating the 'email' field. Whenever I try to change the email address, it gets updated correctly. However, when I attempt to log in again, the new email address does not work; only the old one seems to be functional. Ho ...

"Arranging elements with identical class in a single column but on separate rows using CSS grid - a step-by-step guide

I'm facing an issue with organizing choices and matches in columns using grid CSS. Currently, the match column is overlapping with the choice column. Here is how it is coded at the moment: .grid{ display:grid; grid-template-columns: 1fr 1 ...

When json.parse encounters an undefined value at position 1

Currently, I am diving into learning express.js and my latest project involves creating a web app that can convert cryptocurrency to Fiat currency. Things have been progressing smoothly so far, but I've hit a roadblock when attempting to use json.pars ...

Error with Cross-Origin Resource Sharing (CORS) on my website

During the development of a website, I disabled web security in order to bypass CORS using the command chrome.exe --disable-web-security --user-data-dir=/path/to/foo However, after successfully completing the website and uploading it to my domain, I enco ...

Basic JavaScript string calculator

I'm in the process of creating a basic JavaScript calculator that prompts the user to input their name and then displays a number based on the input. Each letter in the string will correspond to a value, such as a=1 and b=2. For example, if the user e ...

What is the best way to eliminate query parameters in NextJS?

My URL is too long with multiple queries, such as /projects/1/&category=Branding&title=Mobile+App&about=Lorem+ipsum+Lorem+. I just want to simplify it to /projects/1/mobile-app. I've been struggling to fix this for a week. While I found so ...

Ways to dynamically emphasize text within ngFor loop

Within my ngFor loop, I have a set of rows. <div *ngFor="let block of data;"> <div class="class-row"> <div class="left">A Label:</div> <div class="right">{{block.key1}}</div> </div> <div class="clas ...

Creating code that is easily testable for a unique test scenario

My function, readFile(path, callback), is asynchronous. The first time it reads a file, it retrieves the content from the file system and saves it in memory. For subsequent reads of the same file, the function simply returns the cached content from memor ...

Incorporating an express server into the vue-webpack-boilerplate for enhanced functionality

Recently, I got my hands on the vue-webpack-boilerplate from GitHub, and so far, it seems pretty impressive! This is my first time working with webpack and ESlint, so I'm eager to learn more. However, I have a question about integrating an express ba ...

Using d3 to showcase pictures sourced from a csv file

Having recently embarked on a journey to learn javascript, d3, and the polymer project, I am facing a challenge that I hope to get some guidance on. After successfully parsing a csv file containing image information and creating an array specifically for ...

Creating a Stylish Funnel Graph using CSS

I am currently working on customizing a funnel chart based on data from my database that is displayed on the page. Everything is functioning correctly except for the CSS rendering of the chart. <ul id="funnel-cht"> <li style="height:70px;widt ...

How to resolve undefined callback when passing it to a custom hook in React Native

I'm facing an issue with passing a callback to my custom hook: export default function useScreenshotDetection(onScreenshot) { useEffect(() => { ... onScreenshot(); ... }, []); } Strangely, the callback is not being detected ...

Employ an asynchronous immediately-invoked function expression within the callback

Can an asynchronous IIFE be used inside the callback function to avoid the error message "Promise returned in function argument where a void return was expected"? You can find an example here. signIn(email: string, password: string, course?: ICourse): ...

What's the CSS equivalent of Java's window.pack() method?

I'm relatively new to css and I'm attempting to create a border around a <div>. My goal is for the border to be limited to the size of the elements inside the div and adjust dynamically in proportion to any new objects that may appear or di ...