A long long time ago, web pages were actual HTML pages. When navigating to a website, your browser was receiving and displaying an HTML document. By clicking on a button leading you to another page, you were requesting a new HTML document.

These times are more or less over. Most websites now use JavaScript to dynamically load content and adapt what you are seeing. Technically, when you navigate inside a website, you are staying at the exact same place, only the content being displayed to you changes. This reduces the loading time by sparing on the amount of data being exchanged. Such websites are called Single Page Applications (SPA).

The problem with this approach is that, as the page doesn’t technically change when the user clicks a button, the URL doesn’t either. You can’t save links to given content inside a page, and you can’t exchange it with people. We therefore need to adapt what appears in the address bar when the user navigates and to display the right content when the user clicks on a link. This is the role of routers. Angular being a complete framework, it comes with its own Angular Router.

Here is my point: as historically URLs are paths to pages, we forget the fact that the Angular Router is more flexible than that. It is linking views and paths. We’ll see in an example how more powerful that can be.

Basic Page Routing

Let’s start with the standard use of the Angular Router: we will create an application with some pages and buttons to navigate between them. The application allows users to keep an inventory of what they have in their fridge and it looks like this:

ng new fridge-inventory --routing --style=scss

We need a service to deliver our data, a component for each tab, and a general list component. We generate all this:

ng g service services/inventory

ng g component components/dairies

ng g component components/fruitsAndVegetables

ng g component components/meatAndEggs

ng g component components/list

The inventory service provides lists of strings (dairies, fruitsAndVegetables, …). The service is injected in the three components dairies.component.ts, fruitsAndVegetables.component.ts and meatAndEggs.component.ts. Each of them takes the data it is interested in and passes it as input to the list component, which then displays it correctly. You can have a look at this commit.

What we are interested in right now is the routing. Depending on which button is clicked, we want to display a different view. In the app-routing.module.ts, we define routes, i.e. we tell the router which component to display when a given path is called.

Each button has a routerLink attribute that tells the router which route to open on click. It also has a routerActiveLink attribute, that allow us to assign a css class to the active button. To tell Angular what to do with the component the router gives us, we use the router-outlet directive. It is a placeholder that marks the spot where this component should be displayed. In our case, right under the buttons.

We can now see the list being updated when we click on a button.

Use a named Outlet

We now want to allow the user to add items to a grocery list. We need a service in which to store the list, and a component to display it.

ng g service services/grocery-list

ng g component components/grocery-list

We want to display a button “Open grocery list” in the footer. On click on this button, the list should open on the right of the screen and a button “Close grocery list” should be displayed in the footer. The user can switch the tabs, it doesn’t impact the list.

You can also notice that the link changes when opening and closing the grocery list.

The list and its buttons are displayed from the page footer. It is not optimal but it will do for the example. The list has an absolute position. To style the footer and any button in it we use content projection. We create a component footer. We pass our buttons as content to this component, but the footer component takes care of the styling of the footer and of the buttons using ::ng-deep. To learn more about this pattern, you can check this resource.

In app.component.html we add a secondary router-outlet. We can only have one primary unnamed router-outlet, this second one needs a name. We call it groceryList.

We can use this secondary outlet to display everything related to our grocery list. This way the routing done around the grocery list is independent from the “main” routing.

We create the button to open the grocery list, and the grocery list itself and add the routes corresponding to this outlet.

ng g component components/open-grocery-list-button

ng g component components/grocery-list

You can see that these routes have an extra property outlet. We declare to Angular that these are related to our secondary outlet groceryList.

By default, when the path is empty, we display the button to open the list:

This button also has a routerLink but it looks a bit different. This is the complete syntax, what we saw earlier was some syntactic sugar for the most common case of the primary outlet. This time, we tell the router which outlet is concerned by the route, and we give it the path. From this, the router finds the route, and displays the right component in our secondary outlet. You can see that we pass an object as outlets. That means that we could theoretically change the path of many outlets at once. We only change one though, on click on the button the grocery list opens and the address is changed.

Our list component has a textarea and a button. This button has a routerLink that looks like the one we had just before. By clicking on the button, we clear the secondary outlet and its path, displaying the OpenGroceryListButton component again.

The list is independent of the rest of the application. You can open the list and change the food type, the list will stay open. We can also copy the link and open it in another tab, you will find your application in the exact same state as in the first tab (without the content of your grocery list of course). The router knows from the paths only which component to display in each of your outlets. That would work for as many outlets as you like. This gives you a great flexibility in building the navigation experience in your website.