1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
---
title: Managing URI Fragments
order: 11
layout: page
---
[[advanced.urifu]]
= Managing URI Fragments
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
__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
<<dummy/../../../framework/advanced/advanced-navigator#advanced.navigator,"Navigating
in an Application">> 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
<<dummy/../../../framework/application/application-environment#application.environment.web-xml,"Using
a web.xml Deployment Descriptor">>.
|