You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

advanced-urifu.asciidoc 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. ---
  2. title: Managing URI Fragments
  3. order: 11
  4. layout: page
  5. ---
  6. [[advanced.urifu]]
  7. = Managing URI Fragments
  8. A major issue in AJAX applications is that as they run in a single web page,
  9. bookmarking the application URL (or more generally the __URI__) can only
  10. bookmark the application, not an application state. This is a problem for many
  11. applications, such as product catalogs and discussion forums, in which it would
  12. be good to provide links to specific products or messages. Consequently, as
  13. browsers remember the browsing history by URI, the history and the
  14. [guibutton]#Back# button do not normally work. The solution is to use the
  15. __fragment identifier__ part of the URI, which is separated from the primary
  16. part (address + path + optional query parameters) of the URI with the hash (#)
  17. character. For example:
  18. ----
  19. http://example.com/path#myfragment
  20. ----
  21. The exact syntax of the fragment identifier part is defined in RFC 3986
  22. (Internet standard STD 66) that defines the URI syntax. A fragment may only
  23. contain the regular URI __path characters__ (see the standard) and additionally
  24. the slash and the question mark.
  25. Vaadin offers two ways to enable the use of URI fragments: the high-level
  26. [classname]#Navigator# utility described in
  27. <<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator,"Navigating
  28. in an Application">> and the low-level API described here.
  29. [[advanced.urifu.setting]]
  30. == Setting the URI Fragment
  31. You can set the current fragment identifier with the
  32. [methodname]#setUriFragment()# method in the [classname]#Page# object.
  33. [source, java]
  34. ----
  35. Page.getCurrent().setUriFragment("mars");
  36. ----
  37. Setting the URI fragment causes an [interfacename]#UriFragmentChangeEvent#,
  38. which is processed in the same server request. As with UI rendering, the URI
  39. fragment is changed in the browser after the currently processed server request
  40. returns the response.
  41. Prefixing the fragment identifier with an exclamation mark enables the web
  42. crawler support described in <<advanced.urifu.crawling>>.
  43. [[advanced.urifu.reading]]
  44. == Reading the URI Fragment
  45. The current URI fragment can be acquired with the [methodname]#getUriFragment()#
  46. method from the current [classname]#Page# object. The fragment is known when the
  47. [methodname]#init()# method of the UI is called.
  48. [source, java]
  49. ----
  50. // Read initial URI fragment to create UI content
  51. String fragment = getPage().getUriFragment();
  52. enter(fragment);
  53. ----
  54. To enable reusing the same code when the URI fragment is changed, as described
  55. next, it is usually best to build the relevant part of the UI in a separate
  56. method. In the above example, we called an [methodname]#enter()# method, in a
  57. way that is similar to handling view changes with [classname]#Navigator#.
  58. [[advanced.urifu.listening]]
  59. == Listening for URI Fragment Changes
  60. After the UI has been initialized, changes in the URI fragment can be handled
  61. with a [interfacename]#UriFragmentChangeListener#. The listeners are called when
  62. the URI fragment changes, but not when the UI is initialized, where the current
  63. fragment is available from the page object as described earlier.
  64. For example, we could define the listener as follows in the [methodname]#init()#
  65. method of a UI class:
  66. [source, java]
  67. ----
  68. public class MyUI extends UI {
  69. @Override
  70. protected void init(VaadinRequest request) {
  71. getPage().addUriFragmentChangedListener(
  72. new UriFragmentChangedListener() {
  73. public void uriFragmentChanged(
  74. UriFragmentChangedEvent source) {
  75. enter(source.getUriFragment());
  76. }
  77. });
  78. // Read the initial URI fragment
  79. enter(getPage().getUriFragment());
  80. }
  81. void enter(String fragment) {
  82. ... initialize the UI ...
  83. }
  84. }
  85. ----
  86. <<figure.advanced.urifu>> shows an application that allows specifying the menu
  87. selection with a URI fragment and correspondingly sets the fragment when the
  88. user selects a menu item.
  89. [[figure.advanced.urifu]]
  90. .Application State Management with URI Fragment Utility
  91. image::img/urifu-1.png[]
  92. [[advanced.urifu.crawling]]
  93. == Supporting Web Crawling
  94. Stateful AJAX applications can not normally be crawled by a search engine, as
  95. they run in a single page and a crawler can not navigate the states even if URI
  96. fragments are enabled. The Google search engine and crawler
  97. link:http://googlewebmastercentral.blogspot.fi/2009/10/proposal-for-making-ajax-crawlable.html[support
  98. a convention] where the fragment identifiers are prefixed with exclamation mark,
  99. such as [literal]#++#!myfragment++#. The servlet needs to have a separate
  100. searchable content page accessible with the same URL, but with a
  101. [literal]#++_escaped_fragment_++# parameter. For example, for
  102. [literal]#++/myapp/myui#!myfragment++# it would be
  103. [literal]#++/myapp/myui?_escaped_fragment_=myfragment++#.
  104. You can provide the crawl content by overriding the [methodname]#service()#
  105. method in a custom servlet class. For regular requests, you should call the
  106. super implementation in the [classname]#VaadinServlet# class.
  107. [source, java]
  108. ----
  109. public class MyCustomServlet extends VaadinServlet
  110. @Override
  111. protected void service(HttpServletRequest request,
  112. HttpServletResponse response)
  113. throws ServletException, IOException {
  114. String fragment = request
  115. .getParameter("_escaped_fragment_");
  116. if (fragment != null) {
  117. response.setContentType("text/html");
  118. Writer writer = response.getWriter();
  119. writer.append("<html><body>"+
  120. "<p>Here is some crawlable "+
  121. "content about " + fragment + "</p>");
  122. // A list of all crawlable pages
  123. String items[] = {"mercury", "venus",
  124. "earth", "mars"};
  125. writer.append("<p>Index of all content:</p><ul>");
  126. for (String item: items) {
  127. String url = request.getContextPath() +
  128. request.getServletPath() +
  129. request.getPathInfo() + "#!" + item;
  130. writer.append("<li><a href='" + url + "'>" +
  131. item + "</a></li>");
  132. }
  133. writer.append("</ul></body>");
  134. } else
  135. super.service(request, response);
  136. }
  137. }
  138. ----
  139. The crawlable content does not need to be human readable. It can provide an
  140. index of links to other application states, as we did in the example above. The
  141. links should use the " [literal]#++#!++#" notation, but can not be relative to
  142. avoid having the [literal]#++_escaped_fragment_++# parameter.
  143. You need to use the custom servlet class in the [filename]#web.xml# deployment
  144. descriptor instead of the normal [classname]#VaadinServlet# class, as described
  145. in
  146. <<dummy/../../../framework/application/application-environment#application.environment.web-xml,"Using
  147. a web.xml Deployment Descriptor">>.