Angular Dart Router - Navigation
Tags: dart, webdev, angular
Published: 2021-01-16 20:04:30

Navigation is probably the most important feature of a Router, we will have a look are our options to navigate from a Component route to another and pass data to your component through the URL.

First, let's consider the followings `RoutePath` definitions.
```dart
import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';

class AppRoutes {
  static final list = RoutePath(path: 'list');
}

final routes = <RouteDefinition>[
    RouteDefinition(
      routePath: AppRoutes.list, 
      component: ng.ListComponentNgFactory,
    ),
];
```

# Basic navigation

You can simply use the `navigate` method from the Router and defined RoutePath to build the URL you want to navigate to.

```dart
@Component(
  selector: 'nav-bar',
  template: '''
  <div (click)="navigateToList()">List</div>
  ''',
)
class NavBarComponent {
  final Router router;
  NavBarComponent(this.router);

  void navigateToList() {
    router.navigate(AppRoutes.list.toUrl());
  }
}
```

**It works but,** you are not really following HTML semantics. To navigate between pages you should be using an **Anchor** element and set `href` attribute on it. 

However, if you try to set it manually as follow.
```html
<a [attr.href]="AppRoutes.list.toUrl()">List</a>
```
It won't always work since the element is not aware of your `LocationStrategy` and your `<base href="...">` configuration.

To set the URL on an anchor, angular provides the `RouterLink` directive that you can use as a replacement of the `href` attribute.
```html
<a [routerLink]="AppRoutes.list.toUrl()">List</a>
```

As a bonus, you will be able to use the `RouterLinkActive` directive to apply CSS class to your element if the URL is currently active.
```html
<a [routerLink]="AppRoutes.list.toUrl()"
   routerLinkActive="my-class">
  List
</a>
```

# Navigate with parameters

2 types of parameters are available.

The first one, simply calls `parameters` could be considered as required parameters, that must be predefined in the `RoutePath` definition.
```dart
class AppRoutes {
  ...
  /// required parameter with name 'idemId'
  static final listItem = RoutePath(path: 'list/:itemId');
}

final routes = <RouteDefinition>[
    ...
    RouteDefinition(
      routePath: AppRoutes.listItem, 
      component: ng.ListItemComponentNgFactory,
    ),
];
```

The second one, `queryParameters` are mostly optional parameters, in the URL, this is everything that comes after `?` and separated by `&`. (ex: `example.com?foo=bar&val=42`)

Now, imagine you want to navigate to a specific list item and you also want to pass an optional value to the `ListItemComponent` to make a feature visible or not for example. You just need to use available parameters of the `RoutePath.toUrl` method.

```dart
/// can be use with RouterLink directive
String buildListItemUrl(String id) {
  return AppRoutes.listItem.toUrl(
    parameters: { 'itemId': id },
    queryParameters: { 'showAwesomeFeature': 'true' },
  );
}

// or
void navigateToListItem(String id) {
  router.navigate(buildListItemUrl(id));
}
```

> Both parameters must be strict `Map<String, String>` objects. if you want to pass complex data, you must have a way to encode/decode it.

# Read URL parameters

Now that you know how to build URLs and trigger navigation to the route component. You probably want to access the info you stored in the URL.

To do so, you need to implement the `OnActivate` provided by Angular on your component.

```dart
import 'package:angular/angular.dart';
import 'package:angular_router/angular_router.dart';

@Component(
  ...
)
class ListItemComponent implements OnActivate {
  @override
  void onActivate(RouterState previous, RouterState current) { ... }
}
```

Each time the router navigates to this component, the `onActivate` method is called and gives you access to the previous and the current `RouterState` containing both parameters that you can decode.

```dart
void onActivate(RouterState previous, RouterState current) {
  final listItemId = current.parameters['itemId'];
  fetchItem(listItemId);

  showAwesomeFeature = current.queryParameters['showAwesomeFeature'] == 'true';
}
```