aboutsummaryrefslogtreecommitdiffstats
path: root/documentation/articles/CreatingABookmarkableApplicationWithBackButtonSupport.asciidoc
blob: 52dd8ef0933c8995a6bce0ca14db5d523f9039bb (plain)
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
---
title: Creating A Bookmarkable Application With Back Button Support
order: 81
layout: page
---

[[creating-a-bookmarkable-application-with-back-button-support]]
= Creating a bookmarkable application with back button support

Vaadin 7 comes with a new set of APIs to aid creation of navigation
within your application. The main concepts are *Navigator* and *View*,
and using these you can easily create an application that supports the
standard browser methods for navigation; bookmarking, history, back- and
forward navigation using browser buttons. This is (usually) done using
browser "fragments" (the stuff after the #-character in the URI).

At the same time, the API provides a natural way of partitioning your
application into views - something most applications did previously
anyway, but previously without framework 'guidance'.

Let's start by making a View that counts the times it has been created.
This is a simple example, but will later shed some light on when Views
are created, but let's not worry about that just yet:

[source,java]
....
import com.vaadin.navigator.View;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;

public class CountView extends Panel implements View {
  public static final String NAME = "count";

  private static int count = 1;

  public CountView() {
    setContent(new Label("Created: " + count++));
  }

  public void enter(ViewChangeEvent event) {
  }
}
....

We'll extend Panel as a convenient base, and add a Label to that in the
constructor, updating the static count. The _enter()_ -method comes from
View, and is called when our View is activated, but we'll do nothing
about that in our simplistic View.

Note the _static final NAME_: we'll use it instead of a 'magic' string
when we register the View with the Navigator later. Feel free to use any
method you like to keep track of your View-names (e.g Enum, simpleName
of the View's class, and so on…)

In order to do any navigating, we'll need at least two views, so let's
create a main view that has a link to the counting view we just created.

[source,java]
....
import com.vaadin.navigator.View;
import com.vaadin.server.ExternalResource;
import com.vaadin.ui.Link;
import com.vaadin.ui.Panel;

public class MainView extends Panel implements View {

  public static final String NAME = "";

  public MainView() {
    Link lnk = new Link("Count", new ExternalResource("#!"
        + CountView.NAME));
    setContent(lnk);
  }

  public void enter(ViewChangeEvent event) {
  }
}
....

Note the empty string used as _NAME_. This is because we want this to be
our main ("home") View, displayed before any navigation is done.

In this example we use a Link and let the browser do the navigating. We
could just as easily use a Button and tell the Navigator where we want
to go when the button's ClickListener is invoked. Note that we're using
_CountView.NAME_, and what we're actually doing is using the "fragment"
part of the application URI to indicate the view. The resulting URI will
look something like http://.../application#count .

Ok, one last thing: we need to set up a UI with a Navigator, and
register our views:

[source,java]
....
import com.vaadin.navigator.Navigator;
import com.vaadin.navigator.Navigator.SimpleViewDisplay;
import com.vaadin.server.Page;
import com.vaadin.server.WrappedRequest;
import com.vaadin.ui.UI;

public class NavigationtestUI extends UI {
  @Override
  public void init(VaadinRequest request) {
    // Create Navigator, use the UI content layout to display the views
    Navigator navigator = new Navigator(this, this);

    // Add some Views
    navigator.addView(MainView.NAME, new MainView()); // no fragment

    // #count will be a new instance each time we navigate to it, counts:
    navigator.addView(CountView.NAME, CountView.class);

    // The Navigator attached to the UI will automatically navigate to the initial fragment once
    // the UI has been initialized.
  }
}
....

There are advanced ways to use the Navigator API, and there are simple
ways. Most applications will do fine with the simple ways, and the
Navigator constructor we used is written that in mind. It simply takes
any ComponentContainer, assumes that all our Views are also Components,
and on a view change sets the given view as the ComponentContainer's
only child. Internally, it uses a _ViewDisplay_ subclass called
ComponentContainerViewDisplay to do this. If we had more advanced
requirements, we could write our own ViewDisplay subclass to show our
views in whatever fashion we'd like.

The Navigator finds out about URI fragment changes through the Page, and
directs the ViewDisplay accordingly. We register our Views using
_addView()_ so that the Navigator knows how to connect fragments with
Views. Again notice how we use the static NAME instead of
_addView("name", view)_ - but feel free to use other approaches.

In order to illustrate how the two differ, we register an _instance_ of
the MainView, but _CountView.class_. As a result, the MainView is
created once, when the UI is created, and lives as long as the UI lives.
On the other hand, a new CountView instance will be created each time we
navigate to it (but no earlier). You can try navigating back-and-forth
and see how the count is updated - try registering it using new
CountView() instead…

It's also good to keep in mind that a new UI is created each time you
press reload in the browser, unless you use the @PreserveOnRefresh
annotation on the UI.