miércoles, 2 de enero de 2019

03. Routing

The examples from this post have been copied from the vaadin tutorial
This post is only instructional and is a bit boring as no application is created, but you may take it as simple reference card. Sorry folks!

1. @Route annotation

Here is a simple example of this annotation


1
2
3
4
5
6
@Route("")
public class HelloWorld extends Div {
  public HelloWorld() {
    setText("Hello world");
  }
}

This is shown in the URL http://localhost:8080/myProjectName

Where myProjectName can be used or not depending on the type of deployment (maybe in Jetty is not used but in Tomcat should be used)

@Route ("some/path") references http://localhost:8080/myProjectName/some/path

If the @Route annotation is not present, this criterion is used:

  1. The route is the class name but decapitalized ("MyClass" -> "myclass")
  2. The suffix "view" will be trimmed ("PersonView" -> "person")
  3. The suffix "main" will be trimmed ("MainPerson"-> "person",  "MainView"-> "")

2. Events

2.1 Before leaving a page...

Before leaving a page can be detected by means of "BeforeLeaveEvent" only if:
  1. The UI implements the "BeforeLeaveObserver" interface (preferred) or
  2. The UI executes the method addBeforeLeaveListener(BeforeLeaveListener)
This enables to change, deny or delay the navigation.

"BeforeLeaveEvent" has a method "postpone" that puts off the navigation until a condition is met. Then the "ContinueNavigationAction" method resumes the transition. Here is an example from vaadin manual


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class SignupForm extends Div implements BeforeLeaveObserver {
    @Override
    public void beforeLeave(BeforeLeaveEvent event) {
        if (this.hasChanges()) {
            ContinueNavigationAction action = event.postpone();
            ConfirmDialog.build("Are you sure you want to leave this page?")
                    .ifAccept(action::proceed).show();
        }
    }

    private boolean hasChanges() {
        // no-op implementation
        return true;
    }
}

2.2. Before entering a page ...

Before entering a page can be detected by means of "BeforeEnterEvent" only if
  1. The UI implements the "BeforeEnterObserverinterface (preferred) or
  2. The UI executes the method addBeforeEnterListener(BeforeEnterListener)
This enables to redirect or deny the navigation in case the user has not been logged or has no permission or maybe if there are no data to display.

This event has a method called "rerouteTo" in order to redirect the navigation. Here is an example from vaadin manual


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Class to be rerouted to..
@Route("no-items")
public class NoItemsView extends Div {
    public NoItemsView() {
        setText("No items found.");
    }
}

//Sender class..
@Route("blog")
public class BlogList extends Div implements BeforeEnterObserver {
    @Override
    public void beforeEnter(BeforeEnterEvent event) {
        // implementation omitted
        Object record = getItem();
        if (record == null)  event.rerouteTo(NoItemsView.class);
     }

    private Object getItem() {
        // no-op implementation
        return null;
    }
}

2.3. After navigating to a page ...

After navigating to a page can be detected by means of "AfterNavigationEvent" only if
  1. The UI implements the "AfterNavigationObserverinterface (preferred) or
  2. The UI executes the method addAfterNavigationListener(AfterNavigationListener)
This enables to updating various parts of the UI once the actual navigation has been completed. Here is an example from vaadin manual


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class SideMenu extends Div implements AfterNavigationObserver {
    Anchor blog = new Anchor("blog", "Blog");

    @Override
    public void afterNavigation(AfterNavigationEvent event) {
        boolean active = event.getLocation().getFirstSegment()
                .equals(blog.getHref());
        blog.getElement().getClassList().set("active", active);
    }
}

The order of the events are:

  1. BeforeLeaveEvent
  2. BeforeEnterEvent
  3. AfterNavigationEvent

3. Router Layouts

A RouterLayout component:

  1. Should implement RouterLayout interface
  2. Supplies his <body> for containing other components that have the property "layout" of the @Route annotation pointing to the parent component (the one implementing the  RouterLayout interface)
  3. Can nest multiple components, displaying only one child, when navigating from one child to another.

Here is the vaadin manual example of a component that uses a RouterLayout as parent. Notice the "@Route(layout property)"


1
2
3
4
@Tag("div")
@Route(value="company", layout=MainLayout.class)
public class CompanyComponent extends Component { 
}

3.1 Nested Router Layouts

There can be a MainLayout (implementing the RouterLayout interface) that can nest other RouterLayouts. The nested RouterLayouts uses the @ParentLayout that points to its parent Here is the vaadin tutorial example


 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
//1. Main RouterLayout 
public class MainLayout extends Div implements RouterLayout {
}

//2. Nested Routed Layout in the MainLayout
@ParentLayout(MainLayout.class)
public class MenuBar extends Div implements RouterLayout {
    public MenuBar() {
        addMenuElement(TutorialView.class, "Tutorial");
        addMenuElement(IconsView.class, "Icons");
    }
    private void addMenuElement(Class<? extends Component> navigationTarget,
            String name) {
        // implementation omitted
    }
}

//2.1 Nested layout 
@Route(value = "tutorial", layout = MenuBar.class)
public class TutorialView extends Div {
}

//2.2 Another nested layout
@Route(value="icons", layout = MenuBar.class)
public class IconsView extends Div {
}

3.2 Route prefixes to RouterLayouts

This is accomplished using the @RoutePrefix("prefix-route") to the RouterLayout. In this example from vaadin manual, the child component can be accessed by the complete route "some/path"


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//Access this component with route to "some/path"
@Route(value = "path", layout = SomeParent.class)
public class PathComponent extends Div {
    // Implementation omitted
}

// Adds the prefix "some" to the routes of its nested components
@RoutePrefix("some")
public class SomeParent extends Div implements RouterLayout {
    // Implementation omitted
}

3.3 Absolute routes

Setting the property absolute = true to

  1. @Route annotation or
  2. @RoutePrefix

can obviate parents' route. Here are the examples from vaadin tutorial


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//Main layout access by route "some"
@RoutePrefix("some")
public class SomeParent extends Div implements RouterLayout {
    // Implementation omitted
}
//Access this component with route to "content" and NOT to "some/content"
@Route(value = "content", layout = SomeParent.class, absolute = true)
public class MyContent extends Div {
    // Implementation omitted
}
//Access by route "framework" and NOT to "some/framework" 
@RoutePrefix(value = "framework", absolute = true)
@ParentLayout(SomeParent.class)
public class FrameworkSite extends Div implements RouterLayout {
    // Implementation omitted
}
// No absolute route so it is "framework/tutorial" and NOT "some/framework/tutorial"
@Route(value = "tutorial", layout = FrameworkSite.class)
public class Tutorials extends Div {
    // Implementation omitted
}

4. URL Parameters

4.1 Parameters

A class can accept parameters in the URL if extends the HasUrlParameter interface

In this example from vaadin tutorial, executing in the browser "http:8080/MyProject/greet/Emily" it displays "Hello, Emily" where Emily is the parameter


1
2
3
4
5
6
7
8
9
@Route(value = "greet")
public class GreetingComponent extends Div
        implements HasUrlParameter<String> {

    @Override
    public void setParameter(BeforeEvent event, String parameter) {
        setText(String.format("Hello, %s!", parameter));
    }
}

However, this URL will fail if the last parameter (Emily) is not included. It is solved using the @OptionalParameter annotation. The example code is


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Route("greet")
public class OptionalGreeting extends Div
        implements HasUrlParameter<String> {

    @Override
    public void setParameter(BeforeEvent event,
            @OptionalParameter String parameter) {
        //Will not fail with no parameters
        if (parameter == null) {
            setText("Welcome anonymous.");
        // Uses the parameter if included
        } else {
            setText(String.format("Welcome %s.", parameter));
        }
    }
}

If more than one parameter is sent, replace the @OptionalParameter annotation with @WildcardParameter 

4.2 Query parameters

We can use query parameters:
?name1=value1&name2=value2

To use it:
  1. Let's locate the method setParameter that is overridden from HasUrlParameter interface
  2. Get a Location instance from the BeforeEvent
  3. Get a QueryParameters instance from the Location instance
Here is the example


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Route("greet")
public class OptionalGreeting extends Div
        implements HasUrlParameter<String> {

    @Override
    public void setParameter(BeforeEvent event,
        @OptionalParameter String parameter) {

    Location location = event.getLocation();
    QueryParameters queryParameters = location.getQueryParameters();

    Map<String, List<String>> parametersMap = queryParameters.getParameters();
}

5. URL Generation

To get or produce the URL of any component (that implements the HasUrlParameter) you only have to use this statement for the case it has no parameters:

String myRoute=UI.getCurrent().getRouter.getUrl(MyComponent.class)

Note that the getUrl method applies to a Router instance

In case that there are some parameters, the statement is similar (here it is one parameter):

String myRoute=UI.getCurrent().getRouter.getUrl(MyComponent.class,"parameter_to_pass")

Now we can make a link to that URL  by means of an Anchor instance

Anchor link= new Anchor (myRoute,"Text to display in the link")

6. Navigation between routes

6.1 routerLink

With the RouterLink we can navigate to another component without knowing its URL route, only knowing its class.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Route(value = "rlexample")
//No need to use the route to the link, only the class name!!
public class RouterLinkExample extends VerticalLayout{
 public RouterLinkExample() {
     Div menu = new Div();
            // link to a component (MainView2) without parameters
     menu.add(new RouterLink("Option-1" , MainView2.class));
            // link to the component GreetingComponent with a parameter
     menu.add(new RouterLink("Option-2 ", GreetingComponent.class, "Ximo"));
            // link to the component GreetingComponent with a parameter
     menu.add(new RouterLink("Option-3 ", GreetingComponent.class, "Paco"));
     add(menu);
 }
}

//A component class to be routed.
@Route(value = "greet")
public class GreetingComponent extends Div
        implements HasUrlParameter<String> {
    @Override
    public void setParameter(BeforeEvent event, String parameter) {
        setText(String.format("Hello, %s!", parameter));
    }
}

When clicking "Option-2" we get "Hello, Ximo" and when clicking  "Option-3" we get "Hello, Paco"

6.2 Using html "<a href="company">"

RouterLink does not reload the page, but <a href ..> does!
<a router-link href="company"> also reloads the page

6.3 Navigation from the server side

There are several options:

  1. UI.navigate(String) that navegates to the route inidicated in the string 
  2. UI.navigate(MyComponent.class) if no parameters
  3. UI.navigate(MyComponent.class, "myParameter") if there is aparameter
Here is an example, the Greetingcomponent class is the same as the one listed peviously

 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
@Route(value = "rlexample")
public class RouterLinkExample extends VerticalLayout{
 public RouterLinkExample() {
     Div menu = new Div();
     menu.add(new RouterLink("Option-1" , MainView2.class));
     menu.add(new RouterLink("Option-2 ", GreetingComponent.class, "Ximo"));
     menu.add(new RouterLink("Option-3 ", GreetingComponent.class, "Paco"));
     add(menu);
     
     //Server routing
     NativeButton button = new NativeButton("Navigate to Greeting component with no params version 1");
     button.addClickListener( e-> {
          button.getUI().ifPresent(ui -> ui.navigate("greet"));
     });
     add(button);
     
     NativeButton button1 = new NativeButton("Navigate to Greeting component with no params version 2");
     button1.addClickListener( e-> {
          button1.getUI().ifPresent(ui -> ui.navigate(GreetingComponent.class));
     });
     add(button1);
     
     NativeButton button2 = new NativeButton("Navigate to Greeting component with params");
     button2.addClickListener( e-> {
          button2.getUI().ifPresent(ui -> ui.navigate(GreetingComponent.class, "Paco"));
     });
     add(button2);
 }
}

6.4 Getting the routes registered in the application

The Router has the method getRoutes() that retrieves all the registered routes.
The RouteData object contains the information form a route.
To obtain the routes by its parent, use getRoutesByParent method


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 01. Get the Router instance
Router router = UI.getCurrent().getRouter();

// 02. Get the list of routes
List<RouteData> routes = router.getRoutes();

// 03. Get all routes by parent
Map<Class<? extends RouterLayout>, List<RouteData>> routesByParent = router.getRoutesByParent();

// 04. Filter the routes of one parent 
List<RouteData> myRoutes = routesByParent.get(MyParentLayout.class);

7. Page Title on Navigation

To update the page title during navigation can be done with 2 ways:

  1. Using @PageTitle("Title name") annotation at the top of the class or
  2. Implementing HasDynamicTitle interface and using the overidden method getPageTitle()

8. Router Exception Handling

There are implemented vaadin by default classes to whom are redirected when this exception occurs:

  1. Class: RouteNotFoundError for NotFoundException with a 404 code
  2. Class: InternalServerError for java.lang.Exception with 500 code

By extending these classes one can customize the results here is a simple example for the first class:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@ParentLayout(MainLayout.class)
public class CustomNotFoundTarget extends RouteNotFoundError {

    @Override
    public int setErrorParameter(BeforeEnterEvent event,
            ErrorParameter<NotFoundException> parameter) {
        getElement().setText("My custom not found class!");
        return HttpServletResponse.SC_NOT_FOUND;
    }
}

In a dashboard with multiple elements, we can protect an element from unauthorised access raising an AccessDeniedException, but for the moment it is a bit ou of my pretensions. See the tutorial



No hay comentarios:

Publicar un comentario