英文:
reliability of Button "disableOnClick" in Vaadin 14
问题
我正在使用 Vaadin 14,在用户双击按钮时,为了避免多次发送请求,我在 Button
实例上调用了 setDisableOnClick(true)
。
但是对于某些按钮来说,仅此还不够:该按钮仍然可以被多次点击。在 Chrome 开发工具中检查 DOM,我可以看到这些按钮的 disableonclick
属性已设置如下:
我怀疑这可能与调用事件处理程序的顺序有关(应先调用禁用处理程序,然后再调用发送请求的处理程序)。
我不想在服务器端设置布尔值来检查是否已执行操作。是否有其他选项在点击后可靠地立即禁用按钮?
英文:
I'm using Vaadin 14 and to avoid sending requests multiple times when a user double-clicks a button instead of just once, I'm calling setDisableOnClick(true)
on the Button
instance.
For some buttons this is not enough: the button can be still clicked multiple times. When inspecting the dom in Chrome devtools, I can see that the disableonclick
property is set for these buttons:
I suspect it's an issue with the order in which event handlers are called (the one to disable should be called before the one to send the request).
I'd rather not set a Boolean on the server side to check whether the action has been performed. Is there another option to reliably disable the button immediately after clicking?
答案1
得分: 3
这似乎是一个 Vaadin 的 bug。它不起作用的情况是当按钮位于一个被关闭然后重新打开的 Dialog
中。在调用 setDisableOnClick(true)
时,它们会使用一个从服务器端添加事件监听器的方法来进行处理:
/**
* 初始化客户端禁用,以便在点击时禁用,即使服务器端处理需要一些时间。
*/
private void initDisableOnClick() {
if (!disableOnClickConfigured) {
getElement().executeJs("var disableEvent = function () {"
+ "if($0.getAttribute('disableOnClick')){"
+ " $0.setAttribute('disabled', 'true');" + "}" + "};"
+ "$0.addEventListener('click', disableEvent)");
disableOnClickConfigured = true;
}
}
当按钮被分离然后重新连接时,此事件处理程序就会消失。
不幸的是,在重新打开对话框后再次调用 setDisableOnClick(true)
是无效的,因为 disableOnClickConfigured
已经设置为 true,所以此代码将被跳过。
我看到三个选项:
- 当按钮被重新连接(对话框重新打开)时,通过反射将私有字段
disableOnClickConfigured
重置为false
,然后再次调用setDisableOnClick(true)
。 - 将添加客户端事件监听器的代码复制并在按钮被重新连接时执行它。
- 通过替换按钮为新按钮,甚至每次打开时创建一个全新的
Dialog
实例,来避免这个问题。
我选择了最后一个选项。Dialog
是一个 Spring bean,所以我使用了 Prototype 作用域,并使用 ObjectFactory
进行注入,在调用 open
之前获取一个新的实例。
英文:
This seems to be a Vaadin bug. The cases where it doesn't work are when the buttons are in a Dialog
that gets closed and then reopened. When you call setDisableOnClick(true)
, they're using a hack to add the event listener from the server side:
/**
* Initialize client side disabling so disabled if immediate on click even
* if server-side handling takes some time.
*/
private void initDisableOnClick() {
if (!disableOnClickConfigured) {
getElement().executeJs("var disableEvent = function () {"
+ "if($0.getAttribute('disableOnClick')){"
+ " $0.setAttribute('disabled', 'true');" + "}" + "};"
+ "$0.addEventListener('click', disableEvent)");
disableOnClickConfigured = true;
}
}
When the button is detached and re-attached, this event handler is gone.
Unfortunately calling setDisableOnClick(true)
again after re-opening the dialog won't help, since disableOnClickConfigured
will already be set to true so this code will be skipped.
I see three options:
- When the button gets re-attached (dialog gets reopened), reset private field
disableOnClickConfigured
tofalse
through reflection and then callsetDisableOnClick(true)
again. - Copy the code to add the client-side event listener and execute it when the button gets re-attached.
- Avoid the problem by replacing the button with a new one or even create whole a new
Dialog
instance each time it is opened.
It's the last option I chose to go with. The Dialog
was a Spring bean so I'm using Prototype scope and inject it with an ObjectFactory
, then get a new instance before calling open
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论