123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- ---
- title: Managing URI Fragments
- order: 11
- 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
- <<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 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:
-
-
- ----
- http://example.com/path#myfragment
- ----
-
- The exact syntax of the fragment identifier part is defined in RFC 3986
- (Internet standard STD 66) that defines the URI syntax. A fragment may only
- contain the regular URI __path characters__ (see the standard) and additionally
- 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
- <<advanced-navigator#advanced.navigator,"Navigating
- 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
-
- You can set the current fragment identifier with the
- [methodname]#setUriFragment()# method in the [classname]#Page# object.
-
-
- [source, java]
- ----
- Page.getCurrent().setUriFragment("mars");
- ----
-
- Setting the URI fragment causes an [interfacename]#UriFragmentChangeEvent#,
- which is processed in the same server request. As with UI rendering, the URI
- fragment is changed in the browser after the currently processed server request
- returns the response.
-
- Prefixing the fragment identifier with an exclamation mark enables the web
- crawler support described in <<advanced.urifu.crawling>>.
-
-
- [[advanced.urifu.reading]]
- == Reading the URI Fragment
-
- The current URI fragment can be acquired with the [methodname]#getUriFragment()#
- method from the current [classname]#Page# object. The fragment is known when the
- [methodname]#init()# method of the UI is called.
-
-
- [source, java]
- ----
- // Read initial URI fragment to create UI content
- String fragment = getPage().getUriFragment();
- enter(fragment);
- ----
-
- To enable reusing the same code when the URI fragment is changed, as described
- next, it is usually best to build the relevant part of the UI in a separate
- method. In the above example, we called an [methodname]#enter()# method, in a
- way that is similar to handling view changes with [classname]#Navigator#.
-
-
- [[advanced.urifu.listening]]
- == Listening for URI Fragment Changes
-
- After the UI has been initialized, changes in the URI fragment can be handled
- with a [interfacename]#UriFragmentChangeListener#. The listeners are called when
- the URI fragment changes, but not when the UI is initialized, where the current
- fragment is available from the page object as described earlier.
-
- 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().addUriFragmentChangedListener(
- new UriFragmentChangedListener() {
- public void uriFragmentChanged(
- UriFragmentChangedEvent source) {
- enter(source.getUriFragment());
- }
- });
-
- // Read the initial URI fragment
- enter(getPage().getUriFragment());
- }
-
- void enter(String fragment) {
- ... initialize the UI ...
- }
- }
- ----
-
- <<figure.advanced.urifu>> shows an application that allows specifying the menu
- selection with a URI fragment and correspondingly sets the fragment when the
- user selects a menu item.
-
- [[figure.advanced.urifu]]
- .Application State Management with URI Fragment Utility
- image::img/urifu-1.png[]
-
-
- [[advanced.urifu.crawling]]
- == Supporting Web Crawling
-
- Stateful AJAX applications can not normally be crawled by a search engine, as
- they run in a single page and a crawler can not navigate the states even if URI
- fragments are enabled. The Google search engine and crawler
- link:http://googlewebmastercentral.blogspot.fi/2009/10/proposal-for-making-ajax-crawlable.html[support
- a convention] where the fragment identifiers are prefixed with exclamation mark,
- such as [literal]#++#!myfragment++#. The servlet needs to have a separate
- searchable content page accessible with the same URL, but with a
- [literal]#++_escaped_fragment_++# parameter. For example, for
- [literal]#++/myapp/myui#!myfragment++# it would be
- [literal]#++/myapp/myui?_escaped_fragment_=myfragment++#.
-
- You can provide the crawl content by overriding the [methodname]#service()#
- method in a custom servlet class. For regular requests, you should call the
- super implementation in the [classname]#VaadinServlet# class.
-
-
- [source, java]
- ----
- public class MyCustomServlet extends VaadinServlet
- @Override
- protected void service(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- String fragment = request
- .getParameter("_escaped_fragment_");
- if (fragment != null) {
- response.setContentType("text/html");
- Writer writer = response.getWriter();
- writer.append("<html><body>"+
- "<p>Here is some crawlable "+
- "content about " + fragment + "</p>");
-
- // A list of all crawlable pages
- String items[] = {"mercury", "venus",
- "earth", "mars"};
- writer.append("<p>Index of all content:</p><ul>");
- for (String item: items) {
- String url = request.getContextPath() +
- request.getServletPath() +
- request.getPathInfo() + "#!" + item;
- writer.append("<li><a href='" + url + "'>" +
- item + "</a></li>");
- }
- writer.append("</ul></body>");
- } else
- super.service(request, response);
- }
- }
- ----
-
- The crawlable content does not need to be human readable. It can provide an
- index of links to other application states, as we did in the example above. The
- links should use the " [literal]#++#!++#" notation, but can not be relative to
- avoid having the [literal]#++_escaped_fragment_++# parameter.
-
- You need to use the custom servlet class in the [filename]#web.xml# deployment
- descriptor instead of the normal [classname]#VaadinServlet# class, as described
- in
- <<../application/application-environment#application.environment.web-xml,"Using
- a web.xml Deployment Descriptor">>.
-
-
-
|