Aurelia – Router links, click delegate, routing parameters

This post is going to cover multiple of topics that I hit while creating a contact detail page to go along with the contact list that is part of my ASP.NET Basics repo. The code before any changes can be found here. If you are following along with the sample application keep in mind all the changes in this post take place in the Aurelia project.

Creating a detail view and view model

Create contactDetail.html file inside of ClientApp\app\components\contacts. This is the view that will be used to display all the details of a specific contact as well as a link back to the contact list. The following image shows the folder structure with the view and view model already added.

View

The following is the full contents of the view. This will be followed up few a calls out of Aurelia specific things going on.

<template>
    <h1>Contact Details</h1>
    <hr />
    <div if.bind="contact">
        <dl class="dl-horizontal">
            <dt>ID</dt>
            <dd>${contact.id}</dd>
            <dt>Name</dt>
            <dd>${contact.name}</dd>
            <dt>Address</dt>
            <dd>${contact.getAddress()}</dd>
            <dt>Phone</dt>
            <dd>${contact.phone}</dd>
            <dt>Email</dt>
            <dd>${contact.email}</dd>
        </dl>
    </div>
    <a route-href="route: contactlist">Back to List</a>
    <hr />
</template>

if.bind will keep the contact details out of the DOM if the condition fails. In this case, if the contact is null.

${value} is one of Aurelia’s bind syntaxes. For more details check out their docs.

<a route-href=”route: contactlist”> is using Aurelia’s router to generate a link back to the contact list component.

View model

Next, create a contactDetail.ts file inside of ClientApp\app\components\contacts  which is the view model that goes with the view created above.

The view model gets an instance of the ContactService injected which is used to pull a contact’s detail information during the activate lifecycle hook. Notice the first parameter of the function (parms) which is where the route parameters are passed in.

import { inject } from 'aurelia-framework';
import { Contact } from './contact';
import { ContactService } from './contactService';

@inject(ContactService)
export class ContactDetail {
     contact: Contact;

    constructor(private contactService: ContactService) { }

    activate(parms, routeConfig) {
        return this.contactService.getById(parms.id)
            .then(contact => this.contact = contact);
    }
}

Adding get by ID to the Contact Service

The existing ContactService doesn’t provide a function to get a contact by a specific ID so one needs to be added.

The following calls the API in the Contacts project and uses the result to create an instance of a Contact as a promise which is returned to the caller.

getById(id: string): Promise<Contact> {
    return this.http.fetch(id)
        .then(response => response.json())
        .then(contact => new Contact(contact))
        .catch(error => console.log(error));
}

Add a route with a parameter

Next, the router needs to be made aware of the new contact details. Open app.ts inside of the ClientApp/app/components/app folder. This file contains all the routes for the application. The bit we are interested in is the config.map which is an array of routes the application handles. Contact details is a new route which means adding a new object to the config.map array.

{
 route: 'contact-detail/:id',
 name: 'contactdetail',
 moduleId: '../contacts/contactDetail',
 nav: false,
 title: 'Contact Detail'
}

The route is the pattern used to match URLs. In addition, parameters can be used in the form of :parameterName. The above route will handle requests for http://baseurl/contact-detail/{id} where {id} is an ID of a contact. If a route has a parameter it will be made available to the activate function via the parms parameter in the view model.

moduleId is used to locate the view/view model that goes with the route.

nav controls if the route will be included in the routers navigation model. This is used to build the menu in this application. Contact details shouldn’t show in the menu which is why nav is set to false. For more details on Aurelia’s router check out the docs.

Integrating the detail view with the contact list

The contact list view and view model needed changes to support the contact detail page.

View model

The change to the view model was the simplest. A variable for the ID of the select contact was added as well as a function that gets called when a contact is selected. The following is the fully contact list view model.

import { inject } from 'aurelia-framework';
import { Contact } from './contact';
import { ContactService } from './contactService';

@inject(ContactService)
export class ContactList {
    contacts: Contact[];
    selectedContactId: number = null;

    constructor(private contactService: ContactService) {}

    created() {
        this.contactService.getAll()
            .then(contacts => this.contacts = contacts);
    }

    select(contact) {
        this.selectedContactId = contact.id;
    }
}

The changes to the view model were not really required to add the contact detail page, but they are there to show how to setup a click delegate on the view side. In the future, the selected contact could come in handy if the list and details were shown at the same time.

View

On the view, the amount of data being displayed was reduced to show just contact ID and name. A column was added with a link to the details page that is now used to show the rest of information about a contact. The following is the full view.

<template>
    <h1>Contact List</h1>
    <p if.bind="!contacts"><em>Loading...</em></p>

    <table class="table" if.bind="contacts">
        <thead>
        <tr>
            <th>IDs</th>
            <th>Name</th>
            <th></th>
        </tr>
        </thead>
        <tbody>
        <tr repeat.for="contact of contacts" 
          class="${contact.id === selectedContactId ? 'active' : ''" }>
            <td>${contact.id}</td>
            <td>${contact.name}</td>
            <td><a route-href="route: contactdetail; params.bind: {id:contact.id}" click.delegate="select($contact)">
                  Details
                </a>
            </td>
        </tr>
        </tbody>
    </table>
    <hr />
</template>

click.delegate=”select($contact)” will cause the select function of the view model to be called when the associated element clicked and passes the relevant contact object. The docs go into more depth on when to use delegates vs triggers.

The details link contains a lot of concepts. route-href is binding the anchor to Aurelia’s router.

route: contactdetail is telling the router which route to load.

params.bind: {id:contact.id} is telling the link to pass the contact’s ID to through the router to the view model that is being loaded. The following is the activate function of the contact detail view model as a reminder of the parameter’s usage.

activate(parms, routeConfig) {
    return this.contactService.getById(parms.id)
        .then(contact => this.contact = contact);
}

Wrapping up

This post cover a lot of topics, but they were all things I had to review in the course of adding contact details. My hope is this post will shortcut your own research and get you back to your task at hand. The finished code can be found here.

Leave a comment with any thoughts and/or questions.

8 thoughts on “Aurelia – Router links, click delegate, routing parameters”

  1. Big THANK YOU! Your example saved me a lot of time.

    One small bug I found in the ContactList view on line:


    Details

    I had to change the click.delegate function call to “select(contact)” to make your example work.

  2. One small bug I found in the ContactList view on line:

    <a route-href=”route: contactdetail; params.bind: {id:contact.id}” click.delegate=”select($contact)”>
    Details
    <a>

    I had to change the click.delegate function call to “select(contact)” to make your example work.

  3. How would I normally find out about route-href usage such as ‘params.bind’ and ‘route:’? There’s nothing about it in their docs and they brag how incredible their docs are. It’s crazy that something as basic as that has to be gathered from multiple blogs and stack overflow.

    route-href=”route: users; params.bind:{id:v.id}”

  4. I’m glad you found the answers you were looking for. Looking back at the official documentation how it works isn’t the clearest. This is an older post so they maybe an easier way now.

  5. Chandra Prakash Yadav

    Hello ,
    I am trying to use click.delegate or click.trigger in anchor tag but it’s not working in my case.

    While clicking on anchor tab function is not getting call.

    Please suggest. I need to refresh other part of my application when user clicked on anchor link.

  6. Pingback: Aurelia, call click function before route-href – Fix Code Error

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.