LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • 0001 to 0023
      • 0088 to 0723
      • 0901 to 0908
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page
  • Introduction
  • Installation
  • Add routes and a viewport
  • Enable routing
  • Create some routes
  • Create some components
  • Listing the recipes
  • Add in support for 404 fallback
  • Reading route parameters
  • Styling active links
  • That's it

Was this helpful?

Export as PDF
  1. Getting to know Aurelia
  2. Routing
  3. @aurelia/router

Router Tutorial

PreviousRoute EventsNextRouter Recipes

Last updated 1 year ago

Was this helpful?

Introduction

Aurelia comes with a powerful fully-featured router without the need to install any additional dependencies. If you are new to Aurelia, we recommend visiting the Getting Started section first to familiarise you with the framework.

You are here because you want to familiarize yourself with the router, so we'll make this quick. At the end of this tutorial, you will be familiar with all the concepts and APIs you need to add routing into your Aurelia applications.

While building our recipe application, we will cover the following:

  • How to create and configure routes

  • Navigating with load in your views as well as view-models

  • Styling active links

  • Programmatically navigating using router APIs

  • Working with route parameters

A working demo and code for the following tutorial can also be found .

Installation

To do this tutorial, you'll need a working Aurelia application. We highly recommend following the guide to scaffold an application. However, for this tutorial, we have a ready to go that we recommend. It will allow you to follow along and live code.

As you will see, it's a boring application with no routing or anything exciting. All it says is Recipes (hardly a recipe application).

Add routes and a viewport

The viewport is where our loaded routes are dynamically loaded into. When we click on a route, the <au-viewport> element will be populated with the requested component.

Open my-app.html and add in the <au-viewport> and some navigation links.

my-app.html
<div>
  <h1>Recipes</h1>

  <nav>
    <a load="/">Home</a>&nbsp;&nbsp;
    <a load="/recipes">Recipes</a>
  </nav>

  <au-viewport></au-viewport>
</div>

This won't do anything just yet because we haven't enabled routing or created any routes.

Enable routing

Let's go into main.ts and configure Aurelia to use routing. Import the RouterConfiguration object and pass it to Aurelia's register method.

We also configure the router to use push-state routing instead of the hash-based router. This gives us cleaner URLs.

main.ts
import Aurelia from 'aurelia';
import { RouterConfiguration } from '@aurelia/router';
import { MyApp } from './my-app';

Aurelia
  .register(RouterConfiguration.customize({ useUrlFragmentHash: false }))
  .app(MyApp)
  .start();

Create some routes

Now that we have a viewport and the router enabled let's create some routes. We will start by adding our routes to our root my-app.ts component.

export class MyApp {
  static routes = [
    {
      path: '',
      component: '',
      title: 'Home',
    },
    {
      path: 'recipes',
      component: '',
      title: 'Recipes',
    },
    {
      path: 'recipes/:recipeId',
      component: '',
      title: 'Recipe',
    },
  ];
}

The important thing to note here is that we are not specifying a component to load for these routes. We will do that shortly, but the routing structure is what we are creating here.

The first route has an empty path , which means it's a default route (the router will load this first if no other route is requested). The second route recipes tells the router when the user visits /recipes to load our recipes component. Lastly, the recipes/:recipeId route has a route parameter that allows us to load specific recipes.

Create some components

We now need to create three components for our routes: the homepage, recipes page, and recipe detail page.

Unlike other frameworks and libraries, Aurelia works on the premise of a view-and-view model. If you familiarize yourself with the Getting Started section, you would already be familiar with these concepts.

Let's create the homepage first:

home-page.ts
export class HomePage {
}
home-page.html
<p>Welcome to flavortown. Aurelia Recipes is the only recipe application you will need to manage your recipes.</p>

Let's create the recipes page:

recipes-page.ts
export class RecipesPage {
}
recipes-page.html
<p>Your recipes. In one place.</p>

Let's create the recipe detail page:

recipe-detail.ts
export class RecipeDetail {
}
recipe-detail.html
<p>This is a recipe.</p>

Lastly, let's import our components in my-app.ts for the routes to load them.

my-app.ts
import { HomePage } from './home-page';
import { RecipesPage } from './recipes-page';
import { RecipeDetail } from './recipe-detail';

export class MyApp {
  static routes = [
    {
      path: '',
      component: HomePage,
      title: 'Home',
    },
    {
      path: '/recipes',
      component: RecipesPage,
      title: 'Recipes',
    },
    {
      path: '/recipes/:recipeId',
      component: RecipeDetail,
      title: 'Recipe',
    },
  ];
}

Listing the recipes

In a non-tutorial application, you would have your API and server providing this data. But, for our tutorial will use a public recipe API instead. We could use mock data, but it would be a lot of data for a recipe application.

In recipes-page.ts add the following to the component view model:

recipes-page.ts
import { HttpClient } from '@aurelia/fetch-client';

export class RecipesPage {
    private http: HttpClient = new HttpClient();
    private recipes = [];

    async bound() {
        const response = await this.http.fetch(`https://www.themealdb.com/api/json/v1/1/search.php?f=b`);

        const result = await response.json();

        this.recipes = result.meals;
    }
}

We've loaded the recipes. Now it's time to display them. Open recipes-page.html and add in the following:

<ul>
    <li repeat.for="recipe of recipes"><a load="/recipes/${recipe.idMeal}">${recipe.strMeal}</a></li>
</ul>

We use Aurelia's repeat.for functionality to loop over the recipes. But take notice of the link with load attribute. Aurelia Router sees this and knows this is a route. We are providing the recipe detail route with the ID of the recipe.

It's not pretty, but we now have a list of recipes.

Add in support for 404 fallback

Sometimes a user might attempt to visit a recipe or page that doesn't exist. You might want to redirect the user or display a 404 page in those situations.

Let's create another component and call it fourohfour-page

fourohfour-page.ts
export class 404Page {
}
fourohfour-page.html
<h1>Oops!</h1>
<p>Sorry, that link doesn't exist.</p>

We then specify on our <au-viewport> what our fallback is. We need to import this 404 component to use it.

my-app.html
<import from="./fourohfour-page"></import>

<div>
  <h1>Recipes</h1>

  <nav>
    <a load="/">Home</a>&nbsp;&nbsp;&nbsp;&nbsp;
    <a load="/recipes">Recipes</a>
  </nav>
  <au-viewport fallback="fourohfour-page"></au-viewport>
</div

Reading route parameters

When we created our routes in my-app.ts you might recall we created a recipe detail route which had a recipeId parameter. Now, we are going to modify our recipe-detail component to read this value from the URL and load the content.

recipe-detail.ts
import { resolve } from 'aurelia';
import { HttpClient } from '@aurelia/fetch-client';
import { IRouter } from '@aurelia/router';

export class RecipeDetail {
  private http: HttpClient = new HttpClient();
  private recipe;

  readonly router: IRouter = resolve(IRouter);

  async canLoad(parameters) {
    if (parameters?.recipeId) {
      const loadRecipe = await this.loadRecipe(parameters.recipeId);

      if (loadRecipe) {
        this.recipe = loadRecipe;

        return true;
      } else {
        this.router.load(`/recipes`);
      }
    }
  }

  async loadRecipe(recipeId) {
    const request = await this.http.fetch(
      `https://www.themealdb.com/api/json/v1/1/lookup.php?i=${recipeId}`
    );
    const response = await request.json();

    return response.meals ? response.meals[0] : false;
  }
}

There is a little more to unpack here. We inject the router because we will programmatically redirect away if the user attempts to view a non-existent recipe ID. We use the canLoad method because loading our recipe view relies on existing recipes. If the recipe can't be found using the API, we redirect to the recipes page programmatically using the router.load method.

Inside recipe-detail.html we'll render our recipe:

recipe-detail.html
<h1>${recipe.strMeal}</h1>
<img src.bind="recipe.strMealThumb" />
<p textcontent.bind="recipe.strInstructions"></p>

The API we use returns ingredients on a line-by-line basis, so we've omitted those from this example. Now, you should be able to run your application and click on recipes to view them. A headline, image and instructions should now be visible.

As an additional step, you could add those in yourself.

Styling active links

The router automatically adds a active class to all route links when they are active. Because our routes all live in my-app We will edit my-app.css We don't even have to import it (Aurelia does that automatically for us).

my-app.css
.active {
  font-weight: bold;
}

a {
  color: #000;
  text-decoration: none;
}

The active class is now bold when active. We also do some quick styling tweaks to the route links to remove the underline and make them black so we can see the bold stand out more.

That's it

The MealDB is a fantastic free meal API that can give us recipes. We will use the to get this data, which wraps up the native Fetch API.

You just built a recipe application. It doesn't allow you to create recipes, and it's not very pretty, but it works. To see the application in action, a working demo of the above code (or below).

here
Quick Start
starter Aurelia 2 application
Aurelia Fetch Client
can be seen here