Prevent tab switching - vaadin

I use Vaadin23.
Is there a legal way to prevent tab switching?
As i understand documentation method
public Registration addSelectedChangeListener(
ComponentEventListener<SelectedChangeEvent> listener) {
return addListener(SelectedChangeEvent.class, listener);
}
register listener which is triggered after tab change event.
What are the options?
Thanks!

If your tab contents are navigation targets (Routes), you can use the BeforeLeaveObserver interface in your form view:
public class SignupForm extends Div
implements BeforeLeaveObserver {
#Override
public void beforeLeave(BeforeLeaveEvent event) {
if (hasChanges()) {
ContinueNavigationAction action =
event.postpone();
ConfirmDialog confirmDialog = new ConfirmDialog();
confirmDialog.setText("Your form has changes! Are you sure you want to leave?");
confirmDialog.setCancelable(true);
confirmDialog.addConfirmListener(__ -> action.proceed());
confirmDialog.open();
}
}
private boolean hasChanges() {
// TODO: implement your logic to check if there are unsaved changes
}
}

Related

How can I reroute in BeforeEnterObserver in the view before AppLayout is rendered in Vaadin 14 (Vaadin Flow)

So for example let's I have:
public class MainView extends AppLayout {
public MainView() {
User user = VaadinSession.getAttribute("user");
if(user.isCheckSomething())
Span span = new Span("Hello " + user.getFirstname());
}
}
This will fail with a NPE if the user is not already logged in as getting the User from the session will be null. Now I could add a null check before the if(user.isCheckSomething()) but ideally I would prefer not to have the AppLayout rendered at all if the user is not logged in. That is I'd rather fail in the view through BeforeEnterObserver in the public void beforeEnter(BeforeEnterEvent event) method however the AppLayout is called and created before the beforeEnter(...) method is called.
In other words how can I force the instantiation of AppLayout to be skipped entirely if the user isn't logged in through the view so that the AppLayout is never constructed.
As a rule of thumb, it's really not reliable to work with the UI in the constructor in any way, since it's not initialized yet.
AppLayout has its own afterNavigation method which should be called after BeforeEnterEvent.
public class MainView extends AppLayout {
#Override
protected void afterNavigation() {
super.afterNavigation();
User user = VaadinSession.getAttribute("user");
if(user.isCheckSomething())
Span span = new Span("Hello " + user.getFirstname());
}
So in beforeEnter method you will reroute to login, otherwise the after navigation from the parent layout will be fired.
#Override
public void beforeEnter(BeforeEnterEvent event) {
if (!isAuthenticated()) {
event.rerouteTo(LoginView.class);
}
}
You can either implement BeforeEnterObserver in each of your views and check if user is authenticated over and over again... or you can do it once:
#SpringComponent
public class AuthenticationControl implements VaadinServiceInitListener {
#Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().addUIInitListener(uiEvent -> {
final UI ui = uiEvent.getUI();
ui.addBeforeEnterListener(/* do the check here, either with a method reference or create a separate listener class */);
});
}
}

Custom Repeat not breaking in IF

I am creating a CustomRepeat by extending CustomLoopTest. It never breaks in the IF condition.
Because break happens only for concrete class LoopTestStep, is it possible to modify LoopTestStep inside the IF condition by an interface? So we can implement that interface in our CustomLoopTestStep.
Another possiblity is to help to provide an alternative way.
public abstract class CustomLoopTestStep : CustomTestStep
{
protected CancellationTokenSource breakLoopToken { get; private set; }
[Browsable(false)]
protected CancellationToken BreakLoopRequested { get { return breakLoopToken.Token; } }
public CustomLoopTestStep()
{
breakLoopToken = new CancellationTokenSource();
}
public void BreakLoop()
{
breakLoopToken.Cancel();
}
/// <summary> Always call base.Run in LoopTestStep inheritors. </summary>
public override void Run()
{
breakLoopToken = new CancellationTokenSource();
}
}
At the moment (OpenTAP v9.4 and earlier), you need to inherit from LoopTestStep to interact with the If Verdict step. Otherwise if you prefer to achieve this by implementing an interface, you can submit a feature request here to add an interface in OpenTAP.

Custom Listeners with detach() method error

I would like to know about Vaadin's detach() method. How can I understand below definition from API ?
Called before the UI is removed from the session.
I got a problem when creating custom listener such as BroadCaster .
MyCustomListener.java
public interface MyCustomListener {
void fireEvent(MyCustomEvent event);
}
MyCustomEvent.java
public class MyCustomEvent {
private String message;
public MyCustomEvent(final String message) {
this.message = message;
}
public final String getMessage() {
return message;
}
}
MyCustomDispatcher.java
public final class MyCustomDispatcher {
private static LinkedList<MyCustomListener> customListeners = new LinkedList<MyCustomListener>();
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
private MyCustomDispatcher() {
}
public static synchronized void register(final MyCustomListener listener) {
customListeners.add(listener);
}
public static synchronized void unregister(final MyCustomListener listener) {
customListeners.remove(listener);
}
public static synchronized void invokeMyCustomEvent(final String message) {
if (message == null || message.trim().length() <= 0) {
return;
}
for (final MyCustomListener listener : customListeners) {
executorService.execute(new Runnable() {
public void run() {
listener.fireEvent(new MyCustomEvent(message));
}
});
}
}
}
I call this listener from my UI class as ...
public class HelloWorldUI extends UI implements MyCustomListener {
#Override
protected void init(VaadinRequest request) {
System.out.println("Getting initialized !");
MyCustomDispatcher.register(this);
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
setSizeFull();
layout.addComponent(new Label("Hello World !"));
}
#Override
public void detach() {
System.out.println("Listener was Unregister !");
MyCustomDispatcher.unregister(this);
super.detach();
}
#Override
public void fireEvent(MyCustomEvent event) {
// Do Something
}
}
I call unregister() method of my custom listener inside detach() method for
from some examples for custom listener
to avoid receiving messages for UIs no longer in use (and ensuring that the detached UI can be garbage collected).
Cleaning up resources in a UI
My problem was due to detach() method because when I refreshed my browser , my listener instance was deleted (from detach() method). So , I can't get fireEvent() anymore. I debugged , detach() method was called after init() method of my UI when refreshing browser. But if I remove calling unregister(MyCustomListener listener) from detach() method , that may cause nesting events (previous listeners were still alive).
What am I wrong ? How can I fix it ? Any suggestions ?
Sorry ! this is stupid question . Vaadin's component were server-side codes and I should avoid using static as I much as I can. When I am using my custom listeners as static-resources , these events were share all others. If someone invokes one event , every users will get same.
Static collection of listeners (sharing events) may only suitable for server-push.
I shouldn't create custom listeners as like this.
Thanks #HenriKerola for explanation of using static fields in vaadin and about the creating new UI instance when browser was refresh.

Disable ValueChangeListener

I'm new of vaadin and now I'm running in the following trouble. I have a TabSheet that contains a few tabs.
Now my Tax Code component setted in immediate mode and others it has a the following ValueChangeLister
TextField taxCode = new TextField();
taxCode.setImmediate(true);
taxCode.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
if(taxCode.isModified()){
searchByTaxCode(taxCode.getValue());
}
}
});
then each time that the user changes the value to taxCode component, the method searchByTaxCode is invoked. This happens also when the user switch from Tab2 to Tab1 and I don't want this?
How can I fix this problem?
This simple test app does not fire the ValueChangeEvent when switching tabs, only when actually changing the value in the TextField. You have something more that could influence the behaviour?
public class TabSheetEventsUI extends UI {
#WebServlet(value = "/tabsheetevents/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = TabSheetEventsUI.class)
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
TabSheet tabSheet = new TabSheet();
Panel tab1 = new Panel("Tab 1");
Panel tab2 = new Panel("Tab 2");
tabSheet.addTab(tab1);
tabSheet.addTab(tab2);
final TextField tab1tf = new TextField("tab1tf");
tab1tf.setImmediate(true);
tab1tf.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
System.out.println("Value change event catched.");
}
});
tab1.setContent(tab1tf);
setContent(tabSheet);
}
}

How to explicitly access the Connector from Widget Side into Vaadin 7?

I create a Widget with his Server Side Class and the Client Side (Connector Class, ServerRPC Class, State Class and Widget Class).
Connector :
#Connect(Custom.class)
public class CustomConnector extends ButtonConnector {
...
public void myFunc() {
// DO Something
}
}
Widget :
public class CustomWidget extends VButton {
...
private CustomConnector conn = new CustomConnector();
public CustomWidget () {
conn.myFunc();
}
...
}
Now from the Widget Class i want to explicitly call/access the Connector Object, which are not a Singleton, so that i can access a function too. How can i solve it?
In my opinion you should not access connector directly from GWT widget. It is against Vaadin 7 architecture where GWT widgets are objects independent from vaadin at all.
However if we are talking about dirty migration from Vaadin 6 to 7 solution could be:
ComponentConnector connector = ConnectorMap.get(client).getConnector(CustomWidget.this); // client is taken from updateFromUIDL method (Vaadin6)
Better solution will be to add "state" listener to the widget
public interface CustomWidgetStateListener {
public void stateChanged();
}
public class CustomWidget extends VButton {
...
CustomWidgetStateListener listener;
public void addStateListener(CustomWidgetStateListener listener) {
this.listener = listener;
}
public void notifyStateChanged() { // You can call notifyStateChanged() whenever you want to notify connector
listener.stateChanged();
}
...
}
public class CustomConnector extends ButtonConnector {
public CustomConnector() {
getWidget().addStateListener(new CustomWidgetStateListener() {
public void stateChanged() {
myFunc();
}
});
}
...
public void myFunc() {
// DO Something
}
}

Resources