Hi, Today we are going to learn about GuildRunner sandbox Angular 2 application step-by-step.
I recently released version 0.0.2 of my GuildRunner sandbox Angular 2 application. Changes made during this release include:
- The addition of a Bootstrap-powered navigation bar (main-navigation.component.ts) which necessitated adding CDN calls for jQuery and Bootstrap JS (index.html).
- The addition of Angular 2 routing (main.ts, app.routing.ts).
- The creation of guild data for use by the in-memory web API (db/guilds.ts).
- The creation of Guild, Address, and Member domain classes to be populated with the guild data (the address.ts, guild.ts, and member.ts files in the “domain” folder) via the GuildService (guild.service.ts).
- The creation of a master view that displays data from a list of Guild objects within a table (guilds-master.component.ts).
- The creation of a “sandbox” area of the application where I can keep experimental and diagnostic features (the “sandbox” folder).
The in-memory web API has limitations
The in-memory web API is currently limited to mimicking a shallow data graph. It allows you to mock the following types of REST calls:- app/guilds (to retrieve all guilds)
- app/guilds/1 (to retrieve the guild with an id of 1)
- app/guilds/?name=Blacksmiths (to retrieve the guild or guilds based on the query string)
- app/guilds/1/members/1 (retrieving the member with id of 1 from guild 1)
Instantiating domain model classes
In the Tour of Heroes tutorial, the instructions have you create a Hero class with properties for the id and name of the hero. Later, that class is used to declare a component variable with a data type of an array of Heroes:
heroes: Hero[];
…which is then populated with the hero data, an array of object literals:
[
{id: 11, name: 'Mr. Nice'},
{id: 12, name: 'Narco'},
...
]
…like so:
this.heroService.getHeroes().then(heroes => this.heroes = heroes);
From a coding standpoint, declaring the “heroes” variable as an array
of Hero objects ensures that another developer cannot use code to
populate that variable with anything but Hero objects, but that
declaration is meaningless at runtime. Doing the same thing with my
guild data:
//Populates the "guilds" variable with the raw retrieved data (array of object literals)
export class GuildsMasterComponent implements OnInit {
guilds: Guild[];
constructor( private guildService: GuildService ) { }
ngOnInit() {
this.guildService.getGuilds().then( guilds => this.guilds = guilds )
}
}
…results in the “guilds” variable being populated with the raw array
of guild object literals, each with an address object literal and an
array of member object literals. But that’s not what I wanted: I wanted
an array of Guild objects with included Address and Member objects.So I wrote the code to instantiate the desired objects, populating the property values via the constructor method:
//guilds-master.component.ts
...
import { Guild } from '../domain/guild';
...
export class GuildsMasterComponent implements OnInit {
guilds: Guild[] = [];
constructor( private guildService: GuildService ) { }
ngOnInit() {
this.guildService.getGuilds().then( guilds => {
guilds.forEach( guild => {
this.guilds.push( new Guild( guild ) )
})
} );
}
}
//guild.ts
import { Address } from './address';
import { Member } from './member';
export class Guild {
id: number;
name: string;
expenses: number;
revenue: number;
profit: number;
address: Address;
members: Member[] = [];
constructor( guildData:any ) {
this.id = guildData.id;
this.name = guildData.name;
this.expenses = guildData.expenses;
this.revenue = guildData.revenue;
this.profit = this.calculateProfit();
this.address = new Address( guildData.address );
guildData.members.forEach( member => {
this.members.push( new Member( member ) );
}, this );
}
calculateProfit() {
return this.revenue - this.expenses;
}
}
Providing my master view with an array of Guild objects allowed me to
display the profit of each guild in addition to the raw guild data
provided by the in-memory web API.The currency pipe
This release marked my first time using one of the built-in Angular 2 pipes, though it was pretty similar to my experiencing using the built-in filters in Angular 1.I was a tad surprised that the default CurrencyPipe settings would result in the number being prefixed with “USD” rather than a dollar sign. But a quick glance through the CurrencyPipe documentation gave me the settings I wanted and instructions on how to further control the output with the DecimalPipe:
<td align="right”>{{guild.revenue | currency:'USD':true:'.2-2' }}</td>
In cases where you wanted your application to support
internationalization, I imagine you could use a component variable to
dynamically affect the currency code:
<td align="right”>{{guild.revenue | currency:currencyCode:true:'.2-2' }}</td>
Good luck!