How to write AngularJS Components with nested routing using TypeScript?
One of technique to compose Angular Components is to write child or grand-child component(s) nested under a top-level component using the new Component Router. This is useful when you have hierarchical data to present to the user. In this post I’ll tell you how to write AngularJS Components with nested routing using TypeScript.
I’ve written some posts that covers the basic and important concepts of Components: how to use AngularJS Components with TypeScript and how to use AngularJS Component Router with TypeScript.
Scope
The scope of this post is to extend the Review-Details component introduced in the posts linked above to use some new child components with the help of nested routing. If you follow previous and current post, then you will have following by the end of this post:
AngularJS Component nested routes
Nested Component(s)
If you are using component router in your application and you will have a top-level component that contains routing rules. If you need to present hierarchical information to the user, one of the way is to link independent components with the help of nested routing. This concept was missing from ng-Route and that was one of the reasons developers used ui-router module.
Let us discuss the important bits that we need to implement.
Non-terminal Routes
To extend a route to have nested routing, extend this parent route to define it as a Non-terminal route. We need to tell the Router that Review-Details route definition is non-terminal, that it should continue to match Routes in its child Components. We do this by adding a continuation ellipsis ... to the path of parent route as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
namespace app.components { class ReviewAppComponent implements ng.IComponentOptions { templateUrl = ‘/app/components/review-app.component.html’; $routeConfig = [ { path: ‘/list’, component: ‘reviewList’, name: ‘List’ }, { path: ‘/about’, component: ‘appAbout’, name: ‘About’ }, { path: ‘/details/:id/…’, component: ‘reviewDetails’, name: ‘Details’ }, { path: ‘/**’, redirectTo: [‘List’]} // array to pass params to a route, wild car character to matching any other requested route ]; } angular.module(‘app’).component(‘reviewApp’, new ReviewAppComponent()); } |
Line no. 8 above uses ... within path. Without the continuation ellipsis the Details route will never find any nested routes as the router will stop at this level and will not bother to match the rest of the URL.
Route rules in child Component
Just like routing rules in top-level component, now due to addition ... router will expect to find routing rules inside reviewDetails component. This was created in my previous post linked above and below is the updated source:
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 |
namespace app.components { interface IReviewDetailsController { id: number; $routerOnActivate(next, previous); } class ReviewDetailsController implements IReviewDetailsController { id: number; constructor(private $http: angular.IHttpService) { } $routerOnActivate(next, previous) { this.id = next.params.id; // talk to cache or server to get item by id // & show that on UI } } class ReviewDetailsComponent implements ng.IComponentOptions { templateUrl = ‘app/components/review-details.component.html’; $routeConfig = [ { path: ‘/overview’, component: ‘restauratOverview’, name: ‘Overview’ }, { path: ‘/menu’, component: ‘restauratMenu’, name: ‘Menu’ }, { path: ‘/contact’, component: ‘restauratContact’, name: ‘Contact’ } ]; // function member for this hook doesn’t work!! // so either use lambda expression or function directly. // $canActivate = $timeout => $timeout(() => true, 100); controllerAs = ‘model’; controller = [‘$http’, ReviewDetailsController]; } angular.module(‘app’).component(‘reviewDetails’, new ReviewDetailsComponent()); class RestaurantOverviewComponent implements ng.IComponentOptions { template = “This is restaurant’s overview.”; } angular.module(‘app’).component(‘restauratOverview’, new RestaurantOverviewComponent()); class RestaurantMenuComponent implements ng.IComponentOptions { template = “This is restaurant’s menu.”; } angular.module(‘app’).component(‘restauratMenu’, new RestaurantMenuComponent()); class RestaurantContactComponent implements ng.IComponentOptions { template = “This is restaurant’s contact info.”; } angular.module(‘app’).component(‘restauratContact’, new RestaurantContactComponent()); } |
Line no. 24 defines some routing rules that is similar to way of writing rules in top-level component as well. The only difference is that router will now prepend /details/:id and /overview to process any requests for /details/:id/overview. Same is for path with menu and contact values.
I’ve also added 3 new simple components that just have hard-coded template. This can also have real information about a restaurant’s overview, menu and contact that can be either cached or request from the server on-demand.
ngLink Directive
We need to use ngLink directive to present links in the view so that user can navigate from overview to menu or contact view.
The updated source for review-details.component.html is shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<h4>Details for {{model.id}}</h4> <ul> <li> <a data–ng–link=“[‘Overview’]”>Overview</a> </li> <li> <a data–ng–link=“[‘Menu’]”>Menu</a> </li> <li> <a data–ng–link=“[‘Contact’]”>Contact Us</a> </li> </ul> <ng–outlet></ng–outlet> |
Notice that this view also has a ngOutlet directive that is responsible for working as a shell to show view for overview, menu and contact components.
Due to addition of nested routes above, we also need to change anchor tag’s source in reviewList component as shown below: