Now that we have a component that can display our tic tac toe game let's create a service that can communicate with our API, in our terminal type in
ng generate service services/gameService/gameService
I like to isolate each of my services in their own folder, but again, that is just me. We should end up with the following folder structure inside our project.
now this is where we are are going to put our communication to our back-end api logic.
before we start on our service we are first going to create a model for our application to use, we know that our api end points return data in the following format
{
"symbol": "x",
"x": 0,
"y": 1
}
this simply represents a valid move that the computer can make, so let's create a models folder within which there will be a Interface folder and we'll create an IMove interface and Move model which implements the IMove interface.
// Interface
export default interface IMove {
symbol:String;
x:Number;
y:Number;
}
// Model
import IMove from 'src/app/models/interfaces/IMove';
export default class Move implements IMove{
symbol: String;
x: Number;
y: Number;
}
with our payload defined, lets start by loading the httpClientModule, this module lets us communicate with our api, its a wrapper around Ajax which lets us request data from an api via JavaScript. To use this module open up our app.module.ts file and add import statement as well as import it into
with that done, lets create a Game Service interface where we can define our contract, create a _interfaces folder in our services folder and add a IGameService.ts file.
import { Observable } from 'rxjs';
import IMove from 'src/app/models/_interfaces/IMove';
export default interface IGameServiceInterface{
getHello(): Observable<String>;
getRandomMove(moves:string[]): Observable<IMove>;
getGoodMove(moves:string[]): Observable<IMove>;
getExpertMove(moves:string[]): Observable<IMove>;
}
above we import the observable type which will take the place of our traditional promise async solution. and of course we include our IMove interface.
we define four simple methods get hello is just a simple test where as the others will expect an array of all the games moves and will suggest a corresponding next move.
so lets now go to our GameServce.ts file and implement our defined contract.
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import IMove from 'src/app/models/_interfaces/IMove';
import IGameService from 'src/app/services/_interfaces/IGameService';
const httpOption = {
headers: new HttpHeaders(),
responseType: 'text' as 'json'
}
@Injectable({
providedIn: 'root'
})
export class GameService implements IGameService {
private gameUrl: string = 'https://app-api-tiktaktoe-dev.azurewebsites.net/api/v1';
constructor(private http: HttpClient) { }
getHello(): Observable<String> {
let url = `${this.gameUrl}/game/helloworld`;
return this.http.get<String>(url, httpOption);
}
getRandomMove(moves: string[]): Observable<IMove> {
let m = moves.reduce((result, current) => `${result}&m=${current}`)
let url = `${this.gameUrl}/game/randomMove?m=${m}`;
return this.http.get<IMove>(url);
}
getGoodMove(moves: string[]): Observable<IMove> {
throw new Error('Method not implemented.');
}
getExpertMove(moves: string[]): Observable<IMove> {
throw new Error('Method not implemented.');
}
}
above we use the httpClient service to make gets to our api to
- simple hello world case
- get a suggested next move that could be used for the computer player to make a move
next lets open up our grid.component.ts file and call our services getHello function on a cell click just to test.
import { Component, OnInit } from '@angular/core';
import { GameService } from 'src/app/services/gameService/game-service.service';
@Component({
selector: 'app-grid',
templateUrl: './grid.component.html',
styleUrls: ['./grid.component.scss']
})
export class GridComponent implements OnInit {
constructor(private service: GameService) { }
ngOnInit(): void { }
onMark(event: MouseEvent ,cellId: number): void {
console.log(event);
this.service.getHello().subscribe(data => alert(data));
}
}
now lets test our click event
and not only do we get the expected response from our api call, we also capture the mouse event. Next lets swap our hello world for our random move response.
import { Component, OnInit } from '@angular/core';
import { GameService } from 'src/app/services/gameService/game-service.service';
@Component({
selector: 'app-grid',
templateUrl: './grid.component.html',
styleUrls: ['./grid.component.scss']
})
export class GridComponent implements OnInit {
private _moves: string[] = [];
private _open: boolean = true;
constructor(private service: GameService) { }
ngOnInit(): void { }
onMark(event: MouseEvent ,cellId: number): void {
const source = event.target as HTMLSpanElement;
if(this._open && (source.innerText === '' || source.innerText === null)){
this._open = false;
source.innerText = 'X';
this._moves.push(`${cellId}x`);
this.service.getRandomMove(this._moves).subscribe(move => {
document.getElementById(`cell${move.x}${move.y}`).innerText = move.symbol.toUpperCase();
this._moves.push(`${((move.y as number)*3) + (move.x as number)}${move.symbol}`);
this._open = true;
});
}
}
}
with that complete lets test to see if we can fill in all of our cells.
and voila, we have a very rudimentary tic tac toe game sort-of, kind-of working.