MainView include InformationCOmponent:
@Push
@Route
public class MainView extends VerticalLayout {
InformationComponent infoComponent;
public MainView(@Autowired StudentRepository studentRepo, @Autowired Job jobImportCsv, @Autowired JobLauncher jobLauncher, @Value("${file.local-tmp-file}") String inputFile) {
[...] // some stuffs
infoComponent = new InformationComponent(studentRepo);
add(infoComponent);
}
//update when job process is over
private void uploadFileSuccceed() {
infoComponent.update(myUploadComponent.getFile());
}
InformationComponent:
public class InformationComponent extends HorizontalLayout {
StudentRepository studentRepo;
Label nbLineInFile = new Label();
VerticalLayout componentLeft = new VerticalLayout();;
VerticalLayout componentRight = new VerticalLayout();;
public InformationComponent(StudentRepository studentRepo) {
[...] // some init and style stuff
addLine("Nombre de lignes dans le fichier", nbLineInFile);
}
private void addLine(String label, Label value) {
componentLeft.add(new Label(label));
componentRight.add(value);
}
public void update(File file) {
try {
long nbLines = Files.lines(file.toPath(), Charset.defaultCharset()).count();
System.out.println("UPDATED! " +nbLines); // value is display in console ok!
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
When I call InformationComponent from MainView the Label is not update in the browser.
UI.getCurrent().access(() -> nbLineInFile.setText(nbLines))
also try wwith @Push(PushMode.MANUAL) and ui.push(); but doesn't work either...
Complete source code is here: https://github.com/Tyvain/ProcessUploadedFile-Vaadin_SpringBatch/tree/push-not-working
I suspect the problem here is that
uploadFileSuccceed()
is run from a background thread, in which caseUI.getCurrent()
will returnnull
. This would cause aNullPointerException
that either kills the background thread or alternatively the exception is caught and silently ignored by the caller. Another alternative is thatuploadFileSuccceed()
happens through a different browser window and thus also a differentUI
instance, which means that the changes would be pushed in the context of the wrongUI
.For exactly these reasons,
UI.getCurrent().access(...)
is generally an anti pattern, even though it's unfortunately quite widely used in old examples.You can check whether this is the cause of your problem by logging the value of
UI.getCurrent()
in the beginning of theupdate
method, and comparing that to the value ofUI.getCurrent()
e.g. in the constructor ofInformationComponent
.To properly fix the problem, you should pass the correct
UI
instance through the entire chain of events originating from whatever triggers the background processing to start. You should also note that it might be tempting to use thegetUI()
method that is available in anyComponent
subclass, but that method is not thread safe and should thus be avoided in background threads.As a final notice, I would recommend using the
Span
orText
component instead ofLabel
in cases like this. In Vaadin 10, theLabel
component has been changed to use the<label>
HTML element, which means that it's mainly intended to be used as the label of an input component.Based on information provided by Leif you should do something like the following example.
At runtime, when this
HorizontalLayout
subclass object is attached to a parentUI
object, itsonAttach
method is called. At that point we can remember the UI by storing its reference is a member variable namedui
. Actually, anOptional<UI>
is returned rather than aUI
object, so we need to test for null, though it should never be null at point ofonAttach
.