I’m trying to set a selected value programmatically (based on a default selection stored in the database) on a ComboBox.
The strange this is, after I’ve set the items, and set the default value using setValue() I see in the valueChangeListener that first the value that I’ve set is coming in, but then that is immediately followed by a null value.
I have no idea where that null value is coming from.
api.foo().thenAccept(fooList -> {
final Foo foo = fooList.get(0);
ui.access(() -> {
myCombobox.setItems(fooList);
myCombobox.setValue(foo);
});
myCombobox.addValueChangeListener(event -> {
final Foo selectedFoo = event.getValue();
// do something
});
});
api.foo() is potentially slow, so I made it async.
This block is executed in the onAttach method of the view, so I get the ui reference from the attachEvent.
Any ideas what might be causing this?
There might be a logical explanation for this, but I simply don’t see it.
I have had similar experiences when using nullrepresentation on the Binding. Maybe you have a nullrepresentation set to empty string, and the first foo of your list is also an empty string?
Might be related to push… about how long are we talking about? 1-2 seconds that are just annoying while loading the form or more?
If it’s just the first: you can also use a focus listener and only load the data if the user clicks in the field (you still need to quickly load the first value if that always has to be selected)
If that’s not possible, it might help to move the value change listener to the Combobox init
I did loose the valueChangeEvent with event.getValue()==null when adding the changeListener to the init of the ComboBox.
So, now I get a valueChangeEvent with the selected value: basically showing a notification with the value that is supposed to be set, but the combobox remains blank.
I was inspired by this youtube tutorial by @marcushellberg .
I just replaced the ListenableFuture implementation with Spring @Async annotation.
This api call is a REST call, I assumed that doing it this way is actually best practice. Doesn’t seem right that I would do a call to a remote system synchronously from the onAttach method to load data into a component. After all, combobox doesn’t have a FetchCallback or BackendCallback like Grid does.
Small update:
I just discovered that this probably is a timing issue, and not the async per se.
If I do setItems() immediately followed by setValue(), then the valueChangeEvent is triggered, but the value is not shown as the selected value on the combobox.
However when I add a Thread.sleep in between the two, then it works.
Pre-fetching everything is not an option given the amount of these cases I will have in this application eventually and also, now I just select first from the list, but there will be logic behind which one to select as well (user preferences and fallback to system default, which will also require a DB call)
Or am I overengineering this? And should I not worry about doing synchronous REST / DB calls in the onAttach method?
Not sure what’s the correct way here, but multithreading makes the control flow complicated to understand. If you make a change to a component’s state (such as setting the value) once the request thread has completed, it will only be updated to the browser if you have Push enabled or once the next request arrives and returns. Sometimes you can make changes to a Component from a non-request thread during the request’s lifetime, and it works by accident, but once there’s a delay in the thread, it suddenly stops working without obvious explanation. Using language or framework features that hide the threading, such as @Async or asynchronous Events, tend to make things even more difficult to see.