Why does Notification not work in Vaadin 14?

In Vaadin 8, Notification.show(“Testing notification”); would display a notification for the default duration. In Vaadin 14, it does nothing.

works for me. This has to do with your project setup. Or do you maybe call Notification.show(".."); in a background thread without using ui.access(() -> { ... }?

Why would you have to change project settings to get a Notification to show in v14? I didn’t have to do that for v8. More importantly, shouldn’t the documentation point out any differences between the versions?

I was focusing too much on how it has to do with project setup - i don’t know if thats the case.
What I wanted to say is that Notifications have always worked fine for me using Notification.show("..");, and if it doesnt work for you then you should look for the problem in your project and not in vaadins core. Maybe version conflicts or something similar?

Hi Kaspar,

I am trying to execute a long running task and wanted to show the current micro-step on an overlay. I tried notification and confirm dialog. Both dont show up at all.

I have also tried creating new instances of Notification with components (label for text and progressbar for indicating approximate wait time). Doesnt work either.

I did see the error with UI::access when I moved the processing to its own thread. So I tried placing the notification code (create, open and close) inside UI::access as well as UI::accessSynchronously. Also with no effect.

What project settings were referring about? How do I check/define them to get this working? I have lost atleast 2 days with this simple UX issue. As always any help is absolutely welcome and highly appreciated.

Thanks,
Sathya

  1. Have you annotated your view (or RouterLayout) with @Push?
  2. do you maybe use UI.getCurrent() in order to .access(..)? Because that would explain this too. You need to save the ui instance at time of attachment of the view. The following code should show you what I mean.
@Push
@Route("myView")
public class MyView extends VerticalLayout {
	private UI ui;
	
	public MyView(){
		addAttachListener(event -> {
			this.ui = event.getUI();
		});
		
		Button testButton = new Button("Test UI access", click -> {
			Thread backgroundThread = new Thread(() -> {
				// anything happening inside this thread will not be updated to the ui automatically, and needs to be pushed to the ui.
				UI.getCurrent().access(() -> {
					Notification.show("Starting long task");
				});
				
				Thread.sleep(2000); // wait 2 seconds to imitate long task
				
				this.ui.access(() -> {
					Notification.show("long task has finished");
				});
			}); 
			backgroundThread.start();
		});
		add(testButton);
	}
}

Another thing you could do is set the current UI within the new Thread, so you can then call UI.getCurrent().

Thread backgroundThread = new Thread(() -> {
	UI.setCurrent(this.ui); // UI.getCurrent() will now no longer return null within this whole thread.
	UI.getCurrent().access(() -> {
		Notification.show("hello world");
	});
});

Additional documentation:
[Server Push Configuration]
(https://vaadin.com/docs/v14/flow/advanced/tutorial-push-configuration.html),
[Asynchronous updates]
(https://vaadin.com/docs/v14/flow/advanced/tutorial-push-access.html)

PS: I could help you much better if you included some actual code of yours

Thanks a ton Kaspar. My code is very similar to the ones you posted. Except for the attachListener() part. However, I did use reference of the current view to get UI instance like below…

@Route("myPage")
public class MyPage extends AppLayout {
	
	private UI ui = null;
	
	@PostConstruct
	public void init() {
		ui = this.getUI().isPresent() ? this.getUI().get() : UI.getCurrent(); // the latter is always null but i get the ui from the page view instance
		Button startLongTask = new Button("Start", click -> {
			Label status = new Label("Working on step 1...");
			ProgressBar progress = new ProgressBar();
			progress.setIndeterminate(true);
			Notification taskStatus = new Navigation(new VeriticalLayout(status, progress));
			ui.access(() -> taskStatus.open());
			// do step 1
			
			status.setText("Working on step 2...");
			ui.access(() -> {taskStatus.close(); taskStatus.open();)); // to force update the content...
			// do step 2
		});
	}
}

Later I converted the same code to run under the click handler, so i do not ned the UI::access anymore. With a lot of logges, I find the Notification is actually opened in java but it is not getting displayed. For example, here is the code I used for this…

	taskStatus.addOpenedChangeListener( opened -> {
            if (opened.isOpened()) {
                try {
                    logging.info("Downloading and unpacking the baseline data...");
                    refData = baseline.fetch();
                    logging.info("Downloaded and unpacked Baseline Core Data!");
                } catch (ServiceException e) {
                    logging.error("Failed Baseline Prep Operation. Cause: " + e.getMessage(), e);
					taskStatus.close();
					Notification.show("Download/ Unpack baseline failed. Retry later please!!", 2000, Position.TOP_END);
                }
            }
        });

This can be confirmed when I see the notification is opened after all the steps execute the overlay opens with the status of the last step. Kinda defeats the purpose of the status overlay in the firs place haha… What else must I consider to get the overlay to display immediately when the Notification::open() is invoked.

Getting beter but still didnt achieve the intended functionality :slight_smile:

Hi Kaspar,

Thanks to your help I am starting to understand the framework better. UI::push() is required when performing a long running workload. I did observe that the priority is on critical path and not updating the UI. In addition, @Push annotation is required to invoke the push(), else we encounter ‘Push not enabled’ message with nn exception and critical path is broken, which fails the functionality intended as well. As much as possible, avoid separate threads since the UI::access doesnt throw errors nor behaves as expected. Better to keep the design as simple as possible.

Everyday is a new learning. Quite exciting:)