Routing
Creating a route interface
The main purpose of the routing library in Flavour is to provide a type-safe way to generate and parse URLs. Thus, the first thing you have to do to start using routing is creating a route interface.
Let’s create a simple one:
@PathSet
public interface HelloRoute extends Route {
@Path("/")
void index();
@Path("/hello/{name}")
void hello(@PathParameter("name") String name);
@Path("/goodbye")
void goodbye();
}
Here, you can see all basic things:
- Route interface must extend
Route
. - Route interface must be marked with
@PathSet
. - Route interface can only define
void
methods. - Each method must be marked with a
@Path
annotation specifying path template. - To bind a parameter to a template, you should use an
@PathParameter
annotation.
Creating an application skeleton
Our route interface is useless until we start using it, but we don’t have any views to show. Let’s create them:
@BindTemplate("templates/master.html")
public class Client extends ApplicationTemplate implements HelloRoute {
public static void main(String[] args) {
Client client = new Client();
new RouteBinder()
.withDefault(HelloRoute.class, r -> r.index())
.add(client)
.update();
client.bind("application-content");
}
@Override
public void index() {
}
@Override
public void hello(String name) {
}
@Override
public void goodbye() {
}
}
and corresponding template templates/master.html
<div>
<std:insert fragment="content"/>
</div>
Then create views and templates for pages:
@BindTemplate("templates/index.html")
public class IndexView {
private String name;
public void setName(String name) {
this.name = name;
}
}
<div>What's your name?</div>
<div>
<input type="text" html:change="name"/>
<button type="button">Hello</button>
</div>
@BindTemplate("templates/hello.html")
public class HelloView {
private String name;
public HelloView(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
<div>Hello, <html:text value="name"/></div>
@BindTemplate("templates/goodbye.html")
public class GoodbyeView {
}
<div>Goodbye!</div>
Binding URL hashes to pages
Now we want to display pages as the user changes URL in address bar.
It’s easy, since we already have bound URLs to actions, but left these actions empty.
Notice the index
, hello
and goodbye
methods of the Client
class don’t do anything.
Flavour calls these methods automatically on every URL update.
Let’s make these methods to do something useful:
@Override
public void index() {
setView(new IndexView());
}
@Override
public void hello(String name) {
setView(new HelloView(name));
}
@Override
public void goodbye() {
setView(new GoodbyeView());
}
The magic behind setView
is simple: our base class, ApplicationTemplate
takes objects passed to setView
,
creates DOM fragments from them and supplies these fragments via content
property,
which we just displayed in master.html
.
You can run the application and append #/
, #/hello/anonymous
and #/goodbye
to test our new functionality.
Generating links
There are two ways of generating links:
- insert them into the web page;
- generate them programmatically and manually update the URL hash.
Let’s update our application to use both ways.
First, add following code to IndexView
:
public void sayHello() {
Routing.open(HelloRoute.class).hello(name);
}
and of course, bind this method to click
event in index.html
:
<button type="button" event:click="sayHello()">Hello</button>
Now, pressing the button on the first page of our application transfers user to corresponding page and updates URL in browser’s location bar.
Second, add the following code to HelloView
:
public HelloRoute route(Consumer<String> consumer) {
return Routing.build(HelloRoute.class, consumer);
}
and the following markup to hello.html
:
<div>
<a html:link="route(it).goodbye()">Goodbye!</a>
</div>
Now, hello page has a link to goodbye page.
Advanced topics
Path parameters
You can place path parameters everywhere, they are not constrained to be separated by /
character.
I.e. the following pattern is valid:
@Path("/hello-{firstName}-{lastName}")
Note that this pattern is ambiguous. For example, consider this input string:
hello-a-b-c
Both firstName="a-b", secondName="c"
and firstName="a", secondName="b-c"
are possible.
Flavour applies a greedy algorithm, so the first option will be returned.
Using regular expressions
For String
path parameters it’s possible to provide custom regular expressions.
All you need is to put the @Pattern
annotation on a corresponding method parameter.
@Path("/hello/{name}")
void hello(@Pattern("[A-Za-z]+( +[A-Za-z]+)?") @PathParameter("name") String name);
This only affects the parser, not the generator.
Mapping to Java types
Path parameters may have only following types:
- String.
- byte, short, int, float, double, either boxed or unboxed.
- BigInteger, BigDecimal.
- java.util.Date,
matches either date (
yyyy-MM-dd
) or timestamp (yyyy-MM-ddThh:mm:ss
) format. Always produces timestamp format. - Enum fields. Represented by their names.
- Previous
- Next