summaryrefslogtreecommitdiffstats
path: root/documentation/articles/ViewChangeConfirmations.asciidoc
diff options
context:
space:
mode:
authorErik Lumme <erik@vaadin.com>2017-09-13 14:20:22 +0300
committerErik Lumme <erik@vaadin.com>2017-09-13 14:20:22 +0300
commitfa7a1dfc62ac38c164bef4d9d99f1ae8b76f77e5 (patch)
tree66828ba1605c198b1d378f982b596355b29c28e4 /documentation/articles/ViewChangeConfirmations.asciidoc
parent7a403fd723d135b27d2d5225db916d2502ffb3e8 (diff)
downloadvaadin-framework-fa7a1dfc62ac38c164bef4d9d99f1ae8b76f77e5.tar.gz
vaadin-framework-fa7a1dfc62ac38c164bef4d9d99f1ae8b76f77e5.zip
Migrate ViewChangeConfirmations
Diffstat (limited to 'documentation/articles/ViewChangeConfirmations.asciidoc')
-rw-r--r--documentation/articles/ViewChangeConfirmations.asciidoc220
1 files changed, 220 insertions, 0 deletions
diff --git a/documentation/articles/ViewChangeConfirmations.asciidoc b/documentation/articles/ViewChangeConfirmations.asciidoc
new file mode 100644
index 0000000000..5beda623eb
--- /dev/null
+++ b/documentation/articles/ViewChangeConfirmations.asciidoc
@@ -0,0 +1,220 @@
+[[view-change-confirmations]]
+View change confirmations
+-------------------------
+
+The `Navigator` API provides ways to prevent the user from navigating away
+from a view in some cases, usually when the view has some unsaved
+changes. We'll make a simple example that does just that (and only
+that).
+
+We'll create our simple `SettingsView` later, because it has the actual
+meat of this example. Let's set up the basic stuff first, our UI and our
+`MainView`.
+
+UI:
+
+[source,java]
+....
+import com.vaadin.navigator.Navigator;
+import com.vaadin.navigator.ViewChangeListener;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.ui.UI;
+
+public class NavigationtestUI extends UI {
+ @Override
+ public void init(VaadinRequest request) {
+ // Create Navigator, make it control the ViewDisplay
+ Navigator navigator = new Navigator(this, this);
+
+ // no fragment for main view
+ navigator.addView(MainView.NAME, new MainView(navigator));
+
+ // #settings
+ navigator.addView(SettingsView.NAME, new SettingsView(navigator));
+ }
+}
+....
+
+Minimalistic. The only thing to notice is that we pass the `Navigator` to
+the `SettingsView`, so that it can attach a listener and trigger
+navigation. More on that when we actually create the `SettingsView`.
+
+Let's do the `MainView`:
+
+[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(final Navigator navigator) {
+ Link lnk = new Link("Settings", new ExternalResource("#!"
+ + SettingsView.NAME));
+ setContent(lnk);
+ }
+
+ @Override
+ public void enter(ViewChangeEvent event) {
+ }
+}
+....
+
+Yeah, really nothing to see here - we just create this so we can
+navigate back and forth when trying it out.
+
+Now let's do the SettingsView, which has some more things going on in
+order to make it fairly complete:
+
+[source,java]
+....
+import java.util.Date;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.util.ObjectProperty;
+import com.vaadin.navigator.Navigator;
+import com.vaadin.navigator.View;
+import com.vaadin.navigator.ViewChangeListener;
+import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.DateField;
+import com.vaadin.ui.InlineDateField;
+import com.vaadin.ui.Layout;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.Notification.Type;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.themes.Reindeer;
+
+public class SettingsView extends Panel implements View {
+
+ public static String NAME = "settings";
+
+ Navigator navigator;
+ DateField date;
+ Button apply;
+ Button cancel;
+
+ String pendingViewAndParameters = null;
+
+ public SettingsView(final Navigator navigator) {
+ this.navigator = navigator;
+ Layout layout = new VerticalLayout();
+
+ date = new InlineDateField("Birth date");
+ date.setImmediate(true);
+ layout.addComponent(date);
+ // pretend we have a datasource:
+ date.setPropertyDataSource(new ObjectProperty<Date>(new Date()));
+ date.setBuffered(true);
+ // show buttons when date is changed
+ date.addValueChangeListener(new ValueChangeListener() {
+ public void valueChange(ValueChangeEvent event) {
+ hideOrShowButtons();
+ pendingViewAndParameters = null;
+ }
+ });
+
+ // commit the TextField changes when "Save" is clicked
+ apply = new Button("Apply", new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ date.commit();
+ hideOrShowButtons();
+ processPendingView();
+ }
+ });
+ layout.addComponent(apply);
+
+ // Discard the TextField changes when "Cancel" is clicked
+ cancel = new Button("Cancel", new Button.ClickListener() {
+ public void buttonClick(ClickEvent event) {
+ date.discard();
+ hideOrShowButtons();
+ processPendingView();
+ }
+ });
+ cancel.setStyleName(Reindeer.BUTTON_LINK);
+ layout.addComponent(cancel);
+
+ // attach a listener so that we'll get asked isViewChangeAllowed?
+ navigator.addViewChangeListener(new ViewChangeListener() {
+ public boolean beforeViewChange(ViewChangeEvent event) {
+ if (event.getOldView() == SettingsView.this
+ && date.isModified()) {
+
+ // save the View where the user intended to go
+ pendingViewAndParameters = event.getViewName();
+ if (event.getParameters() != null) {
+ pendingViewAndParameters += "/";
+ pendingViewAndParameters += event
+ .getParameters();
+ }
+
+ // Prompt the user to save or cancel if the name is changed
+ Notification.show("Please apply or cancel your changes",
+ Type.WARNING_MESSAGE);
+
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public void afterViewChange(ViewChangeEvent event) {
+ pendingViewAndParameters = null;
+ }
+ });
+
+ setContent(layout);
+ }
+
+ // Hide or show buttons depending on whether date is modified or not
+ private void hideOrShowButtons() {
+ apply.setVisible(date.isModified());
+ cancel.setVisible(date.isModified());
+ }
+
+ // if there is a pending view change, do it now
+ private void processPendingView() {
+ if (pendingViewAndParameters != null) {
+ navigator.navigateTo(pendingViewAndParameters);
+ pendingViewAndParameters = null;
+ }
+ }
+
+ @Override
+ public void enter(ViewChangeEvent event) {
+ hideOrShowButtons();
+ }
+}
+....
+
+First we set up a `DateField` with buffering and a (dummy) datasource to
+make this work more as a real application would. With buffering on, the
+value (date in this case) can be changed, but it will not be written to
+the datasource before we `commit()`, which is what the Save -button does.
+The Cancel -button does `discard()` on the DateField, which returns the
+field to its unmodified state.
+
+The buttons do not need to be shown if nothing has changed, so we add a
+`ValueChangeListener` to the `DateField` for that purpose.
+
+But the main thing that we're trying to demonstrate here happens in the
+`ViewChangeListener` that we attach to the `Navigator`. There, if we're
+about to change _away_ from our settings _and_ the date is changed but
+_not_ saved, we'll make note of where the user wanted to go, but cancel
+that navigation and prompt the user to save or cancel the changes.
+
+When the user saves or cancels changes, we also check if the user
+previously tried to navigate away form the page, and sends him on his
+way if that is the case.
+
+That is basically all there is to this. You'll notice we try to
+carefully clear or set the 'pending view' and hide/show the buttons at
+the right places to make the user happy, other than that this is pretty
+straightforward.