Aurelia

Combine Views with Aurelia

In my contacts sample application, I wanted to combine the list and detail views for a better user experience. Turns out that Aurelia’s contact sample application does this and a few other things I want to try out in the future so I used it as a guide.

Starting with the my app.html with the nav related markup.

<template>
    <require from="lib/bootstrap/dist/css/bootstrap.css"></require>
    <require from="Aurelia/list" as="contact-list"></require>

    <div class="page-host">
        <contact-list class="col-md-4"></contact-list>
        <router-view class="col-md-8"></router-view>
    </div>
</template>

The require statement tells Aurelia to load the specified resource. The as attribute of require is optional and defines how the imported resource will be accessed. By using the as attribute what would have been a list custom element is now a contact-list custom element.

The other change to this file was to render the contact-list element in addition to the router-view. Columns classes were assigned to both since they will be sharing view space now.

In app.js the configuration of the routes had to be adjusted. Before the blank route rendered the list view, but now the list view is rendered outside of the router so the detail route needed to be rendered for the blank route. The original route with an id still exists and is used to load the proper details when an item is selected from the contact list.

export class App {
    configureRouter(config, router) {
        config.title = 'Contacts';
        config.map([
            {
                route: ['', 'detail/:id'],
                name: 'detail',
                moduleId: './Aurelia/detail',
                nav: false,
                title: 'Detail'
            }
        ]);

        this.router = router;
    }
}

After the above changes I tried a test and got a detail view but no list view. After some digging I found out that the activate function was not being called on the list class. Turns out that since list view is being rendered outside of a router that the activate portion of the lifecycle does not happen. Since activate is where I was hitting my ASP.NET 5 web api no contacts were being loaded. The fix was to use the created portion of the lifecycle instead of activate.

created(){
    return this.http.fetch('contacts')
       .then(response => response.json())
       .then(contacts => this.contacts = contacts);
}

After that change all worked as I hoped it would. This is just a basic example of what is a very powerful concept. The more I learn about Aurelia the more I like it.

Combine Views with Aurelia Read More »

Aurelia Routing with a Parameter

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.

project

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.

contactDetailView

Aurelia Routing with a Parameter Read More »

Start Aurelia from an ASP.NET 5 Controller

Last week was all about getting Aurelia up and running from inside of Visual Studio with an ASP.NET 5 project. In last week’s post I started Aurelia from an html page in the wwwroot folder. This week I am going to use a controller action to kick off Aurelia.

Start by modifying the default HomeController, or any controller of your choice, to add an Aurelia action that just returns a view.

public IActionResult Aurelia()
{
    return View();
}

Next right-click on the View/Home folder and add a new item.

AddNewItem

Select MVC View Page and enter a name that matches the above controller action, Aurelia.cshtml for this example.

AddNewItemDialog

The contents of Aurelia.cshtml are very close to the html page from last week except for a couple of items. The aurelia-app is on a div instead of the body and the script source has been changed to go up a folder level since the view will be running out of the Home folder but the scripts will still be in the root of the site when it is being served.

<div aurelia-app>
    <script src="../jspm_packages/system.js"></script>
    <script src="../config.js"></script>
    <script>
        System.import("aurelia-bootstrapper");
    </script>
</div>

Now change _Layout.cshtml in the Views/Shared folder to provide a link to the new Aurelia action. In this case it is being added to the navigation bar.

<div class="navbar-collapse collapse">
    <ul class="nav navbar-nav">
        <li><a asp-controller="Home" asp-action="Index">Home</a></li>
        <li><a asp-controller="Home" asp-action="Aurelia">Aurelia</a></li>
        <li><a asp-controller="Home" asp-action="About">About</a></li>
        <li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
    </ul>
    @await Html.PartialAsync("_LoginPartial")
</div>

A couple of use cases for this might be to split a really large application into multiple sub-spas to keep user from having to load data for parts of the application they may use infrequently. I bet this same concept could be achieved with Aurelia itself, but with an existing application the approach above maybe an easier way to start.

Start Aurelia from an ASP.NET 5 Controller Read More »

Aurelia with ASP.NET 5 and Web API

Update: A series of posts has been made covering this same topic with the RTM versions of both ASP.NET Core and Aurelia. The first post in the series can be found here.

Now that I have a web API for my contacts I wanted to go back and add a front end to view them. I decided this would also be a good time to try out one of the single-page application frameworks instead of using razor. The number of choices for a spa framework is almost over whelming. Angular, Ember, Meteor, etc. In the end I decided to try out Aurelia mostly based on the fact that Rob Eisenberg is behind it.

To get going I highly recommend going through Aurelia’s get started guide. In addition to being a great introduction to Aurelia I also had to pull at least one file out of the sample project to get up and running in my ASP.NET project.

A couple more resources I recommend reading before getting started are a couple of blog post by Scott Allen. The first covers getting jspm going in ASP.NET 5 and the second is a guide to get started with Aurelia in ASP.NET 5.

Now to get started. The first step is to install jspm which is yet another package manager. It can be installed using with the following npm command.

npm install -g jspm@beta

jspm utilizes GitHub in some cases to install packages so it is recommended that jspm be configured with a login or api token for GitHub to avoid any anonymous API request limits.

jspm registry config github

The remaining commands should be run from the project directory that contains the project.json file.

Next run jspm’s init command. This command will ask a series of questions. The default is fine for most of them. The exceptions being the server baseURL should be ./wwwroot and I chose to use Babel as my transpiler.

jspm init

Now to install all the Aurelia bits that will be need to get an application up and hit a web API.

jspm install aurelia-framework
jspm install aurelia-bootstrapper
jspm install aurelia-fetch-client

Next add an html file (any name is fine) to the wwwroot folder. This html file will be how the Aurelia application is accessed and the file does not have a lot in it. If you read either Aurelia’s getting started or Scott’s blog the following contents will look very similar.

<!doctype html>
<html>
<head>
    <title>Hello from Aurelia</title>
</head>
<body aurelia-app>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
        System.import("aurelia-bootstrapper");
    </script>
</body>
</html>

When the above page loads Aurelia kicks in and looks for app.js and app.html. My app.js is based on the users.js from the getting started guide.

import {inject} from 'aurelia-framework';
import {HttpClient} from 'jspm_packages/github/aurelia/[email protected]/aurelia-fetch-client.js';
import 'fetch';

@inject(HttpClient)
export class App{
    heading = 'Contacts';
    contacts = [];

    constructor(http){
        http.configure(config => {
            config
              .useStandardConfiguration()
              .withBaseUrl('https://localhost:14830/api/');
        });

        this.http = http;
    }

    activate(){
        return this.http.fetch('contacts')
          .then(response => response.json())
          .then(contacts => this.contacts = contacts);
    }
}

This class acts as the view model for my contact list. In the constructor function an http client is set up with a base url that matches the base url for the project’s web API which is http://localhost:14830/api/ in this case. The activate function is called by Aurelia and is where the contacts API is called using http.fetch. The import to fetch is the file I had to copy out of the Aurelia get started application into the wwwroot folder. You may also notice that the other imports are referencing specific folders with specific versions. This is not something that should be done for anything other than a short demo. I need to do some learning on gulp and what exactly Aurelia requires to get that fixed.

The following is the app.html.

<template>
    <section>
        <h2>${heading}</h2>
        <div repeat.for="contact of contacts">
            <p>${contact.Name}</p>
            <div repeat.for="emailaddress of contact.EmailAddressModels">
                <p>${emailaddress.Address}</p>
            </div>
        </div>
    </section>
</template>

The ${heading} is binding the value that will be displayed to the heading property defined in the app class. Next notice the repeat.for which will repeat the element it is defined on and its children foreach in the container to the right of the of statement. For example the above is going to print the name of each contact and all that contact’s email address contained in the contacts property of the app class. The resulting page is ugly, but the point is to prove Aurelia running and displaying the proper data.

At this point to remove some complexity I removed the authorize attribute off of the contacts controller and ran a test. As you may have guessed the test failed.

The failure was caused by the API returning a single object that then contain all the contacts instead of an array of contacts. The cause of this issue is the changes I made to work around serialization issue I was having with circular references  from my entity framework navigation properties.

The fix was to remove the following from the ConfigureServices function of the Startup class.

services.ConfigureMvcJson(options =>
{
    options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.All;
});

Then in all the contact related classes I added the JsonIgnore attribute to the property that pointed back to the contact model.

public class ContactEmailModel
{
    public int ContactId { get; set; }
    public int Id { get; set; }
    [EmailAddress]
    public string Address { get; set; }

    [JsonIgnore]
    public ContactModel Contact {get; set;}
}

With those two changes everything started working. At this point I am not sure if that is the proper way to handle my issue or not. I have a feeling it will be something I will end up revisiting as I continue to learn more.

Aurelia with ASP.NET 5 and Web API Read More »