Angular 2 components – piece

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 with templateUrl 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 imgUrlpositionX 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).

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s