2

MainView 包含 Information 组件:

@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());
    }

信息组件:

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);
    }
  }
}

当我从 MainView 调用 InformationComponent 时,标签不会在浏览器中更新

UI.getCurrent().access(() -> nbLineInFile.setText(nbLines))

也可以尝试 wwith @Push(PushMode.MANUAL) 和 ui.push(); 但也不起作用...

完整的源代码在这里:https ://github.com/Tyvain/ProcessUploadedFile-Vaadin_SpringBatch/tree/push-not-working

4

2 回答 2

4

我怀疑这里的问题是uploadFileSuccceed()从后台线程运行的,在这种情况下UI.getCurrent()会返回null. 这将导致NullPointerException杀死后台线程或者异常被调用者捕获并静默忽略。另一种选择是uploadFileSuccceed()通过不同的浏览器窗口发生,因此也发生在不同的UI实例中,这意味着更改将被推送到错误的上下文中UI

正是由于这些原因,UI.getCurrent().access(...)它通常是一种反模式,尽管不幸的是它在旧示例中​​被广泛使用。

UI.getCurrent()您可以通过在方法的开头记录 的值update并将其与UI.getCurrent()例如 的构造函数中的值进行比较来检查这是否是您的问题的原因InformationComponent

要正确解决问题,您应该将正确的UI实例传递给源自触发后台处理开始的任何事件的整个事件链。您还应该注意,使用getUI()任何子Component类中可用的方法可能很诱人,但该方法不是线程安全的,因此应避免在后台线程中使用。

作为最后的通知,我建议在这种情况下使用SpanorText组件而不是Label。在 Vaadin 10 中,Label组件已更改为使用<label>HTML 元素,这意味着它主要用作输入组件的标签。

于 2018-08-23T04:15:52.457 回答
0

根据 Leif 提供的信息,您应该执行以下示例。

在运行时,当这个HorizontalLayout子类对象附加到父UI对象时,它的onAttach方法被调用。那时我们可以通过存储它的引用来记住 UI 是一个名为 的成员变量ui。实际上,Optional<UI>返回的是 an 而不是UI对象,所以我们需要测试 null,尽管它在 点永远不应该为 null onAttach

public class InformationComponent extends HorizontalLayout {

    UI ui;

    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!
        this.ui.access(() -> nbLineInFile.setText(nbLines)); // UI is not updated!!
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (UIDetachedException e) {
        // Do here what is needed to do if UI is no longer attached, user has closed the browser
    }

    @Override  // Called when this component (this `HorizontalLayout`) is attached to a `UI` object.
    public void onAttach() {
        ui = this.getUI().orElseThrow( () -> new IllegalStateException("No UI found, which should be impossible at point of `onAttach` being called.") );

}
于 2018-08-23T06:55:24.650 回答