Important Notice - Forums is archived
To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.
Navigator7 add-on: API of Vaadin 7 ?
Hope that with such a thread title, we are going to have reactions and feed the not-very-active (on the site) debate about what API change developers need to address that:
Window management should be completely revised to make supporting multi-window applications, tabbed navigation, bookmarking and web-style applications easy and logical
http://vaadin.com/web/joonas/wiki/-/wiki/Main/Vaadin_7_API
I've taken a week to extract what I know and have done in that area (with the accumulated knowlege given by your great support on this forum), into a reusable framework that is running in production on BlackBeltFactory.com. I've taken an extra day to document it, in order to have more reactions about what API developers need.
Navigator7 is born: http://vaadin.com/directory#addon/120
Let me already answer the following question:
How Navigator7 compares to the Navigator plugin?
I started from the Navigator plugin (1 class). You will notice that I've kept the examples of it to ease the usage comparison.
I've added all the reusable code of BlackBeltFactory.com in that area. I ended up with a mini framework of a dozen of classes. It's much more code because I address more areas (as URI analysis and page templating. Navigator7 also fixes a technical weakness of UirFragmentUtiliy not addressed by the original Navigator). I've been also much more aggressive/intrusive in the API changes for the programmer.
If you have anything to say about what you expect in Vaadin 7 to ease your life in these areas, please post a reply.
- page navigation,
- tabbed browsing,
- URI parameters handling,
- page templating.
maintain 100% backwards compatibility
You agree with me that Vaadin 7 will be a major new version with lots of enhancement ?
I'm not OK with Joonas about “maintain 100% backwards compatibility “. I think that it'll be a brake to solid fearless and audacious evolution of Vaadin if we maintain 100% backwards compatibility.
What is your opinion about that ?
What is your opinion about that ?
Good question Ramzi.
Navigator7 is an odd-on on top of Vaadin 6. By definition it removes nothing from Vaadin 6 and backward compatibility is 100% (it is just not using the add-on). If it would be integrated in Vaadin 7, it could be an addition of classes without much impact on the existing ones.
I would be to say that it's a layer on top of the existing API. It's obvious to me now Navigator7 exists, but it was not clear to me a month ago. It's like having (sorry for the ugly comparison ;-) Struts2 on top of Servlet. Struts2 does not break Servlet/JSP. Some need Vaadin Window management API as it is now. But it is probably too low level and the answer is probably to leave the current Window management API untouched (or nearly), and to add a web navigation layer.
What needs to change is the documentation in that case: you don't teach the low level (existing) API anymore (Servlet) but directly the higher level one (Struts2) to the masses. The sampler application, for example, if a perfect candidate to benefit Navigator7.
Ramzi Youssef: You agree with me that Vaadin 7 will be a major new version with lots of enhancement ?
I'm not OK with Joonas about “maintain 100% backwards compatibility “. I think that it'll be a brake to solid fearless and audacious evolution of Vaadin if we maintain 100% backwards compatibility.
What is your opinion about that ?
I think that you misread my proposal. The quoted part should be "Make it easier to implement the Container interface, but maintain 100% backwards compatibility.". The API I proposed for windowing in Vaadin 7 is far from being 100% compatible. See http://vaadin.com/web/joonas/wiki/-/wiki/Main/Vaadin_7_API
The word "Application" for the com.vaadin.Application class sounds counter intuitive to me.
When I explain to people that it's in the HttpSession, everybody understand its scope.
But the word "Application" suggest the application level scope (or ServletContext, or Spring ApplicationContext,...), while there is one instance per browsing user.
I think that:
- the word is misleading at that place
- we need, in Vaadin, a real web application scoped object.
With Navigator7, I've stored a reference to the NavigatorConfig and to the UriAnalyzer in the com.vaadin.Application instance.
It's a mistake. There should be one instance of these two classes for the whole web app, and any class of my application should have an easy access to them.
I'll turn them into singletons in the meanwhile.
Hi John!
Just a comment to your thoughts about the Application scope:
I think too that it is a bit counter intuitive if you come from the world of web applications. People are used to that applications larger than just one session - more or less shared with all users.
I have always used the following approach when explaining this:
- Application class is a "template" for application.
- A new instance of an Application (sub-)class is created for every user.
- Good thing here is that all things you store (in fields) in the application instance are for that one user only.
- For web developers, an instance of an Application maps closely to HttpSession. There is ApplicationContext that is more precisely a session and that can map to instances of several applications.
Of course, all this makes more sense when considering desktop applications where there (typically) is no more than a single user per application.
I introduced a new class in the framework: WebApplication.
It's a ServletContext scoped application. There is a name clash with the com.vaadin.Application.
That's now Navigator7 v7.1
The more I think about it, the more I beleive that the "application" defined in the web.xml as init parameter for the vaadin servlet, should be ServletContext scoped (as my new WebApplication), and users should override it, instead of the HttpSession scoped com.vaadin.Application.
If we take the Navigation7 stuff apart, what is com.vaadin.Application instances used for?
1. to hold references to Window objects. Is it ok to store that in the HttpSession? Probably.
2. to define the algorithm for creating the main window. It's probably no user-specific algo => It should probably moved in the ServletContext scoped application.
Now, with Navigator7, user application code (pages) don't need the com.vaadin.Application (or their descendant). They need the main Window (to template it) and the UriAnalyzer (ServletContext scoped now).
As summary, I'd rename com.vaadin.Application to com.vaadin.UserContext, and make it less central in the design, with things moved to the ServletContext scoped WebApplication. People would extend WebApplication and tell the new class name in the web.xml (init param of Vaadin servlet).
I've not changed Navigator7 to that point, it's not goal (at least not before an common agreement after discussion).
People would extend WebApplication and tell the new class name in the web.xml (init param of Vaadin servlet).
I've not changed Navigator7 to that point, it's not goal (at least not before an common agreement after discussion).
I just did it in fact. I've extended Vaadin's ApplicationServlet. Release 7.15
Appreciate the work you done, did some testing earlier with navigator but you provide a more complete solution.
A suggestion, since it's currently hard to secure pages with spring security since every url is one page. I would like to unregister pages once I a user logged out/logged in.
So some more methods to update/remove page sets methods on WebApplication would be useful ! Using this project in http://cia.sourceforge.net/
Best regards Pether
Hi Pether,
What about adding some kind of filter/interceptor mechanism in the Navigator?
A suggestion, since it's currently hard to secure pages with spring security since every url is one page.
That way, you could put your code to decide if the navigation is done or not (or redirect to another error page). You could even annotate your page with some @Authorization(role="manager") annotation that your interceptor would check.
Someone would probably provide a reusable interceptor wrapping Spring security.
What do you think?
John Rizzo: Hi Pether,
What about adding some kind of filter/interceptor mechanism in the Navigator?
That way, you could put your code to decide if the navigation is done or not (or redirect to another error page). You could even annotate your page with some @Authorization(role="manager") annotation that your interceptor would check.
Someone would probably provide a reusable interceptor wrapping Spring security.
What do you think?
Use annotations in the service layer, so would be nice. Will have to solve it some way..
Some questions about the WebApplication class.
Why store "ThreadLocal<ServletContext> currentServletContext" in thread local. if only one instance should be in the application. Otherwise i could use spring annotations for creation of the application impl without setting up the servlet context.
The use of "public static WebApplication getCurrent()" that creates some problem.
Pether
Why store "ThreadLocal<ServletContext> currentServletContext" in thread local.
Because the (not wonderful) Servlet API gives you no way to get the curreent ServletContext out of the blue. You can get it from a Filter and from a Servlet. And we manipulate no filter nor servlet in our Vaadin application components, thus we don't have access to the ServletContext.
I've found the VaadinServlet (NavigableApplicationServlet extends ApplicationServlet) a convenient entry point to catch the ServletContext and store it somewhere. Better ideas are welcome.
I cannot store the ServletContext into a WebApplication instance because I need the ServletContext to retreive WebApplication instances.
if only one instance should be in the application.
There is one ServletContext instance per web application => It's not very clean to store it in a regular static variable.
With threadLocal, I remember the correct reference for each thread. If the Servlet API could tell me "for the current thread, this is the ServletContext" it would not be necessary. That whole notion of multiple web app / ServletContext in the same JVM is probably a mistake of the Servlet API. We have to live with it ;-)
Otherwise i could use spring annotations for creation of the application impl without setting up the servlet context.
I don't understand what you mean. The WebApplication class is instantiated by the NavigableApplicationServlet. Do you bypass the NavigableApplicationServlet?
John Rizzo:
I don't understand what you mean. The WebApplication class is instantiated by the NavigableApplicationServlet. Do you bypass the NavigableApplicationServlet?
I don't use the NavigableApplicationServlet, depend on springapplicationservlet for some functionality so created a new servlet.
Coded
http://cia.sourceforge.net/xref/com/hack23/cia/web/impl/ui/application/ApplicationServlet.java , that works fine.
I always run the same application so not as flexible as http://code.google.com/p/navigator7/source/browse/trunk/Navigator7/src/org/vaadin/navigator7/NavigableApplicationServlet.java .
Pether
Ok, you don't use Vaadin ApplicationServlet that my NavigableApplicationServlet extends, but the servlet of a Spring related add-on.
I've moved the my code from NavigableApplicationServlet to WebApplication. Now NavigableApplicationServlet is minimal, and you can easily adapt your servlet extending SpringApplicationServlet to initialize WebApplication correctly.
See NavigableApplicationServlet as an exemple and copy/paste the 3 lines of code:
WebApplication.init(servletConfig, getServletContext(), getClassLoader());
WebApplication.beforeService(request, response, getServletContext());
WebApplication.afterService(request, response, getServletContext());
I'm uploading v7.16 that includes these changes.
Good luck, and don't hesitate to click 5 stars on the add-on if it's useful for you.
http://vaadin.com/directory#addon/120
John.
John Rizzo:
I've moved the my code from NavigableApplicationServlet to WebApplication. Now NavigableApplicationServlet is minimal, and you can easily adapt your servlet extending SpringApplicationServlet to initialize WebApplication correctly.
See NavigableApplicationServlet as an exemple and copy/paste the 3 lines of code:John.
Thanks for the update, changed my code and works excellent now.
Rated the plugin as well. Saved a lots of time and more advanced than the original navigator, so happy to give it 5 stars :)
Pether
I've added an interceptor mechanism.
Here is the Interceptor interface and doc:
/**
* An interceptor is a stateless class that follows the interceptor pattern, as found in Filter and in AOP languages.
* Interceptors are objects that dynamically intercept Page invocations. They provide the developer with the opportunity to define code that can be executed before and/or after the execution of an action. They also have the ability to prevent a page from being invoked.
* Interceptors provide developers a way to encapsulate common functionality in a re-usable form that can be applied to one or more Pages.
* Interceptors must be stateless and not assume that a new instance will be created for each request or Page.
* Interceptors may do some processing before and/or after delegating the rest of the processing using PageInvocation.invoke().
*
* Navigator7 Interceptor differs from Struts 2 Interceptors and Servlet Filters the following ways:
* - There is one Navigator7 Interceptor invocation for each NavigationEvent (=> once when we move to the page and instanciate it), while Filters and Struts 2 Interceptors are called for every request (included when you click a button on a page).
* - Navigator7 Interceptors are not configured into an external xml file, and you cannot define a set of pages for which they should be applied and others for which they should not be applied. Just program it inside the interceptor if you need to activate it for specific pages. This should probably change if we want to use reusable interceptors written by others only for specific pages, but it's not the case yet. Let's keep it simple for now.
*
* To have an idea of what interceptors can be used for, see Struts 2 documentation (while some examples are meaningless in a Vaadin context):
* http://struts.apache.org/2.x/docs/interceptors.html
*
* A typical usage would be security: the interceptor can check the logged-in user in the session, and can check annotations on your Page classes, and forward to an error page in case of mismatch.
*
* Register your interceptor in your class extending WebApplication, by calling WebApplication.registerInterceptor().
*
*
* @author John Rizzo - BlackBeltFactory.com
*
*/
public interface Interceptor {
public void intercept(PageInvocation pageInvocation);
}
In the initial Navigator, Joonas did implement a dialog box that asks the user to confirm before leaving some pages.
It was there (in a more generic form) in the Navigato7's Navigator class. But I didn't feel it was at the right place, it was too close form the heart.
I've transformed it into an interceptor and it's much better now on the design point of view. It also gives you a non trivial example of Interceptor ;-)
The example application has been updated as well to show how to register interceptors.
It's in version 7.2, available now.
Let's continue this Vaadin-web-7 architecture discussion ;-)
Currently Interceptors are called before the page instantiation. Maybe the page wil never be instantiated.
It might be nice to have the page instance in interceptors because we may like an interceptor to do some automatic value injection in the page, for example.
Struts2 Interceptors can do that because they have access to the Action instance that is instantiated before the interceptors chain is called.
On the contrary our Vaadin Interceptor has no page instance, because:
1- it's heavier to instantiate a Vaadin page than a light Struts2 Action, and we are not sure we'll need the page at all (an interceptor may decide to stop the call chain).
2- Struts2 has the Action.execute() method to call at the end of the interceptor chain. We don't have that in Vaadin. Our (non request scoped) "equivalent" is to instantiate the page (which is a VerticalLayout containing much stuff, or something similar).
A solution to have the page instance in the interceptor is to have our pages implementing a Vaadin interface. (I know, PageInterface is a bad name and it should be "Page", it's just avoid confusion in this post)
interface PageInterface {
Component init(); // result placed in the AppLevelWindow.
}
The navigator would:
1- Instantiate our page (i.e. new HomePage implements PageInterface)
2- Call the interceptors chain (interceptor may inject values based on annotations for example)
3- Call page.init
The disadvantage of introducing PageInterface is that pages cannot be Vaadin Components anymore (instead they contain a main component, as CustomLayouts do). It's more intrusive. I'm not fan of it.
To summarize, we have 3 options:
A. Pages are instantiated after the interceptor chain is called. Interceptors have no access to the page instance. It's the implemented option.
B. Pages are instantiated before the insteceptor chain is called => Interceptors have access to page instance.
B1. and they are normal Vaadin component => much of the UI building is done in the constructor => it would not benefit (at constructor time) value injections done in the interceptor called afterwards => BAD OPTION.
B2. and they implement PageInterface, and kindly put the UI building code in the init method (can we really count on programmer discipline?)
My preference order is:
A. (but we cannot inject values in page instances)
B2.
Whatever you do in Vaadin 7, you probably want think about that aspect carefully.
To add naming consistency for web people, I'd rename TransactionListener to "RequestInterceptor" (if responsible to call the next RequestInterceptor) or "RequestListener" (if as now, has start/end methods).
The current interceptor should probably be renamed NavigationInterceptor (to make people understand it's not called for every request ;-)
John.
In fixed the problem: the PageInvocation has now a lazy getPageInstance() method. If called by an interceptor who needs the instance, it creates it.
As simple as that ;-)
It enabled me to implement parameter injection:
http://vaadin.com/forum/-/message_boards/message/177821
On BlackBeltFactory (using Vaadin and Navigator7), when I come back to my browser and click a link in the menu, I often get this provocating Vaadin top red box "Session Expired". Any Vaadin developper/user know what I mean. When you see that on a web site, you tell "ah, yet another Vaadin web site". It's sad that this redbox is associated to Vaadin brand, isn't it?
Certainly, if I'm triggering some action within a page or a dialog box (as a button ClickListener invocation), and that the page, dialog box and button objects are gone with the expired session, then there is nothing else to do than to apologize to the user.
But, in the case that Vaadin is aware of page navigation (purpose of Navigator 7), it may detect that we want to move to another page. If we move to another page, not showing any error and silently renewing the session would be much more user friendly (as a default behavior, even if there is an option to change it).
Hope you will consider doing that if you integrate something like Navigator7 in Vaadin 7.
A live demo using the navigator https://hack23.com/ , NOTE: self signed cert and still very much work in progress. user/password or sysop/password for admin user.
All the best, Pether
I'd like to describe the following problem here, because I think it should be addressed by the navigation system of Vaadin 7. It can't be addressed by Navigator 7 without making choices for Vaadin core.
A batch job must send a mail (very legitimate need). In the mail, it will include a "click here" link to a page of your web application.
ExternalResource res = new ParamPageResource(CoursePage.class, course);
String courseUrl = BlackBeltUriAnalyzer.getFullUrl( res );
String mailBody = "<a href='"+ courseUrl + "'>Click Here!</a>";
We face 2 problems:
- line 1 needs (through cascading calls) a Nav7 WebApplication initialized.
- line 2 needs the (non existing in batch) ServletContext to build the url.
Problem 1: WebApplicaiton Initialization
ParamPageResource is a class of Nav7 indirectly extending ExternalResource from Vaadin.
ParamPageResource and its ancestor PageResource will verify that:
- the given page (CoursePage.class here) is a registered page (your descendant of WebApplication does register pages), and
- that the given parameter (course) can be injected (type check) in a @Param attribute of the page class (CoursePage.class).
To do the first point, it needs the WebApplication to have been started. But the name of your class (extending WebApplication) is in the web.xml as init parameter of the Vaadin servlet.
Because of that, instantiating your WebApplication class (BlackBeltWebApplication in my exammple) is not possible outside of a web thread. A batch thread within the web application, or a non web application could not start/use the Vaadin navigator.
A possible solution would be to note that Vaadin configuration information outside of the web.xml file. A VaadinConfig.xml (or Vaadin.cfg.xml or Vaadin.properties or whatever) file.
Either we should give the name/location of the file in the web.xml or in the main method of the batch (as Hibernate would do for hibernate.cfg.xml), either we use conventions (as Servlet does for web.xml or JPA does for persistence.xml).
Another solution (than having a config file) would be to annotate the BlackBeltWebApplication class @org.vaadin.Application and have powerful annotation detection code to find it (what do we do if we find two?).
The WebApplication descendant may contain much application specific settings (as the name of the pages), but we need to bootstrap it someway.
Please keep that in mind when doing Vaadin 7.
Problem 2: Absolute URLs
The code of BlackBeltUriAnalyzer.getFullUrl is application specific and simple:
/** With the domain name */
public static String getFullUrl(ExternalResource resource) {
return "http://www.blackbeltfactory.com/ui" + resource.getURL();
}
Any better or more generic suggestion?
The same problem arises with v6 File upload.
Upload.SucceededListener
Vaadin is not "completely initialized". File upload requests don't go through the Vaadin servlet (it's descendant, the nav7 NavigableApplicationServlet), which means that Nav7 is not properly initialized. We could make Nav7 self defensive enough to handle that, but ... user's code is not supposed to avoid getting the WebApplication through the ThreadLocal variable.
WebApplication.getCurrent()
Is it possible to fix that with elegance in Vaadin v7 ?
I've implemented (v7.44) the following work-around: a static variable.
Your WebApplication sub class is instantiated only once (per JVM and per ServletContext) and is storted in both the ServletContext and that static variable.
See the javadoc below for further discussion.
public class WebApplication {
public static final String WEBAPPLICATION_CONTEXT_ATTRIBUTE_NAME = WebApplication.class.getName();
/** Holds the ServletContext instance of the current thread.
* If it's null for the current thread, we are not in a web thread but in a batch thread.
*
* We need the ServletContext because we store the WebApplication instance there.
*
* @See http://vaadin.com/forum/-/message_boards/message/155212?_19_delta=10&_19_keywords=&_19_advancedSearch=false&_19_andOperator=true&cur=2#_19_message_166110
**/
static protected ThreadLocal<ServletContext> currentServletContext = new ThreadLocal<ServletContext>();
/** A batch thread may need the WebApplication instance.
* For example, to build an url (with new ParamPageResource(MyPage.class, myEntity)).
* If this happens, we can get and instance of the WebApplication here (currentServletContext will return null).
*
* This is unfortunate, but somehow dictated by the Servlet specification. Good servlet container practice tells to put the reference in the ServletContext. Batch needs tell to put it in a static variable (singleton).
* An improvement would be to store the name of the WebApplication subclass (and all the Vaadin config) in a more neutral place than the web.xml.
* That way, if the batch thread starts before the first web request (thread) arrives, or if the batch executes outside of the web contained, then we could instantiate from the batch thread (if not instantiated yet, of course).
*
* There is probably a difficult design choice to be made by Vaadin guys here, to integrate Navigator7 into Vaadin7 (difficult design choice to have the notion of Web app at all in Vaadin).
* Keeping the instance in both the ServletContext and a static variable is an overlap.
* Keeping it in the ServletContext only is not enough (would make batch jobs fail).
* Maybe keeping it in this static reference only?
* Note that frameworks as Spring have the same (unsolved as far as I know) problem.
* In web applications, they keep the Spring ApplicationContext in the ServletContext.
* As Quartz is usually started by Spring, it's easy for Spring to pass its (Web)ApplicationContext to Quartz,
* and so to batch jobs. But batch jobs have to do some gym to extract the Spring (Web)ApplicationContext from Quartz,
* it's not transparent.
* We don't have that possibility here (Vaadin does not start batch jobs).
* Maybe somebody will have a better idea ;-)
*/
static protected WebApplication staticReference;
/** Don't hesitate to use this method ;-)
* Returns null if we are not in a web thread (or a badly initialized web app) */
public static WebApplication getCurrent() {
ServletContext servletContext = currentServletContext.get();
if (servletContext == null) { // We are in a batch thread, probably: http://vaadin.com/forum/-/message_boards/message/216481?_19_delta=10&_19_keywords=&_19_advancedSearch=false&_19_andOperator=true&cur=3#_19_message_216481
if (staticReference == null) {
throw new RuntimeException("WebApplication has not been instantiated yet. " +
"A common cause is that the thread executing this, is a batch thread, AND that the NavigableApplicationServlet has not bee initialized yet. " +
"Suggestions:" +
" delay your batch;" +
" or call WebApplication.init(YourWebApplication.class) manually at the beginning of your batch;" +
" or call WebApplication.init(YourWebApplication.class) in a ServletContextListener.contextInitialized() when your web application starts instead of NavigableApplicationServlet.init()");
} else {
return staticReference;
}
} else { // web thread (usual case).
return (WebApplication)servletContext.getAttribute(WEBAPPLICATION_CONTEXT_ATTRIBUTE_NAME);
}
}
If a batch job (thread) starts before the ServletContext is initialized (or if the batch job starts outside a web container), then the batch job will have to call WebApplication.init(MyWebApplication.class), to specify his WebApplication subclass, when it starts (if he wants to use Vaadin to build URLs).
Food for the brain: What is the notion of WebApplication? (what should it be in Vaadin 7?). We know what the Navigator7 class will instantiate (NavigatorConfig, ParamUriAnalyzer), but we don't know what the programmer using the framework will initialize in it's subclass... What if he starts stuff that should only be done in a web thread (and then instantiation from a batch thread would be unfortunate)? Should we provide him 2 entry points, one for generic application Initialization, and another for web application initialization? ...
Hi!
I just started testin Navigator7. I followed the BasicUsage -article on how to set things up but I failed on the very first command in the example, NavigableApplication has no registerPages()-method. With some code investigation I found that it had been moved to WebApplication, but how that works along with the rest of the classes still elude me. I suggest that you update the BasicUsage article to correspond to the current architecture.
You are completely right Jens.
I've improved that page and removed the links to the videos (that were hosted on sreentoaster which is closed).
Thanks.
Great!
I've been cruising along the source code and I want to clarify one thing to myself. Is it correct that every time you call navigateTo(class) a new instance of that view is created, instead of using an existing one?
Jens Jansson: Great!
I've been cruising along the source code and I want to clarify one thing to myself. Is it correct that every time you call navigateTo(class) a new instance of that view is created, instead of using an existing one?
I came to the same conclusion browsing the code. Because i did not want this, i always use getNavigator().invokeInterceptors(page, params, true) to navigate between pages, where the variable 'page' is one i create at the intialization of the app.
However, this only works when having a 'navigate-to-...'-button or menu in your app. For browsing between pages using urls, i had to built in a Interceptor and 'intercept' the navigation to a new instance of the page and overwrite it by instead generating a new PageInvocation of the old page and invoke that one.
I think this is something that should be possible in Vaadin7 without the hacks i'm using...
Is it correct that every time you call navigateTo(class) a new instance of that view is created, instead of using an existing one?
Yes, it is correct.
Imagine your application has 3 pages. User is on page1.
Then he clicks a link to go to page2. Or he clicks a button and in the listener you forward to page2 (navigateTo).
Page2 is instantiated for that user.
What should happen with page1? Would you keep it in a kind of cache (oh no!) ?
If from page2 or page3, you come back to page1, Nav7 reinstantiates page 1.
Pages instances are not shared accross Windows (browser's tabs) neither accross users.
That should, according to me, be the normal behavior.
If I understand well what you are trying to do, you need 2 stateful pages, and allow the user to switch between them often. You don't want to state of the pages to be lost for that user. Nav7 should keep these kind of pages in the Application (user context, not the Nav7 WebApplication) and reuse them. Is that what you want?
If it is what you want, I suggest to add an attribute to the @Page annotation.
@Page(keepAndReuse=true)
Hi John!
We are using your Navigator7 addon, but stumble upon an issue. In NavigableAppLevelWindow you are using a CssLayout, but I need a CustomLayout as container.
What would be the best way to implement this? Trying to extend AppLevelWindow results in an issue with NavigableApplication.createNewNavigableAppLevelWindow(), which returns a NavigableAppLevelWindow.
For now I implemented a patch to Navigator7 (see below), enabling me to extend NavigableAppLevelWindow.
navigator7 jpeeters$ svn diff src/org/vaadin/navigator7/window/NavigableAppLevelWindow.java
Index: src/org/vaadin/navigator7/window/NavigableAppLevelWindow.java
===================================================================
--- src/org/vaadin/navigator7/window/NavigableAppLevelWindow.java (revision 32)
+++ src/org/vaadin/navigator7/window/NavigableAppLevelWindow.java (working copy)
@@ -17,7 +17,7 @@
*/
public abstract class NavigableAppLevelWindow extends AppLevelWindow {
- private Navigator navigator;
+ protected Navigator navigator;
protected Component page; // Current page being displayed. null if no page set yet.
public ComponentContainer pageContainer; // Contains page (there could be no page yet, so we cannot rely on this.page.getParent() because this.page could be null. Instantiated by descendants.
Hi Johaness,
Thanks for using Nav7, I love to get real world feedback.
NavigableAppLevelWindow does not play with CssLayouts.
It's the descendant: FixedAppLevelWindow. You can decide to extend NavigableAppLevelWindow with YourNavigableAppLevelWindow and put the layout constructs that you want.
It would help me to know what you are trying to do (why a CustomLayout?).
Do you need a fixed (width) web layout? (a column with 1000 pixels to contain all your pages)
Do you need a header and a footer?
I've not understood your patch (can't see what you have changed in the code).
Have a good day,
John.
Hi John,
Thanks for the feedback.
In the attach() method of NavigableAppLevelWindow you set the content of the Window to a CssLayout and create the Navigator object.
Layout main = new CssLayout();
main.addStyleName("PageTemplate-mainLayout");
// main.setWidth("100%"); If you do that instead of the addStyleName (containing a width:100%;) above, Vaadin JavaScript will recompute the width everytime you resize the browser.
this.setContent(main);
// Must be done after calling this.setConent(main), as for any component added to the window.
this.navigator = new Navigator();
this.addComponent(navigator);
When subclassing NavigableAppLevelWindow with MyNavigableAppLevelWindow I can't override the attach() method since the navigator instance in NavigableAppLevelWindow is private
private Navigator navigator;
My patch makes navigator protected so I can set it in my overridden attach method. Hereby the current content of MyNavigableAppLevelWindow, for your reference
public class MyAppLevelWindow extends NavigableAppLevelWindow {
@Override
public void attach() {
// Main layout creation. Do that before you add anything to the Window.
CustomLayout main = new CustomLayout("main");
// Needed to prevent Vaadin setting the container size using JavaScript. We prefer CSS styling
main.setSizeUndefined();
this.setContent(main);
// Must be done after calling this.setConent(main), as for any component added to the window.
this.navigator = new Navigator();
this.navigator.addStyleName("navigator7");
// Add the navigator to every page, on a known location
main.addComponent(navigator, "navigator7");
pageContainer = createComponents(); // Let descendants add components in this.getContent().
}
@Override
protected ComponentContainer createComponents() {
// No need to do anything special here, just return the CustomLayout("main") set in attach().
return this.getContent();
}
@Override
public synchronized void changePage(Component pageParam) {
CustomLayout customLayout = (CustomLayout)pageContainer;
this.page = pageParam;
// add the page to the CustomLayout at the mainContainer location
customLayout.addComponent(page, "mainContainer");
}
}
We use CustomLayout to give our layout team full control of the CSS styles.
Ok, sorry, I missed the CssLayout in NavigableAppLevelWindow.
This is Nav7 original code:
public abstract class NavigableAppLevelWindow extends AppLevelWindow {
private Navigator navigator;
protected Component page; // Current page being displayed. null if no page set yet.
public ComponentContainer pageContainer; // Contains page (there could be no page yet, so we cannot rely on this.page.getParent() because this.page could be null. Instantiated by descendants.
@Override
public void attach() {
// Main layout creation. Do that before you add anything to the Window.
Layout main = new CssLayout();
main.addStyleName("PageTemplate-mainLayout");
// main.setWidth("100%"); If you do that instead of the addStyleName (containing a width:100%;) above, Vaadin JavaScript will recompute the width everytime you resize the browser.
this.setContent(main);
// Must be done after calling this.setConent(main), as for any component added to the window.
this.navigator = new Navigator();
this.addComponent(navigator);
pageContainer = createComponents(); // Let descendants add components in this.getContent().
pageContainer.addStyleName("FixedPageTemplate-bandOuterLayoutPage");
}
I made navigator protected instead of private, you have a good idea, thank you.
I made pageContainer protected instead of public.
I created the method "createMainLayout" that you may override to return your CustomLayout instead of the CssLayout
I moved this line to the FixedAppLevelWindow class:
pageContainer.addStyleName("FixedPageTemplate-bandOuterLayoutPage");
public abstract class FixedApplLevelWindow extends NavigableAppLevelWindow {
...
@Override
public void attach() {
super.attach();
pageContainer.addStyleName("FixedPageTemplate-bandOuterLayoutPage");
}
Your class MyNavigableAppLevelWindow may look like this now:
public class MyAppLevelWindow extends NavigableAppLevelWindow {
@Override
protected Layout createMainLayout() {
Layout layout = new CustomLayout("main");
// Needed to prevent Vaadin setting the container size using JavaScript. We prefer CSS styling
layout.setSizeUndefined();
retunr layout;
}
@Override
public void attach() {
super.attach();
this.navigator.addStyleName("navigator7");
// navigator is already in the main layout but here we specify the location
CustomLayout customLayout = (CustomLayout)pageContainer;
customLayout.addComponent(navigator, "navigator7");
}
@Override
public synchronized void changePage(Component pageParam) {
CustomLayout customLayout = (CustomLayout)pageContainer;
this.page = pageParam;
// add the page to the CustomLayout at the mainContainer location
customLayout.addComponent(page, "mainContainer");
}
}
It's it ok with you, I'll release a new version.
Hi John,
That looks good. I assume you are using Layout main = createMainLayout() in NavibableAppLevelWindow?
I'm fine with the commit.
Hi there!
First of, thank you for that great addon!
ApplicationConnection.updateVariable() method doesn't work in my vaadin widget in Navigaror7 based application. I just can't get data from client side. When I change NavigableApplication to usual com.vaadin.Application it works fine. Where could be a problem?
Thank you!
Greg.
Hi guys,
For of all, thanks for this amazing Add-on.
I've got few questions regarding this Add-On and Spring.
We've got an application which is like this
An Auth module without Header/Footer
Once User is logged on we use Spring to find out implementation on modules the user is registered and build Application with Header and footer
Questions are :
* How to get access to application context before createComponents() method is called as Spring need it ? I mean in ApplicationLevel Class extending HeaderFooterFixedAppLevelWindow
* How to modify Header and Footer Application "on the fly" as we need empty header/footer at the beggining
* Is it possible to keep in memory pages ? As each time we navigate to it, it seems pages are reloaded and we need to keep track of it because of long queries executed on some pages.
* How to keep variables in AppLevel class and access it in pages --> use NavigableApplication.getCurrent();
* And if you have some advice to send me when using Spring and Navigator7 u'll be welcome :)
Sorry for all theses questions, I'm still a noob at programming ;)
Hi Gregory,
Gregory Katkov:
First of, thank you for that great addon!
Thank you.
ApplicationConnection.updateVariable() method doesn't work in my vaadin widget in Navigaror7 based application. I just can't get data from client side. When I change NavigableApplication to usual com.vaadin.Application it works fine. Where could be a problem?
I'm afraid that I've no idea. My GWT and client side skills are very poor. I don't see what in Nav7 could interfer with C/S communications. I suggest you to ask the question in another thread giving more details (what you've found out so far).
John.
For of all, thanks for this amazing Add-on.
Thank you Anthony. Feel free to vote 5* on it ;-)
An Auth module without Header/Footer
Once User is logged on we use Spring to find out implementation on modules the user is registered and build Application with Header and footer
I guess that Vaadin needs the Application instance very early. You probably leave it "empty" until you know the user and then create visual components for header/footer, attached to the Window, attached to the Application.
Questions are :
* How to get access to application context before createComponents() method is called as Spring need it ? I mean in ApplicationLevel Class extending HeaderFooterFixedAppLevelWindow
Sorry, I don't understand this one. Maybe my replies to the other questions will reply this?
* How to modify Header and Footer Application "on the fly" as we need empty header/footer at the beggining
You have an example of modifying the header in the sample application with Nav7. It shows you part of the URI if I remember well.
///// NavigationListener label
final Label navLabel = new Label();
navLabel.setWidth(null);
header.addComponent(navLabel);
header.setComponentAlignment(navLabel, Alignment.TOP_RIGHT);
((MyWebApplication)MyWebApplication.getCurrent())
.getPageChangeListenerInterceptor()
.addPageChangeListener( new PageChangeListener() {
@Override public void pageChanged(NavigationEvent event) {
navLabel.setValue("PageChangeListener: pageClass = "+ event.getPageClass() +
" -- params = " + event.getParams());
}
});
This example is probably more complex than what you need: it uses an interceptor to change the header at every request.
You have a very specific place (user login) to change the header.
Just store the header component as attribute in MyAppLevelWindow extends HeaderFooterFixedAppLevelWindow, and from the login code, call a method to fill it with the appropriate content.
Now, how from your login code, to get an instance to MyAppLevelWindow ?
NavigableApplication.getCurrentNavigableAppLevelWindow()
Simple.
But maybe not enough: what if your user did open multiple tabs before loging in. When he comes back to another tab, the header should be updated too. There is one instance of (MyAppLevel)Window per browser tab. This is more subtle.
This is some code from BlackBeltFactory application:
public static void refreshHeaders() {
for (Object w : BlackBeltApplication.getCurrent().getWindows()) { // For all the windows of the current session.
if (w instanceof BlackBeltAppLevelWindow) {
((BlackBeltAppLevelWindow)w).getHeader().refresh();
}
}
}
* Is it possible to keep in memory pages ? As each time we navigate to it, it seems pages are reloaded and we need to keep track of it because of long queries executed on some pages.
I replied above in this thread:
http://vaadin.com/forum/-/message_boards/message/155212?_19_delta=10&_19_keywords=&_19_advancedSearch=false&_19_andOperator=true&cur=3#_19_message_224760
but nobody confirmed that it would help, so I've not implemented it yet.
* How to keep variables in AppLevel class and access it in pages --> use NavigableApplication.getCurrent();
Yes. It's an important method.
WebApplication.getCurrent() is probably more important (and things not specific to a user should be kept there).
* And if you have some advice to send me when using Spring and Navigator7 u'll be welcome
Not for Nav7. For Vaadin in general, I suggest you to use @Configurable(preConstruction=true) on your Vaadin components (as Header, or MyPage,...) and link to your Spring beans (service and DAO layer) with @Autowired. It will require you to install AJDT in Eclipse and to add weaving in your ant build.
Sorry for all theses questions, I'm still a noob at programming ;)
You are welcome. These questions show that you are no noob at programming.
O_o !
What a good support !
It has definitely opened my eyes on how to use it !
Thanks a lot, I'll try all your advices and'll come back for feedbacks :)
[Edit : +5 stars rated :) ]