diff options
author | Teemu Suo-Anttila <tsuoanttila@users.noreply.github.com> | 2017-09-27 11:40:17 +0300 |
---|---|---|
committer | Henri Sara <henri.sara@gmail.com> | 2017-09-27 11:40:17 +0300 |
commit | 367c7751a6ff9234fd47bc5a48e6ef9a4117a7a2 (patch) | |
tree | 5b3849bafb37b49c3dcc8616e064f60bd081dff0 /documentation | |
parent | 69776b1d08d40bcdd89b9cc5b050e8db793ec06b (diff) | |
download | vaadin-framework-367c7751a6ff9234fd47bc5a48e6ef9a4117a7a2.tar.gz vaadin-framework-367c7751a6ff9234fd47bc5a48e6ef9a4117a7a2.zip |
Add option to use PushState instead of URI fragments in Navigator (#10042)
* Navigator now by default uses pushState and normal URLs
* added documentation for pushState and updated Navigator documentation
* improving docs etc, adding one TODO to be solved before merging
* pushState/replaceState no work better with changing titles
* Making uri fragment navigator work when not using specially mapped UI
* Revert to older default, add annotation for selecting
* Fix tests, add null checks
* Reorder if-clause, fix tests
* Revert unnecessary test change
* Use correct variable in UI, fix test clean up
* Updates to JavaDocs, fix some methods and tests
* Add comments, fix test ui, TODO for fallbacks
* Navigation documentation, JavaDocs, removed TODOs
* Documentation fixes
* Improve JavaDocs
* Fix link name in documentation
* Improve throws declaration in getLocation
* Change documentation about the PushState based navigation
* Add since tags
* Add since tags for UI
Diffstat (limited to 'documentation')
4 files changed, 105 insertions, 43 deletions
diff --git a/documentation/advanced/advanced-navigator.asciidoc b/documentation/advanced/advanced-navigator.asciidoc index 7ef5b1ac36..00fedd635e 100644 --- a/documentation/advanced/advanced-navigator.asciidoc +++ b/documentation/advanced/advanced-navigator.asciidoc @@ -10,9 +10,9 @@ layout: page Plain Vaadin applications do not have normal web page navigation as they usually run on a single page, as all Ajax applications do. Quite commonly, however, applications have different views between which the user should be able to -navigate. The [classname]#Navigator# in Vaadin can be used for most cases of -navigation. Views managed by the navigator automatically get a distinct URI -fragment, which can be used to be able to bookmark the views and their states +navigate. Also users often need to have direct links to specific views. The [classname]#Navigator# in Vaadin can be used for most cases of +navigation. Views managed by the navigator automatically get a distinct URI, +which can be used to be able to bookmark the views and their states and to go back and forward in the browser history. [[advanced.navigator.navigating]] @@ -61,17 +61,9 @@ public class NavigatorUI extends UI { } ---- -The [classname]#Navigator# automatically sets the URI fragment of the -application URL. It also registers a [interfacename]#URIFragmentChangedListener# -in the page +The [classname]#Navigator# automatically parses the URI to identify and show the [interfacename]#View#. The browser navigation back and forward are also handled by it. -ifdef::web[] -(see <<dummy/../../../framework/advanced/advanced-urifu#advanced.urifu,"Managing -URI -Fragments">>) -endif::web[] - to show the view identified by the URI fragment if entered or navigated to in -the browser. This also enables browser navigation history in the application. +Starting from [literal]#++8.2.0.alpha2++# there is also PushState based navigation. This new way for the Navigator to manage URLs is described here as well. It is still under development so changes are expected. To enable this feature, add the [classname]#PushStateNavigation# annotation to your UI. [[advanced.navigator.navigating.viewprovider]] === View Providers @@ -95,7 +87,7 @@ You can handle view changes also by implementing a [interfacename]#ViewChangeListener# and adding it to a [classname]#Navigator#. When a view change occurs, a listener receives a [classname]#ViewChangeEvent# object, which has references to the old and the activated view, the name of the -activated view, as well as the fragment parameters. +activated view, as well as the parameters (the part of of URI after the viewname). @@ -104,7 +96,7 @@ activated view, as well as the fragment parameters. Views can be any objects that implement the [interfacename]#View# interface. When the [methodname]#navigateTo()# is called for the navigator, or the -application is opened with the URI fragment associated with the view, the +application is opened with the URI associated with the view, the navigator switches to the view and calls its [methodname]#enter()# method. To continue with the example, consider the following simple start view that just @@ -143,21 +135,23 @@ latter method is that the view is attached to the view container as well as to the UI at that time, which is not the case in the constructor. -[[advanced.navigator.urifragment]] -== Handling URI Fragment Path +[[advanced.navigator.pathparam]] +== Handling Path Parameters -URI fragment part of a URL is the part after a hash [literal]#++#++# character. -Is used for within-UI URLs, because it is the only part of the URL that can be -changed with JavaScript from within a page without reloading the page. The URLs -with URI fragments can be used for hyperlinking and bookmarking, as well as -browser history, just like any other URLs. In addition, an exclamation mark -[literal]#++#!++# after the hash marks that the page is a stateful AJAX page, -which can be crawled by search engines. Crawling requires that the application -also responds to special URLs to get the searchable content. URI fragments are -managed by [classname]#Page#, which provides a low-level API. +By default the URLs managed through the [classname]#Navigator# have a URI fragment +that contains the identifier of the [interfacename]#View#. The URI fragment is +separated from the rest of the URL by a [literal]#++#++# character. -URI fragments can be used with [classname]#Navigator# in two ways: for -navigating to a view and to a state within a view. The URI fragment accepted by +If the [classname]#PushStateNavigation# annotation is present on the [classname]#UI# +the HTML5 History API is used. When using the PushState, the identifier is separated +from the root path by a [literal]#++/++# like a real URL. + +In addition to the View identifier, URI can contain additional parameters to be +passed to views. The parameters are the part of the URI after the longest matching view identifier, separated by [literal]#++/++#. These parameters together with the identifier +form the __navigation state__. + +The navigation state can be used with [classname]#Navigator# in two ways: for +navigating to a view and to a state within a view. The navigation state accepted by [methodname]#navigateTo()# can have the view name at the root, followed by fragment parameters after a slash (" [literal]#++/++#"). These parameters are passed to the [methodname]#enter()# method in the [interfacename]#View#. @@ -202,7 +196,7 @@ public class MainView extends VerticalLayout implements View { navigator.navigateTo(MAINVIEW + "/" + menuitem); } } - + VerticalLayout menuContent; Panel equalPanel; Button logout; @@ -224,19 +218,19 @@ public class MainView extends VerticalLayout implements View { new ButtonListener("sheep"))); // Allow going back to the start - logout.addClickListener(event -> // Java 8 + logout.addClickListener(event -> navigator.navigateTo("")); - } - + } + @DesignRoot class AnimalViewer extends VerticalLayout { Label watching; Embedded pic; Label back; - + public AnimalViewer(String animal) { Design.read(this); - + watching.setValue("You are currently watching a " + animal); pic.setSource(new ThemeResource( @@ -274,12 +268,8 @@ The animal sub-view would have the following declarative design: ---- The main view is shown in <<figure.advanced.navigator.mainview>>. At this point, -the URL would be [literal]#++http://localhost:8080/myapp#!main/reindeer++#. +the URL would be [literal]#++http://localhost:8080/myapp/main/reindeer++#. [[figure.advanced.navigator.mainview]] .Navigator Main View image::img/navigator-mainview.png[] - - - - diff --git a/documentation/advanced/advanced-pushstate.asciidoc b/documentation/advanced/advanced-pushstate.asciidoc new file mode 100644 index 0000000000..a29ce865de --- /dev/null +++ b/documentation/advanced/advanced-pushstate.asciidoc @@ -0,0 +1,69 @@ +--- +title: Mananipulating browser history +order: 11 +layout: page +--- + +[[advanced.pushstate]] += Mananipulating browser history + +A major issue in AJAX applications is that as they run in a single web page. +Bookmarking the application URL (or more generally the __URI__) can only +bookmark the application, not an application state. This is a problem for many +applications, such as product catalogs and discussion forums, in which it would +be good to provide links to specific products or messages. The solution is to +modify the URI of the browser using https://developer.mozilla.org/en-US/docs/Web/API/History_API[History APIs] +[methodname]#pushState# or [methodname]#replaceState# functions, whenever developer +wants to simulate a logical page change. There is a server side API for those +methods and a mechanism to listen to changes in the client side URI in the +[classname]#Page# object. + +Vaadin offers two ways to modify URIs: the high-level +[classname]#Navigator# utility described in +<<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator,"Navigating +in an Application">> and the low-level API described here. + +[[advanced.urifu.setting]] +== Setting the URL displayed in the browser + +You can set the current fragment identifier with the +[methodname]#pushState()# method in the [classname]#Page# object. + + +[source, java] +---- +Page.getCurrent().pushState("mars"); +---- + +The parameter (both String and URI are supported) is resolved on the current URL. Both relative and absolute URIs are supported, but note that browsers accept only URLs of the same origin as the current URL. + +A call to _pushState_ creates a new entry to browsers history. If you wish to avoid this, and just replace the current URL in the browser, use the related [methodname]#replaceState# method. + + +[[advanced.pushstate.popstate]] +== Listening for "in-page" URI Changes + +If your application uses pushState to update the location and after that the user uses browsers back/forward button, a full page reload does not happen and the UIs init method is not called like when entering the page for the first time. To detect these change you can use [interfacename]#PopChangeListener#. + +For example, we could define the listener as follows in the [methodname]#init()# +method of a UI class: + + +[source, java] +---- +public class MyUI extends UI { + @Override + protected void init(VaadinRequest request) { + getPage().addPopStateListener( e -> enter() ); + + // Read the initial URI fragment + enter(); + } + + void enter() { + URI location = getPage().getLocation(); + ... initialize the UI ... + } +} +---- + diff --git a/documentation/advanced/advanced-urifu.asciidoc b/documentation/advanced/advanced-urifu.asciidoc index e245c8e727..e3b6b5c1ba 100644 --- a/documentation/advanced/advanced-urifu.asciidoc +++ b/documentation/advanced/advanced-urifu.asciidoc @@ -7,13 +7,16 @@ layout: page [[advanced.urifu]] = Managing URI Fragments +NOTE: This chapter contains instructions how to manage URI fragments. As browser support for HTML5 History API has improved, developers should in most cases developers instead use real URIs with _pushState_ method. Read more from +<<dummy/../../../framework/advanced/advanced-navigator#advanced.pushstate,"Manipulating Browser History">>. + A major issue in AJAX applications is that as they run in a single web page, bookmarking the application URL (or more generally the __URI__) can only bookmark the application, not an application state. This is a problem for many applications, such as product catalogs and discussion forums, in which it would be good to provide links to specific products or messages. Consequently, as browsers remember the browsing history by URI, the history and the -[guibutton]#Back# button do not normally work. The solution is to use the +[guibutton]#Back# button do not normally work. The solution before HTML5 API was available was to use the __fragment identifier__ part of the URI, which is separated from the primary part (address + path + optional query parameters) of the URI with the hash (#) character. For example: @@ -31,7 +34,7 @@ the slash and the question mark. Vaadin offers two ways to enable the use of URI fragments: the high-level [classname]#Navigator# utility described in <<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator,"Navigating -in an Application">> and the low-level API described here. +in an Application">> (if the legacy [classname]#UriFragmentManager# is configured for the Navigator) and the low-level API described here. [[advanced.urifu.setting]] == Setting the URI Fragment diff --git a/documentation/application/application-declarative.asciidoc b/documentation/application/application-declarative.asciidoc index 9bffa4b371..c044b472ae 100644 --- a/documentation/application/application-declarative.asciidoc +++ b/documentation/application/application-declarative.asciidoc @@ -370,5 +370,5 @@ navigator.addView(MAINVIEW, new MainView()); ---- See -<<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator.urifragment,"Handling -URI Fragment Path">> for a complete example. +<<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator.pathparam,"Handling +Path Parameters">> for a complete example. |