You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vaadin Flow v21服务端同步获取/设置TextField光标位置方案咨询

你写的同步获取代码抛出异常,是Vaadin Flow的请求响应机制决定的:executeJs提交的JavaScript指令,只会在当前服务端请求处理完成、响应返回给前端浏览器时才会执行。在当前请求处理过程中阻塞线程等待Future返回会直接死锁——JS指令还没发往前端,不可能拿到执行结果,框架本身也禁止在UI请求处理中阻塞线程。

下面是两种可行的实现方案:

方案1:异步回调实现(官方推荐)

这是最适配Vaadin Flow架构的实现方式,不需要修改原有交互逻辑,仅需将获取位置后的业务逻辑放到回调中执行即可:

public class CustomTextField extends TextField {
    // 定义光标位置返回的回调接口
    @FunctionalInterface
    public interface SelectionPositionCallback {
        void onPositionReceived(int selectionStart, int selectionEnd);
    }

    // 异步获取光标选择位置
    public void getSelectionPosition(SelectionPositionCallback callback) {
        getElement().executeJs(
            "return {start: this.shadowRoot.querySelector('[part=\"value\"]').selectionStart, end: this.shadowRoot.querySelector('[part=\"value\"]').selectionEnd};"
        ).then(jsonValue -> {
            int start = jsonValue.getNumber("start").intValue();
            int end = jsonValue.getNumber("end").intValue();
            callback.onPositionReceived(start, end);
        });
    }

    // 原有设置光标位置方法可直接保留
    public void setSelectionPosition(int pos) {
        getElement().executeJs(
            "this.shadowRoot.querySelector('[part=\"value\"]').setSelectionRange($0, $0);",
            pos
        );
    }
}

使用时把拿到位置后需要执行的逻辑放到回调内即可:

// 示例使用
CustomTextField textField = new CustomTextField();
Button getPosBtn = new Button("获取光标位置", e -> {
    textField.getSelectionPosition((start, end) -> {
        // 此处编写获取到位置后的业务逻辑
        Notification.show("光标起始位置:" + start + ",结束位置:" + end);
    });
});

方案2:同步获取缓存实现

如果业务逻辑确实无法拆分回调,可以通过前端主动上报位置、服务端缓存的方式实现同步读取:

public class CachedPositionTextField extends TextField {
    // 缓存最新的光标位置
    private int cachedSelectionStart = 0;
    private int cachedSelectionEnd = 0;

    public CachedPositionTextField() {
        super();
        // 注册前端事件监听,光标变化时主动上报位置
        getElement().executeJs(
            "const input = this.shadowRoot.querySelector('[part=\"value\"]');" +
            // 监听所有可能修改光标位置的前端事件
            "['select', 'click', 'keyup', 'input'].forEach(evt => {" +
            "  input.addEventListener(evt, () => {" +
            "    $0.$server.updateSelectionPosition(input.selectionStart, input.selectionEnd);" +
            "  });" +
            "});",
            getElement()
        );
    }

    // 供前端调用的服务端方法,更新缓存位置
    @ClientCallable
    public void updateSelectionPosition(int start, int end) {
        this.cachedSelectionStart = start;
        this.cachedSelectionEnd = end;
    }

    // 直接返回缓存的位置,实现同步获取
    public int getSelectionStart() {
        return cachedSelectionStart;
    }

    public int getSelectionEnd() {
        return cachedSelectionEnd;
    }

    // 设置方法不变
    public void setSelectionPosition(int pos) {
        getElement().executeJs(
            "this.shadowRoot.querySelector('[part=\"value\"]').setSelectionRange($0, $0);",
            pos
        );
    }
}

该方案适合光标操作仅通过常规键鼠触发的场景,如果存在自定义JS修改光标位置的情况,补充对应事件监听即可保证缓存准确性。

内容的提问来源于stack exchange,提问作者user1536873

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 16:57:01