Vaadin disable button during an action - vaadin

How can I acheive this in Vaadin.
// inside myButton click event
myButton.setEnabled(false);
doMyActionThatTakeSomeTime();
myButton.setEnabled(true);
Inside the event, the button is never disabled because the UI is not refresh.
What would be the best practice to do this in Vaadin 11 (or 10) ?
force view to refresh ? (how?)
put my action inside a Thread ? (how ?)
Edit SOLUTION - How to make it work with Thread
So far, example with Thread (working):
#Push
#Route(value = "secured")
public class MainView extends VerticalLayout
[ ... ]
// inside click event
UI ui = UI.getCurrent();
new Thread(() -> {
ui.access(() -> {
goButton.setEnabled(false);
ui.push();
});
doMyActionThatTakeSomeTime();
ui.access(() -> {
goButton.setEnabled(true);
ui.push();
});
}).start();

Sounds like the "Asynchronous updates" chapter in the docs explains what you want: https://vaadin.com/docs/v11/flow/advanced/tutorial-push-access.html . Basically: run doMyActionThatTakeSomeTime() in a separate background thread, then re-enable the button once the thread completes and Server Push will make sure the UI state is correctly updated.
This is frequently asked topic, there is also another answer here: How to dismiss Vaadin 8 confirmation dialog while performing lengthy operation Doing asynchronous updates work in Vaadin 8 and Vaadin 10+ in similar manner.

In Vaadin 8, there’s a Button::setDisableOnClick() method for this exact purpose.
That should probably be reintroduced in the newer versions as well.

For me the simplest way is this:
Button btnAction = new Button("Action");
btnAction.setDisableOnClick(true);
btnAction.addClickListener(e -> {
try {
for (int i = 0; i < 900000; i++) {
System.out.println(i);
}
} finally {
btnAction.setEnabled(true);
}
});

A better way of doing it:
UI ui = UI.getCurrent();
ui.access( () -> {
// disable button
goButton.setEnabled(false);
ui.push();
doMyActionThatTakeSomeTime();
// enable
goButton.setEnabled(true);
ui.push();
});

Related

Update UI rendering in React Natives 'focus' Event Listener

I am navigating from different class objects (screens) within my React Native App. Once I navigate to a given screen, I want to execute different functions including Activity Indicators to show that the function is still running.
Therefore is set up a focus Event Listener which is called every time I navigate back to my screen. My current problem is that my Activity Indicators are not updated within the focus Event but directly after, which is not my wanted behavior as I have to wait for my functions to end without any visual indicator.
class Homescreen extends Component {
constructor(props){
super(props};
this.state = {
showIndicator: false;
}
}
componentDidMount(){
this.props.navigation.addListener('focus', this._onFocus);
}
componentWillUnmount(){
this.props.navigation.removeListener('focus', this._onFocus);
}
_onFocus = async () => {
this.setState({showIndicator: true});
asyncFunc();
};
async asyncFunc(){
//calling a function from swift which calls a C function inside DispatchQeue.main.async
//inside my C function I successfully set my ActivityIndicator to false with an Event which calls
//this.setState({showIndicator: false});
}
render(){
console.log(this.state.showIndicator); // correct value is printed while I wait inside the ```focus``` Event to finish, but the UI is not updated.
return(
{this.state.showIndicator && <ActivityIndicator/>}
)
}
}
I receive the correct current status of showIndicatorbut before the focus Event is not finished, my render does not redraw.
To me it seems like the UI won't update until I am out of my focus Event. How can I reach my goal of displaying the ActivityIndicator before the focus Event or how do I call my function right after the focus Event?
I can achieve my wanted behavior by using the blurevent and setting my ActivityIndicator visible while moving to another screen and after moving back I wait until my function is finished and remove the ActivityIndicator but this seems like a bad workaround and also I can see the ActivityIndicator when moving which might be confusing.
You should replace curly braces around this statement {this.state.showIndicator && } and check.
If it doesn't work accordingly then let me know.

Using Threads to Update UI in Vaadin 14

I created a thread (via a lambda expression) to fetch some data based on user input fields but when I try to click on dropdown menus while it is retrieving data I get the mini progress bar indicator. So is a new thread even being created? What am I doing wrong here?
Button doComputation = new Button("Get Results);
doComputation.addClickListener(event -> {
UI ui = UI.getCurrent();
new Thread(() -> {
// Do some work
ui.access(() -> layout.add(results);
}).start();
});
UPDATE: RESOLVED! Unneccesary ui.access calls were made, which locked up resources. Thank you to all that commented and helped.
The code looks correct to me (apart from the missing end parenthesis after ui.access). Is that the only ui.access call, and is that all you do inside it?
I made this example for reference, and the combo box stays responsive while the background task is running.
#Route
public class ThreadView extends VerticalLayout {
public ThreadView() {
Button runThreadButton = new Button("Start thread", e -> {
UI ui = UI.getCurrent();
new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
ui.access(() -> Notification.show("Completed"));
}).start();
});
ComboBox<String> comboBox = new ComboBox<>("Items", "One", "Two", "Three");
add(runThreadButton, comboBox);
}
}
So is a new thread even being created?
You can verify that by adding a System.out.println("Thread started") and checking whether that message is printed, or by running your application in debug mode and setting a breakpoint on the part that you're interested in.
What am I doing wrong here?
The code that you showed looks fine. I would guess there's a problem outside the shown code, namely that you have forgotten that you also need to add the #Push annotation. Without that annotation, the server will have no way of directly sending messages to the client.

Flutter: How to prevent banner ad from showing if view is already disposed

I am currently experimenting with banner ads from the firebase_admob plugin. The process to show and dispose them is pretty straightforward, I do it in initState() and dispose().
The code to create and display the add looks like this:
_bannerAd = createBannerAd();
_bannerAd
..load().then((loaded) {
if (loaded) {
_bannerAd..show();
}
});
However, as I am calling show() asynchronously, it is possible that the view was already closed when the ad is being shown (i.e. by clicking back button really fast). In that case, the dispose() method will never be called and the ad will be "stuck" on the bottom of the screen.
How can I solve this problem? Am I using the banner ad wrong or is it possible to detect if the view was already changed? I tried using the "mounted" property of the state but it didn't seem to work.
Just check "this.mounted" property of state class before showing the add.
_bannerAd = createBannerAd();
_bannerAd
..load().then((loaded) {
if (loaded && this.mounted) {
_bannerAd..show();
}
});
From https://github.com/flutter/flutter/issues/21474#issuecomment-535188820, that's a little hack but it works for me.
You can add a little delay in your dispose method like this:
static void hideBannerAd() {
Future.delayed(Duration(milliseconds: 500), () {
if (_bannerAd != null) _bannerAd.dispose();
_bannerAd = null;
});
}
500 milliseconds is enough.

Vaadin 7 security code placement

In my Vaadin 7 application I have to add Delete button, but this button should be only accessible to an authorized person.
I have added the button with a following code:
if (canRemove()) {
layout.addComponent(createRemoveButton());
}
Also I have added a listener to this button:
button.addClickListener(e -> {
//some logic
});
Do I need to add one more condition inside of this listener:
button.addClickListener(e -> {
if (canRemove()) {
//some logic
}
});
or this condition is redundant and I can avoid it ?
Summarizing the comments on the question:
It's redundant, no button, no click event. Alternative is hiding the button like button.setVisible(isAuthorized(user)) if not authorized.

jquery-ui sortable | How to get it work on iPad/touchdevices?

How do I get the jQuery-UI sortable feature working on iPad and other touch devices?
http://jqueryui.com/demos/sortable/
I tried to using event.preventDefault();, event.cancelBubble=true;, and event.stopPropagation(); with the touchmove and the scroll events, but the result was that the page does not scroll any longer.
Any ideas?
Found a solution (only tested with iPad until now!)!
https://github.com/furf/jquery-ui-touch-punch
To make sortable work on mobile.
Im using touch-punch like this:
$("#target").sortable({
// option: 'value1',
// otherOption: 'value2',
});
$("#target").disableSelection();
Take note of adding disableSelection(); after creating the sortable instance.
The solution provided by #eventhorizon works 100%.
However, when you enable it on phones, you will get problems in scrolling in most cases, and in my case, my accordion stopped working since it went non-clickable. A workaround to solve it is to make the dragging initializable by an icon, for example, then make sortable use it to initialize the dragging like this:
$("#sortableDivId").sortable({
handle: ".ui-icon"
});
where you pass the class name of what you'd like as an initializer.
Tom,
I have added following code to mouseProto._touchStart event:
var time1Sec;
var ifProceed = false, timerStart = false;
mouseProto._touchStart = function (event) {
var self = this;
// Ignore the event if another widget is already being handled
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
return;
}
if (!timerStart) {
time1Sec = setTimeout(function () {
ifProceed = true;
}, 1000);
timerStart=true;
}
if (ifProceed) {
// Set the flag to prevent other widgets from inheriting the touch event
touchHandled = true;
// Track movement to determine if interaction was a click
self._touchMoved = false;
// Simulate the mouseover event
simulateMouseEvent(event, 'mouseover');
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
// Simulate the mousedown event
simulateMouseEvent(event, 'mousedown');
ifProceed = false;
timerStart=false;
clearTimeout(time1Sec);
}
};
The link for the top-voted Answer is now broken.
To get jQuery UI Sortable working on mobile:
Add this JavaScript file to your project.
Reference that JS file on your page.
For more information, check out this link.

Resources