This article is part of a series – the contents page is here.
So far we’ve talked a lot about chess and not much about Angular. It’s time to change tack and look at some components. We’ll start with the simplest component: ‘piece’, which represents a single chess piece on the playing board UI. This article might be of interest to developers who are just starting to explore Angular 2.
Consuming the component
It might seem like putting the cart before the horse, but let’s start by looking at how we will consume the piece component. Here’s the relevant snippet from game.component.html
:
<piece *ngFor="let occupiedSquare of occupiedSquares" [squarePiece]="occupiedSquare"> </piece>
Straight away we notice that this is a custom element. Once we get everything wired up, Angular will render using our piece component.
But one piece is no good; we need an element for every piece on the board. Hence the *ngFor
directive that loops over the occupiedSquares
collection. occupiedSquares
is a property of the game
component that holds an array of OccupiedSquare
objects. OccupiedSquare
is defined in ChessElements.ts; it’s simply a combination of a BoardSquare
and a ColouredPiece
(so an OccupiedSquare
instance can represent a white pawn on e4, for example).
Finally, notice the [squarePiece] attribute. The square brackets tell us that squarePiece is an input property of the piece component. (When you see square brackets in an Angular 2 element, think “the data is being pushed into the component.”) In this case the value is occupiedSquare
, which is the loop variable from our *ngFor.
Declaring the component
So we know that our component will be created for each piece and it will be given a rich object with all the details of the square and piece in question. Fantastic – talk about spoon feeding! Let’s start building a component to consume it, in piece.component.ts
:
import { Component, Input } from '@angular/core'; import * as Chess from '../engine/ChessElements'; @Component({ selector: 'piece', styleUrls: ['./piece.component.css'], template: `<img src="{{imgUrl}}" class="piece" [style.left.px]="positionX" [style.top.px]="positionY" />` }) export class PieceComponent { @Input() public squarePiece: Chess.OccupiedSquare; constructor() { } // ... declarations of imgUrl, positionX and positionY go here. }
We start by importing the code that our component references. This is a pretty simple component so there isn’t much: Angular’s @Component and @Input decorators and Shallow Thought’s Chess types.
The PieceComponent class
If you look down below the @Component decorator you will see that this file is actually declaring a class: PieceComponent
. At runtime, every component will in fact be an instance of this class. As a class, we can populate it with fields, properties, methods, etc. – and we will do that shortly. But first we need to amplify this plain old class into a full Angular component, which we do by decorating it with @Component.
The Component decorator
The @Component decorator is the heart of our component’s declaration. Everything else in this TypeScript file is providing the data that’s demanded in this declaration.
- The
selector
is “piece”. This will wire the component up to the element that we saw earlier. - The
styleUrls
value points to a CSS file where we can style the HTML element that our component emits. - The
template
value is a literal string containing the HTML to be emitted. An embedded string is appropriate here because we don’t have much HTML, but if there was a lot more then we would put it in a separate .html file and point to it withtemplateUrl
instead.
The class properties
Recall that our element injected the occupiedSquare object via an attribute called “squarePiece”. To make that work we need to declare squarePiece
as a class property and decorate it with @Input. That’s all!
Notice that the template refers to imgUrl
, positionX
and positionY
. We will declare these as properties of our class:
public get imgUrl(): string {
const colour = (this.squarePiece.piece.player === Chess.Player.White) ? "l" : "d";
let pieceCode: string;
switch (this.squarePiece.piece.piece) {
case Chess.PieceType.Pawn:
pieceCode = "p";
break;
case Chess.PieceType.Knight:
pieceCode = "n";
break;
case Chess.PieceType.Bishop:
pieceCode = "b";
break;
case Chess.PieceType.Rook:
pieceCode = "r";
break;
case Chess.PieceType.Queen:
pieceCode = "q";
break;
case Chess.PieceType.King:
pieceCode = "k";
break;
}
return `assets/img/${pieceCode}${colour}.png`;
}
private static squareSize = 60;
public get positionX(): number {
return (this.squarePiece.square.file - 1) * PieceComponent.squareSize;
}
public get positionY(): number {
return (8 - this.squarePiece.square.rank) * PieceComponent.squareSize;
}
I’ve always loved read-only (calculated) properties. They can do most of the heavy lifting in the system while being easy to reason about (since they have no side-effects).