A post covering the same concepts but with updated versions of ASP.NET and Aurelia can be found here.
I have spent some time building out the client side of my contacts application using Aurelia. For a starting point with Aurelia check out my previous post on Aurelia with ASP.NET 5 and Web API and Start Aurelia from an ASP.NET 5 Controller. In this post I am going to cover passing a parameter via routing.
Keep in mind I am serving this application from ASP.NET 5 and all the Aurelia related files are in the wwwroot folder of my ASP.NET 5 project. App.html and app.js in wwwroot with the remaining Aurelia files in an Aurelia folder.
In app.js a new route was added for the detail view. Make special note that the new route expects an id parameter.
import 'lib/bootstrap/dist/js/bootstrap'; export class App { configureRouter(config, router) { config.title = 'Contacts'; config.map([ { route: ['', 'list'], name: 'list', moduleId: './Aurelia/list', nav: true, title: 'List' }, { route: 'add', name: 'add', moduleId: './Aurelia/add', nav: true, title: 'Add' }, { route: 'detail/:id', name: 'detail', moduleId: './Aurelia/detail', nav: false, title: 'Detail' } ]); this.router = router; } }
In list.html the contacts need to be rendered with an anchor that will lead to the proper detail view. To accomplish this Aurelia has a route-href custom attribute that can be used with an anchor.
<template> <div> <ul class="list-group"> <li repeat.for="contact of contacts" class="list-group-item"> <h4 class="list-group-item-heading"> <a route-href="route: detail; params.bind: {id:contact.Id}"> ${contact.Name} </a> </h4> <p repeat.for="emailaddress of contact.EmailAddresses" class="list-group-item-text"> <a href="mailto:${emailaddress.Address}"> ${emailaddress.Address} </a> </p> </li> </ul> </div> </template>
With route-href route, set to detail above, defines the name of the route from the route config. Parms.bind creates an object with an id property which is bound to the id defined on the route. Any property on the object that does not have a in the route definition will be added to the query string.
When one of the routing links is clicked then on activation detail.js queries the Web API for the contact details based on the id in the parms.
import {inject} from 'aurelia-framework'; import {HttpClient, json} from 'aurelia-fetch-client'; import 'fetch'; @inject(HttpClient) export class Detail{ contact = ''; constructor(http){ http.configure(config => { config .useStandardConfiguration() .withBaseUrl('https://localhost:13102/api/'); }); this.http = http; } activate(params) { return this.http.fetch('contacts/' + params.id) .then(response => response.json()) .then(contact => this.contact = contact); } }
No real new concepts in the above. On active the Web API is called and the resulting json is used to populate a contact property.
The detail view then uses the contact property to bind to the contact information.
<template> <div> <div class="row"> <label class="label label-info">${contact.Id}</label> </div> <div class="row"> <label class="col-sm-1 control-label">Name</label> <div class="col-sm-11"> <p class="control-label">${contact.Name}</p> </div> </div> <div class="row"> <label class="col-sm-1 control-label">Addresses</label> <div class="col-sm-11"> <p repeat.for="address of contact.Addresses" class="list-group-item-text"> ${address.City}, ${address.State} </p> </div> </div> <div class="row"> <label class="col-sm-1 control-label">Email Addresses</label> <div class="col-sm-11"> <p repeat.for="emailaddress of contact.EmailAddresses" class="list-group-item-text"> <a href="mailto:${emailaddress.Address}">${emailaddress.Address}</a> </p> </div> </div> <div class="row"> <label class="col-sm-1 control-label">Phone Numbers</label> <div class="col-sm-11"> <p repeat.for="phone of contact.PhoneNumbers" class="list-group-item-text"> ${phone.Number} </p> </div> </div> </div> </template>
The above results in the following view.
When I use a list similar to the canonical child-router.html from the skeleton, and I use the normal “nav: true” routes, the node highlights as having been “selected”. When I add some elements from a list like your example and select them, it works fine as does your example. However, the item does not “select” (add the “active” class” the same way because there is no hooks in place to set the “isActive” flag. Have you come up with a solution for that also?
Paul I don’t have an answer for you at the moment. I will try and take some time this weekend and see if I can find an answer for you.
Just to be clear are you talking about the navigation (like List and Add above) or are you talking about highlighting the selected item in your list of items?
I have a gist.run to see the problem I am currently working through:
https://gist.github.com/paulharker/bbb7c66ff12fc9c57af9abf3f7863f17
I’m using the routes.configuration collection plus my own model collection to create my navigation menu. I’m using one routes (with nav: false) to display from my model collection, but something is not quite right. Any assistance is appreciated.
Paul
From what I am seeing the constructor for settings is only run once which is creating the routes set for Paul. If you look at the links for Profile in the setting nav for Charlie it will look like this:
href="#/users/settings/Paul/dashboard2"
and as you can see that is pointing to Paul instead of Charlie.In your settings.js file add this import:
import {activationStrategy} from "aurelia-router";
And then add this function:
determineActivationStrategy() {
return activationStrategy.replace;
}
With those two changes the issues I was seeing in your example seem to be fix. Please follow up and let me know if fixes the issues you are seeing.
Thank you Eric, you are very generous with your time.
This sounds promising,and i will try it shortly.
The reference to activationStrategy brings up concepts for a future area of development: The application will have a “dock” that will hold “currently active transactions” that can be reactivated quickly. I need to to decide between two possible approaches: 1) that each “active” transaction, when de-activated, stores it’s state, then reloads its state when re-activated — OR 2) that each “active transaction” stays in memory and is simply re-shown from memory. (I’m answering my question as I’m writing it!) The best way to describe the behavior is that of a multiple-document interface – where multiple transactions can be started and minimized the way Windows minimizes them to the task bar. They are still in memory, no longer taking up space in the main part of the screen, and reactivate at the speed of RAM rather than from being retrieved from he server.
I believe activationStrategy will be “app-wide”, is that correct? When the app re-opens a route, it will ALWAYS re-run constructor, configureRoute, and activate routines, is that correct?
In any case, thank you kindly! Where are you located? I owed you a beer or a coffee!
Paul
It is great how just the act of talk/writing something out will trigger things in our own heads.
I am not sure about the scope of activationStrategy, but I would think it would only change the behavior of the component it was added to. That is a good question on the constructor. I would have to rerun samples and watch the logs.
I am in the Nashville, TN area. If you are in the area we should meet up!
Nashville is definitely one of my future destinations!
Hi Eric, the activationStrategy change has fixed this specific issue! Thank you. I will delve more deeply into understanding why and let you know any more insights I find while doing so. I wish this were my primary project as I think my progress would be faster. Paul
Glad to hear it Paul! Keep the updates coming.