Register session to User - vaadin

I'm building a prototype using Vaadin8 starting from a single-module template.
I'm trying to assign a unique UI instance (a session) to each authenticated user, so that each user is presented with a particular type of content according to their own settings within the app. Here's my configuration:
#WebListener
public class Market implements ServletContextListener {
public static ArrayList<User>users;
public void contextInitialized(ServletContextEvent sce) {
users=new ArrayList<User>();
User hau=new User("hau");
users.add(hau);
User moc=new User("moc");
users.add(moc);
}
public void contextDestroyed(ServletContextEvent sce){}
}
public class User {
public String username;
public user(String username){this.username=username;}
}
public class MyUI extends UI {
User us3r;
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
String username;
if (this.us3r==null) {username="Guest";}else {username=us3r.username;}
Label who=new Label(username);
TextField userfield=new TextField();
Button login=new Button("login");
login.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
for (User user:Market.users) {
if (userfield.getValue().equals(user.username)) {
us3r=user;Page.getCurrent().reload();return;
}
}Notification.show("No user "+userfield.getValue());
}
});
Button logout=new Button("logout");
logout.addClickListener(new ClickListener() {
public void buttonClick(ClickEvent event) {
if(us3r!=null) {us3r=null; Page.getCurrent().reload();}
}
});
layout.addComponent(userfield);
layout.addComponent(login);
layout.addComponent(who);
layout.addComponent(logout);
setContent(layout);
}
After inputting one of the two usernames registered in the Database, I'd like the Label object to display the name of the authenticated user, instead of "Guest". Another effect I'm trying to achieve is if a user is logged in and there is another request to the server, it should generate a fresh UI with the uninstantiated us3r attribute.

Caveats: I have been using Vaadin Flow lately rather than Vaadin 8. So my memory is hazy, and my code may be wrong. And I have kept all the examples overly simple, not ready for production. Lastly, I am sure others would take a different approach, so you may want to do some internet searching to see alternatives.
UI is malleable
The UI of Vaadin is more plastic and malleable than you may realize. You can entirely replace the initial VerticalLayout with some other widget-containing-view.
The way I have handled logins with Vaadin is that my default UI subclass checks for an object of my own User class in the web session. Being based on Jakarta Servlet technology, every Vaadin web app automatically benefits from the Servlet-based session handling provided by the Servlet container. Furthermore, Vaadin wraps those as a VaadinSession.
If the User object is found to be existing as an "attribute" (key-value pair) in the session, then I know the user has already logged-in successfully. So I display the main content in that initial UI subclass object. By "main content", I mean an instance of a particular class I wrote that extends VertialLayout, or HoriontalLayout or some such.
If no User object is found, then my initial UI subclass object displays a login view. By "login view" I mean an instance of some other particular class I wrote that extends VertialLayout, or HoriontalLayout or some such.
When you switch or morph the content within a UI subclass instance, Vaadin takes care of all the updating of the client. The change in state of your UI object on the server made by your Java code is automatically communicated to the Vaadin JavaScript library that was initially installed in the web browser. That Vaadin JS
library automatically renders your changed user-interface by generating the needed HTML, CSS, JavaScript, and so on. There is no need for you to be reloading the page as you seem to be doing in your example code. As a single-page web app, the web page only loads once. In Vaadin, we largely forget about the HTTP Request/Response cycle.
Example app
First we need a simple User class for demonstration purposes.
package work.basil.example;
import java.time.Instant;
import java.util.Objects;
public class User
{
private String name;
private Instant whenAuthenticated;
public User ( String name )
{
Objects.requireNonNull( name );
if ( name.isEmpty() || name.isBlank() ) { throw new IllegalArgumentException( "The user name is empty or blank. Message # b2ec1529-47aa-47c1-9702-c2b2689753cd." ); }
this.name = name;
this.whenAuthenticated = Instant.now();
}
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
User user = ( User ) o;
return name.equals( user.name );
}
#Override
public int hashCode ( )
{
return Objects.hash( name );
}
}
The starting point of our app, our subclass of UI checks the session and switches content. Notice how we segregated the check-and-switch code to a named method, ShowLoginOrContent. This allows us to invoke that code again after login, and again after logout.
package work.basil.example;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.UI;
import javax.servlet.annotation.WebServlet;
import java.util.Objects;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of an HTML page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {#link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
#Theme ( "mytheme" )
public class MyUI extends UI
{
#Override
protected void init ( VaadinRequest vaadinRequest )
{
this.showLoginOrContent();
}
void showLoginOrContent ( )
{
// Check for User object in session, indicating the user is currently logged-in.
User user = VaadinSession.getCurrent().getAttribute( User.class );
if ( Objects.isNull( user ) )
{
LoginView loginView = new LoginView();
this.setContent( loginView );
} else
{
CustomerListingView customerListingView = new CustomerListingView();
this.setContent( customerListingView );
}
}
#WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
#VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet
{
}
}
Here is that LoginView, a VerticalLayout. We have our username & password, with a "Sign in" button. Notice how on successful authentication we:
Instantiate a User and add to the automatically-created session as an "attribute" key-value pair. The key is the class User, and the value is the User instance. Alternatively, you can choose to use a String as the key.
Invoke that showLoginOrContent method on MyUI to swap out our login view with a main content view.
In real work, I would locate the user-authentication mechanism to its own class unrelated to the user-interface. But here we ignore the process of authentication for this demonstration.
package work.basil.example;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.*;
public class LoginView extends VerticalLayout
{
private TextField userNameField;
private PasswordField passwordField;
private Button authenticateButton;
public LoginView ( )
{
// Widgets
this.userNameField = new TextField();
this.userNameField.setCaption( "User-account name:" );
this.passwordField = new PasswordField();
this.passwordField.setCaption( "Passphrase:" );
this.authenticateButton = new Button( "Sign in" );
this.authenticateButton.addClickListener( ( Button.ClickListener ) clickEvent -> {
// Verify user inputs, not null, not empty, not blank.
// Do the work to authenticate the user.
User user = new User( this.userNameField.getValue() );
VaadinSession.getCurrent().setAttribute( User.class , user );
( ( MyUI ) UI.getCurrent() ).showLoginOrContent(); // Switch out the content in our `UI` subclass instance.
}
);
// Arrange
this.addComponents( this.userNameField , this.passwordField , this.authenticateButton );
}
}
Lastly, we need our main content view. Here we use a "customer listing" that is not yet actually built. Instead, we place a couple pieces of text so you know the layout is appearing. Notice how in this code we look up the user's name from our User object in the session attribute.
We include a "Sign out" button to show how we reverse the authentication simply by clearing our User instance as the value of our "attribute" on the session. Alternatively, you could kill the entire session by calling VaadinSession::close. Which is appropriate depends on your specific app.
package work.basil.example;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import java.time.Duration;
import java.time.Instant;
public class CustomerListingView extends VerticalLayout
{
Button logoutButton;
public CustomerListingView ( )
{
// Widgets
this.logoutButton = new Button( "Sign out" );
this.logoutButton.addClickListener( ( Button.ClickListener ) clickEvent -> {
VaadinSession.getCurrent().setAttribute( User.class , null ); // Pass null to clear the value.
( ( MyUI ) UI.getCurrent() ).showLoginOrContent();
}
);
User user = VaadinSession.getCurrent().getAttribute( User.class );
Duration duration = Duration.between( user.getWhenAuthenticated() , Instant.now() );
Label welcome = new Label( "Bonjour, " + user.getName() + ". You’ve been signed in for: " + duration.toString() + "." );
Label placeholder = new Label( "This view is under construction. A table of customers will appear here.\"" );
// Arrange
this.addComponents( this.logoutButton , welcome , placeholder );
}
}
The effect of the "Sign out" button is to remove the main content, and take the user back to the login view.
Separation of concerns
One of the aims of the approach to logins is separation of concerns. The concern of building an interactive user-interface (Vaadin widgets and code) should be kept largely separate from the business logic of how we determine if a user is who they claim to be (authentication code).
Our UI subclass knows almost nothing about user-authentication. We moved all the mechanics of logging-in to other non-Vaadin-specific classes. The Vaadin-related code only has two connection points to authentication: (a) Passing collected credentials (username, password, or such), and (b) Checking for the presence of a User object in the session’s key-value store.
Multi-window web apps
By the way, you should know that Vaadin 8 has amazing support for multi-window web apps. You can write links or buttons to open additional windows/tabs in the browser, all working within the same web app and the same user session. Each tab/window has its own instance of a UI subclass you wrote. All of these UI subclass instances share the same VaadinSession object.
So using the logic seen above applies to all such tab/windows: Multiple windows all belonging to one session with one login.
Fake dialog boxes are not secure
You might be tempted to put your login view inside a dialog box appearing over your main content. Do not do this. A web dialog box is “fake”, in that it is not a window created and operated by the operating-system. A web app dialog window is just some graphics to create the illusion of a second window. The pretend dialog and the underlying content are actually all one web page.
A hacker might gain access to the content on the page, and might be able to defeat your login dialog. This is mentioned in the Vaadin manual, on the page Sub-Windows.
In my example above, we have no such security problem. The sensitive main content arrives on the user’s web browser only after authentication completes.
Web app lifecycle hooks
By the way, your use of ServletContextListener is correct. That is the standard hook for the lifecycle of your web app launching. That listener is guaranteed to run before the first user’s request arrives, and again after the last user’s response is sent. This is an appropriate place to configure resources needed by your app in general, across various users.
However, in Vaadin, you have an alternative. Vaadin provides the VaadinServiceInitListener for you to implement. This may be more convenient than the standard approach, though you need to configure it by creating a file to make your implementation available via the Java Service Implementation Interface (SPI) facility. Your VaadinServiceInitListener as another place to setup resources for your entire web app. You can also register further listeners for the service (web app) shutting down, and for user-session starting or stopping.
Browser Reload button
One last tip: You may want to use the #PreserveOnRefresh annotation.
Vaadin Flow
In Vaadin Flow (versions 10+), I take the same approach to logins.
Well, basically the same. In Vaadin Flow, the purpose of the UI class was dramatically revamped. Actually, that class should have been renamed given how differently it behaves. We no longer routinely write a subclass of UI when starting a Vaadin app. A UI instance is no longer stable during the user's session. The Vaadin runtime will replace the UI object by another new instance (or re-initialize it), sometimes quite rapidly, for reasons I do not yet understand. So I do not see much practical use for UI for those of us writing Vaadin apps.
Now in Flow I start with an empty layout, instead of a UI subclass. Inside that layout I swap nested layouts. First the login view. After authentication, I swap the login view for the main content view. On logout, the opposite (or close the VaadinSession object).

Related

Vaadin 14.2: Intercept logout (invalidate session) with BeforeLeaveListener

in my Vaadin 14.2.0 application there is a BeforeLeaveListener to show a confirmation dialog when the input is dirty (= there are unsaved changes in the input fields) to cancel (or intentionally proceed) the navigation:
BeforeLeaveListener listener = new BeforeLeaveListener() {
#Override
public void beforeLeave(BeforeLeaveEvent event) {
if (dirtyFlag.isDirty()) {
ContinueNavigationAction postponeAction = event.postpone();
// show confirmation dialog and maybe execute a proceed
[...] () -> postponeAction.proceed();
}
}
};
UI.getCurrent().addBeforeLeaveListener(listener);
This works fine for everything but for the logout.
This is how my logout button looked like in the start (what works as long as I do not want to postpone/cancel the logout):
Anchor link = new Anchor();
link.getElement().addEventListener("click", e -> {
VaadinSession.getCurrent().getSession().invalidate();
UI.getCurrent().navigate(LoginView.class);
});
Now I want to postpone the logout until the user confirms that the unsaved changes should get discarded. (Switching both lines in the EventListener seems to be a good idea, but did not work because of the following.)
Within the navigate-call the BeforeLeaveListener is called (good), but the logout is done nevertheless, because the code after the navigate()-call is executed (of course, because it is no blocking call), the session is invalidated and the user is logged out though the confirmation dialog just popped out (but the user had no chance to confirm/deny).
I tried to move the session invalidation into the LoginView (into a BeforeEnterObserver), but the result is that the login view is reloaded in an endless loop.
Question: is there something like "navigate to LoginView and if navigation is not postponed and not cancelled, then invalidate session"?
A workaround is to navigate to an intercepting LogoutView that just invalidates the session and redirects/forwards to the LoginView:
#Route(value = "logout")
public class LogoutView implements BeforeEnterObserver {
#Override
public void beforeEnter(BeforeEnterEvent event) {
VaadinSession.getCurrent().getSession().invalidate();
event.forwardTo(LoginView.class);
}
}
But this seems to me to be just a bad workaround with some overhead (creating a view just to forward to another view)...
I know, you didnt mention Spring. But that's actually what I believe Spring Security is doing under the hood.
My Logout Button (using Spring Security) is looking like this:
new Button("Logout", click -> {
UI.getCurrent().getPage().executeJs("location.assign('logout')");
});
Once you click it, you are logged out and redirected to the login view (more details from Baeldung). I think it will be just fine if you do your LogoutView redirection detour.
The fact that you can even use the Navigator to go to your LogoutView is even better here, since this is caught by BeforeLeaveObserver (in contrast to completely new requests by assigning a new location or refreshing the page. See BeforeLeaveObserver vs. beforeunload for more details on that)
I would still like to propose another idea for your usecase. I think it will be much simpler, but requires that the logout link knows of the dirtyFlag:
Anchor link = new Anchor();
link.getElement().addEventListener("click", e -> {
if(dirtyFlag.isDirty()){
// show Dialog with
// - a confirm btn that calls doLogout() on click
// - a cancel btn that closes the Dialog
} else {
doLogout();
}
});
...
private void doLogout(){
VaadinSession.getCurrent().getSession().invalidate();
UI.getCurrent().navigate(LoginView.class);
}

How to make runtime changes to a Vaadin component after loading

How can I change a Vaadin h1 element dynamically using a timer after the page is navigated to from another Vaadin page?
navigate
start.addClickListener(e->{
ActorCallsHandler.ach.startGame();
UI.getCurrent().navigate(Playboard.class);
});
class
public class Playboard extends VerticalLayout
{
private H1 timer;
public Playboard() throws ExecutionException, InterruptedException{
generateGUI();
}
private void generateGUI(){
//h1 element that should alter using a timer after page load
H1 timer = new H1();
}
}
For this to work, you need to use either the Push or Polling functionality of Vaadin so that changes can be sent to the client at any time instead of only in the response to a request that is triggered by some user action.
I would recommend that you check out the documentation for the Push feature at https://vaadin.com/docs/v13/flow/advanced/tutorial-push-configuration.html.

User Sessions with Vaadin Flow

I am currently a budding Java developer that wants to get into Vaadin development and currently trying to implement User Session login for my application. I have read about the content regarding the usage of VaadinServlets to do so : https://vaadin.com/docs/v10/flow/advanced/tutorial-application-lifecycle.html.
After relentlessly digging through API documentations and sample codes, I still am not able to understand how to implement the User Sessions for a specific user that logs into my platform. From what I understand is that, I can initialize my user session by using what i have implemented below.
However my aims for the application are slightly different:
[Use Case]
1.User logs in with their specific credentials.
2.Gets Redirected to a SecuredPage (which will create a User Session storing the user's username and retrieves a token?)
3.After 2-3mins of inactivity, the User will get forced out of the SecuredPage and Session closes?
#WebServlet(urlPatterns = "/*", name = "VaadinFlowServlet", asyncSupported = true)
#VaadinServletConfiguration(heartbeatInterval = 5, productionMode = false)
public class LoginServlet extends VaadinServlet implements SessionInitListener, SessionDestroyListener {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginServlet.class);
// <Method> ::servletInitialized():: -> handles most of the servlet customization. (write my servlet customization under this function.
// ::getService():: -> returns a VaadinServletService type?
// ::addSessionInitListener(this):: -> An event listener that can be registered to a VaadinService to get an event -> when a new Vaadin service session is initialized for that service.
// ::addSessionDestroyListener(this):: -> A listener that gets notified when a Vaadin service session is no longer used.
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(this);
getService().addSessionDestroyListener(this);
}
// <Method> ::sessionInit:: -> Starts Session?
// <Parameter> ::SessionInitEvent:: -> Event gets fired when a new Vaadin service session is initialized for a Vaadin service.
#Override
public void sessionInit(SessionInitEvent event) throws ServiceException{
// Do Session start stuff here
// Creates a Session?
LOGGER.info("session init() "
+ " Session-ID: " + event.getSession().getSession().getId()
+ " CSRF: " + event.getSession().getCsrfToken());
}
// <Method> ::sessionDestroy:: -> Stops Session?
// <Parameter> ::SessionDestroyEvent:: -> Event fired when a Vaadin service session is no longer in use.
#Override
public void sessionDestroy(SessionDestroyEvent event) {
// Do session end stuff here
LOGGER.info("session destory()");
}
}
1
So I was wondering if anybody can help me understand this matter better? Fully Appreciated
tl;dr
The mere existence of a custom-defined user-login object stored as an attribute in the key-value store of your VaadinSession represents the user having successfully authenticated. No need for all the session-listener code you wrote.
Let Vaadin do the heavy-lifting
I suspect you are working too hard.
There is no need for your session listeners. Vaadin handles nearly all the Java Servlet details on our behalf.
No need for the redirects. As a Vaadin developer, you are in full control of the content displayed in the browser tab/window, so you can switch between login form and main app content. Caveat: I am new to the #Route feature in Vaadin Flow, so there may be a niftier way with that feature to flip between login and main-content. And if you are using #Route for multiple views, each of those views should test for the authentication as described below.
VaadinSession
At the entry point of your Vaadin app code, retrieve the current VaadinSession object. This VaadinSession is a wrapper around the javax.servlet.http.HttpSession class defined by the Java Servlet spec. Vaadin automatically instantiates a session when the user’s browser first connects to your Vaadin web app (actually, Vaadin wraps the session instantiated by your web container). The session is automatically closed when the browser closes its tab/window, a time-out of inactivity occurs, or you programmatically close the session.
VaadinSession vaadinSession = VaadinSession.getCurrent() ;
Session attributes (key-value store)
Interrogate that session object’s key-value store known as “attributes”. The key is of type String and the value is of type Object (the superclass of all Java objects). After retrieving the Object object, you cast to the known class. You know the class, because it is your code that stored the attribute.
Your user-login class
You would have defined a class to store your user-login related info. Perhaps you named it UserLogin.
Something like:
public class UserLogin {
// Member values.
String userName ;
Instant whenAuthenticated ;
// Constructor.
public UserLogin( String userNameArg , Instant whenAuthenticatedArg ) {
this.userName = userNameArg ;
this.whenAuthenticated = whenAuthenticatedArg ;
}
}
Attempt to retrieve object of your user-login class from the session’s key-value store
Retrieve such an object of that type from the session attributes key-value store.
String attributeName = "my-user-login" ;
UserLogin userLogin = vaadinSession.getAttribute( attributeName ) ;
Rather than invent an attribute name, you could just use the class name. The Class class lets you ask for the name of a class as text.
String attributeName = UserLogin.class.getName() ;
UserLogin userLogin = vaadinSession.getAttribute( attributeName ) ;
If you want to use the class name as the key in this way, the VaadinSession class provides a shortcut.
UserLogin userLogin = vaadinSession.getAttribute( UserLogin.class ) ;
Check to see if your UserLogin object is null. If you retrieved a null, then you know you have not yet stored an attribute (or willfully stored a null).
If not null, it means your user already has an active UserLogin object stored. How could they be logged-in already if the entry point of your app is executing? This can happen if the user hits the Reload button on their browser window. (Train your user not to do so on a single-page web app such as Vaadin.)
Outline of code to write
UserLogin userLogin = vaadinSession.getAttribute( UserLogin.class ) ;
if( Objects.isNull( userLogin ) ) {
… display login form …
… when authenticated, instantiate a `UserLogin` and store as attribute …
if( authenticationSuccessful ) { // Testing some did-user-authenticate variable you defined in your login-form.
Instant whenAuthenticated = Instant.now() ; // Capture the current moment in UTC.
UserLogin userLogin = new UserLogin( userName , whenAuthenticated ) ;
VaadinSession.getCurrent().setAttribute( UserLogin.class , userLogin ) ; // Using class name as the `String` key tracking this `userLogin` object.
… switch content of the tab/window from authentication form to your main app content …
}
} else { // Else not null. User already authenticated. User may have hit "Reload" button in browser.
… display app content …
… perhaps log this event … maybe user needs to be trained to not hit Reload on a Single-Page Web App …
}
By the way… the discussion above about sessions is scoped to each user’s own connection to your web app in a single web browser tab/window.
At some point you may look for a hook into the lifecycle of your entire web app, before the first user connects and/or after the last user disconnects, learn about the hook defined in the Java Servlet spec. This hook is the ServletContextListener interface, where “context” means your web app as a whole. This is standard Java Servlet stuff, not at all specific to Vaadin, but Vaadin is actually a Servlet (perhaps the most sophisticated Servlet ever) so this context listener paradigm applies.
You write a class implementing that interface, by writing the before-first-user and the after-last-user methods. Identify your class to the web container by annotating #WebListener (or alternative means). Search Stack Overflow as this has been covered multiple times already.

Xamarin.Auth Google Presenter not closing in Android Xamarin.Forms App

I am using Xamarin.Form to write a Android app. I have successfully implemented the OAuth and I can sign in and get the user information using OAuth2Authenticator.
When the user clicks signup/Login I show the OAuthLoginPresenter as follows:
var oAuthLoginPresenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
oAuthLoginPresenter.Login(App.OAuth2Authenticator);
This works great and the user sees the login page.
When the user clicks Allow the completed Event on the OAuth2Authenticator instance fires as expected and the user is once again back looking at the app.
However this is where my problem is - I get a notification:
If CustomTabs Login Screen does not close automatically close
CustomTabs by Navigating back to the app.
Thing is though I am back at the app. If I look at all my open apps I can see the login screen is still running in the back ground so that presenter has not closed.
In my intercept Activity which gets the redirect looks like this:
[Activity(Label = "GoogleAuthInterceptor")]
[IntentFilter
(
actions: new[] { Intent.ActionView },
Categories = new[]
{
Intent.CategoryDefault,
Intent.CategoryBrowsable
},
DataSchemes = new[]
{
// First part of the redirect url (Package name)
"com.myapp.platform"
},
DataPaths = new[]
{
// Second part of the redirect url (Path)
"/oauth2redirect"
}
)]
public class GoogleAuthInterceptor: Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
Android.Net.Uri uri_android = Intent.Data;
// Convert Android Url to C#/netxf/BCL System.Uri
Uri uri_netfx = new Uri(uri_android.ToString());
// Send the URI to the Authenticator for continuation
App.OAuth2Authenticator?.OnPageLoading(uri_netfx);
Finish();
}
}
Am I missing a step in here to close that presenter? Any other ideas please?
UPDATE:
I have now found that using Chrome as the default browser works fine and presenter is closed. But if I use a Samsung browser as my default browser - it does not close.
So I need a way to close it manually.
Just set property to null when initializing Xamarin.Auth in your main activity:
//don't show warning message when closing account selection page
CustomTabsConfiguration.CustomTabsClosingMessage = null;

Vaadin 7 and browser url update

My Vaadin 7 application doesn't react on browser url changing. For example I entering from keyboard a new url parameters and pressing Enter key and after that nothing is changing.
Application only reacts on F5 or page refresh button.
How to also make Vaadin 7 application to respond to Enter key after url update ?
UPDATED
I'm using Vaadin com.vaadin.navigator.Navigator object.
For example I have an url: http://example.com/#!products/30970
When I change the url in browser address bar (for example to http://example.com/#!products/34894) and press enter key I would like to change information at my page in order to show info about product with id 34894 instead of product with a previous id 30970.
Vaadin Navigator and UriFragmentChangedListener
Right now I'm using Vaadin Navigator in order to define views:
Navigator navigator = new Navigator(this, viewDisplay);
navigator.addView("products", ProductView.class);
First time in web browser I'm successfully able to access this view with product id parameter for example by the following url:
http://example.com/#!products/30970
ProductView is constructed first time and in its public void enter(ViewChangeListener.ViewChangeEvent event) method I'm able to get uri parameters.
But after that when I change product id in web browser address bar in this url to another one(for example to 30971 in order to display information for another product):
http://example.com/#!products/30971
and press Enter key the view is not refreshed and doesn't react on these changes..
As suggested in the comments I have added UriFragmentChangedListener listener and now at least able to handle URL fragment changes(after Enter key presing).
Now, my logic have to react on these changes and I'm looking for a correct way how it should be implemented in Vaadin 7.
So, If I understood correctly - in additional to Navigator logic I also have to use this listener and inside of this listener logic I have to get a reference on appropriate view(navigator.getCurrentView() ?) object and invoke some method on this object in order to change internal view state without full view rebuild ? If I'm correct - is there some standard mechanism in Vaadin in order to simplify this job ?
i can not think of another way than pass the UriFragmentChangeEvent manually to your View. I guess the Vaadin API can not do it automatic.
public class MyUI extends UI{
#Override
protected void init(final VaadinRequest request) {
/*
* UriFragmentChangedListener
* when URL+Parameter manuell eingegeben werden
*/
getPage().addUriFragmentChangedListener(new UriFragmentChangedListener() {
#Override
public void uriFragmentChanged(UriFragmentChangedEvent event) {
View currentView = getNavigator().getCurrentView();
if(currentView != null){
if(currentView instanceof UriFragmentChangedListener){
((UriFragmentChangedListener)currentView).uriFragmentChanged(event); //custom method
}
}
}
});
}
}
To make this work add UriFragmentChangedListener to your ProductView:
public class ProductView extends CustomComponent implements View, UriFragmentChangedListener {
}
take a look at the Parameters in the ViewChangeEvent of your ProductView
By using this url structure "http://example.com/#!products/30970", you can read the product id as following:
#Override
public void enter(ViewChangeEvent event) {
String productId = event.getParameters();
}

Resources