JSON

Custom Pipes in Angular 2

I am working on a project that is using an ASP.NET Core web API to return JSON that is then used by Angular 2. The dates that needed to be formatted before being shown to the user which lead me to pipes.

Pipes

In Angular 2 pipes are away to apply some transformation on a field before its value is displayed to the user. Some common pipes are provided out of the box for things like numbers, dates and percentages.

For the following example created  is a property for the date a contact was added. To have created just display the month, date and year the date pipe will be used with the short date format.

{{contact.created | date: 'shortDate'}}

The above is standard Angular 2 binding  with an added pipe “|” followed by the name of the pipe which in this case is date. The built-in date pipe can take arguments for how to the date should be formatted. If the created date was today than the resulting output of the above binding would be 4/10/2016. For a full list of built-in formats see the Angular docs.

The Problem

I added the date pipe to the fields that needed formatting and ran the app. Instead of the formatted date I was expecting I got a partially rendered page. A quick peek in Chrome’s dev tools showed the following exception.

EXCEPTION: Invalid argument ‘2016-04-10T08:00:00’ for pipe ‘DatePipe’ in [{{contact.created | date: ‘shortDate’}}

Turns out that the date pipe only works if the value it is processing is a number or a date. I found this out by looking at the code for the date pipe which can be found here. My created property came from JSON which doesn’t have a way to denote something as a date. This means the created property is actually a string instead of a date..

Solution 1 – Convert to a date

One way to fix this issue is to use the string representation of the date to create a new date and assign it over the top of the existing property. For example:

contact.created = new Date(contact.created);

After the above is applied to all the dates that need formatted Angular’s date pipe can be used with no problems.

Solution 2 – Custom pipe

I decided to try writing a custom pipe that would take a string that should be a date and format it if it is actually date using Angular’s date pipe.  The following is the resulting pipe.

import {Pipe, PipeTransform} from 'angular2/core';
import {DatePipe} from 'angular2/common';

@Pipe({ name: 'dateString' })
export class DateString implements PipeTransform {
    transform(value: string, args: any[]): string {
        var parsedDate = Date.parse(value);
        if (isNaN(parsedDate)) {
            return "";
        }
        else {
            return new DatePipe().transform(new Date(parsedDate), args);
        }
    }
}

First Pipe and PipeTransform are imported. The class must be decorated with @Pipe which is also used to give the pipe its name. The class must also implement PipeTransform which defines a transform function.

The transform function is where all the work happens. It takes a value and formatting arguments and return the value that should be displayed. In my case the value is parsed to a date which returns the number of milliseconds since January 1, 1970 00:00:00 UTC or not a number if parsing fails for some reason. For the not a number result an empty string will be return and displayed to the user.

In the case the date is value then a new instance of Angular’s date pipe is created it handles formatting using the new date value which is then return and displayed for the user. In order to use Angular’s date pipe inside my custom pipe it needed to be imported as well.

Custom Pipe Usage

To use a custom pipe it must be imported by the view model class of the view that needs formatted.

import { DateString } from './dateString.pipe';

Then it must be added to the component decorator.

@Component({
    selector: 'viewContacts',
    templateUrl: 'viewContacts.component.html',
    pipes: [DateString]
})

The view will now be able to use the pipe. The syntax is the same as built-in pipes just with the name provided in the pipe decorator which is dateString in this example.

{{contact.created | dateString: 'shortDate'}}

Suggestions

The above are two of the solutions I came up with when I had problems with Angular’s built in date pipe. They are by no means the only options. If you have a suggestion on a better way to handle dates coming from JSON please leave a comment.

Also note that there is an issue logged on the Angular repo to support strings in the built in date pipe which can be found here.

Custom Pipes in Angular 2 Read More »

Basic Web API with ASP.NET 5

I am going to create basic web API access to the contacts data I have been using in previous posts. To start with I added an API folder to my project to hold my API controller. Next I added a contacts controller to the API folder by right clicking on the folder and selecting add new item.

AddNewItem

From the add new item under DNX selected Web API Controller Class, entered a name and clicked add.

AddNewItemDialog

From the resulting code I removed all the actions except for two get functions.

[Route("api/[controller]")]
public class ContactsController : Controller
{
    private readonly ContactsDbContext _dbContext;

    public ContactsController(ContactsDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // GET: api/values
    [HttpGet]
    public async Task<IEnumerable<ContactModel>> Get()
    {
        return await GetContacts().ToListAsync();
    }

    // GET api/values/5
    [HttpGet("{id}")]
    public async Task<ContactModel> Get(int id)
    {
        return await GetContacts()
                     .Where(c => c.Id == id).FirstOrDefaultAsync();
    }

    private IQueryable GetContacts()
    {
        var contacts = from c in _dbContext.ContactModels
                                 .Include(c => c.AddressModels)
                                 .Include(c => c.EmailAddressModels)
                                 .Include(c => c.PhoneModels)
                       select c;
        return contacts;
    }
}

The above code contains a lot of new concepts I am going to break it down more.

[Route("api/[controller]")]
public class ContactsController : Controller

The first thing to notice is the route attribute on the class declaration. The route attribute is how the routing engine determines where to send requests. Using [controller] tells the routing engine to use the class name minus the word controller. For example the above route handles api/contacts.

private readonly ContactsDbContext _dbContext;

public ContactsController(ContactsDbContext dbContext)
{
    _dbContext = dbContext;
}

The constructor takes the DbContext needed to access contacts. Note that the context is being automatically injected via the constructor thanks to the fact that ASP.NET 5 now comes with dependency injection out of the box.

// GET: api/values
[HttpGet]
public async Task<IEnumerable<ContactModel>> Get()
{
    return await GetContacts().ToListAsync();
}

// GET api/values/5
[HttpGet("{id}")]
public async Task<ContactModel> Get(int id)
{
    return await GetContacts()
                 .Where(c => c.Id == id).FirstOrDefaultAsync();
}

First get function returns all contacts and the second returns a specific contact based on the contact’s ID.

private IQueryable<ContactModel> GetContacts()
{
    var contacts = from c in _dbContext.ContactModels
                             .Include(c => c.AddressModels)
                             .Include(c => c.EmailAddressModels)
                             .Include(c => c.PhoneModels)
                   select c;
    return contacts;
}

Note that the query contains three includes and each of the included classes contain a navigation property back to the main contact. For example here is the email address model.

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

    public ContactModel Contact {get; set;}
}

All of the above compiles and seems to run fine, but will not provide a response. The navigation property for contact creates a circular reference that the response serializer throws an exception trying to serialize.

Thankfully the framework has a configuration option to work around this problem. In the ConfigureServices function of the Startup class add the following.

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

The above options marks the Contact property as a reference and does not try to circularly serialize it.

Now by running the project and going to http://localhost:port/api/contacts/1 in the browser I get all the contact data related to the contact with an ID of 1. I recommend using something like Postman to make the result more readable if you don’t have a front end to display the data.

Basic Web API with ASP.NET 5 Read More »