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
相关产品推荐
相关产品推荐

