Wednesday, 30 September 2020

Communicate between components

we are going to create a service that will communicate between our grid component and our difficulty component, so to do that let's first create a difficultyBusService; again just my naming convention.

ng generate service services/difficultyBus/difficultyBus

in our services folder we should now see the following

by default when we added our service we see that it is a singleton, meaning that it is shared between all of our various componenets where it is injected

the above class attribute ensures that only one difficulty bus exists that is shared amongst any components that instantiate it

import { Injectable } from '@angular/core';
import { ObservableSubject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class DifficultyBusService {

  constructor() { }

  // Observable backing field
  private difficultySubject = new Subject<string>();

  // Observable string streams
  public readonly difficultyObservableObservable<string> = this.difficultySubject.asObservable();

  // Service message commands
  public setDifficulty(difficultystring): void  {
    this.difficultySubject.next(difficulty);
  }
}

we have added three bits of code here

  • The backing field is an instance of the Subject object from RxJS. Is an implementation of the observer pattern
  • The readonly observable property allows our "watching" object to subscribe to changes
  • The setDifficulty method allows a class to push a difficulty to our BUS service which will then emit the change

Now that we have configured our bus, in this case it will be a one way bus, communicating from our difficulty component to our grid component, lets configure our difficulty component to publish a change in difficulty.

import { ComponentOnInit } from '@angular/core';
import { DifficultyBusService } from 'src/app/services/difficultyBus/difficulty-bus.service';

@Component({
  selector: 'app-opponent-chooser',
  templateUrl: './opponent-chooser.component.html',
  styleUrls: ['./opponent-chooser.component.scss']
})
export class OpponentChooserComponent implements OnInit {
  
  constructor(private difficultyBusDifficultyBusService) { }
  
  ngOnInit(): void {}

  selectedDifficultystring = "Easy";

  selectDifficulty(e:MouseEvent):void {
    this.selectedDifficulty = (e.target as HTMLSpanElement).innerText;
    this.difficultyBus.setDifficulty(this.selectedDifficulty);
  }
}

Now we only made two very simple changes here:

  1. In the constructor we inject our DifficultyBusService which facilitates this cross component communication.
  2. In the SelectDifficulty method which is fired every time we change the difficulty we set the new difficulty, now as you may recall what will happen is in the difficultyBusService we are going to call the next method on our difficulty subject which in turn will notify any watchers who have subscribed to the difficultyObservable.
With that said let's open up the grid component and observe our change.

import { ComponentOnInit } from '@angular/core';
import { GameService } from 'src/app/services/gameService/game-service.service';
import { DifficultyBusService } from 'src/app/services/difficultyBus/difficulty-bus.service';
import { Subscription } from "rxjs";
import IMove from "src/app/models/_interfaces/IMove";

@Component({
  selector: 'app-grid',
  templateUrl: './grid.component.html',
  styleUrls: ['./grid.component.scss']
})
export class GridComponent implements OnInit {
  private _movesstring[] = [];
  private _openboolean = true;
  private _difficultystring = "Easy";
  private difficultySubscriptionSubscription;
 
  constructor(private serviceGameServiceprivate difficultyBusDifficultyBusService) { }

  ngOnInit(): void { 
    this.difficultySubscription = 
      this.difficultyBus.difficultyObservable.subscribe(difficulty => {
        alert(difficulty)
        this._difficulty = difficulty;
      });
  }

  ngOnDestroy() { 
    // prevent memory leak when component destroyed 
    this.difficultySubscription.unsubscribe(); 
  }

  onMark(eventMouseEvent ,cellIdnumber): 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`);

      switch(this._difficulty){
        case "Easy":
          this.service.getRandomMove(this._moves).subscribe(this.markOnBoard.bind(this));
        break;
        case "Medium":
          this.service.getGoodMove(this._moves).subscribe(this.markOnBoard.bind(this));
        break;
        case "Difficult":
          this.service.getExpertMove(this._moves).subscribe(this.markOnBoard.bind(this));
        break;
        case "Human":
          this.service.getHello().subscribe(m=> alert(m));
        break;
      }
    }
  }

  private markOnBoard(mIMove): void {
    document.getElementById(`cell${m.x}${m.y}`).innerText = m.symbol.toUpperCase();
    this._moves.push(`${((m.y as number)*3) + (m.x as number)}${m.symbol}`);
    this._open = true;
  }
}

now here is where we make the bulk of our changes:

  1. We inject our DifficultyBuss into the constructor, now as we mentioned before it is a singleton, meaning that no matter how many times we inject it it is always the same instance, so the difficulty bus in this class is in memory the same difficulty bus as in our difficulty component.
  2. In the ngOnInit function we subscribe to the difficultyObservable property which will execute the delegate function which we defined, in this case it will alert us to the difficulty and set the backing field accordingly. 
  3. for good measure we reference this Subscription (from rxJS) which then allows us to unsubscribe from this observable should the component ever be destroyed, we do this to prevent memory leaks. In this app it is unnecessary since neither of these components will ever be destroyed , however it is always good practice.
  4. in our on mark method we call a different endpoint based on what the difficulty is.
  5. and finally we created this private markOnBoard method that we can pass to the service we use to figure out our next move to mark that move on the grid DRY principle  

Tuesday, 29 September 2020

Styling a componenet using ngClass

Now that we have a working tic tac toe game using an api end point for simple moves, lets create a new component that is going to let us choose between various endpoints, so let us choose between and easy, moderate and impossible computer response.

so let's get started by creating a second component, lets call it opponentChooser, because it is going to let us choose initially the difficulty of our opponent.

so lets start by typing in 

ng generate component components/opponentChooser


Now within our application if we look into our components folder we should now see our new component


Now we are going to make this very simple, we are going to have four settings:
  • easy
  • medium
  • impossible
  • human
in this post we are going to tackle the first 3 and we will look at websockets in a subsequent post to enable multi player games, but for now let's add the following function to our component file.

import { DirectiveResolver } from '@angular/compiler';
import { ComponentOnInit } from '@angular/core';

@Component({
  selector: 'app-opponent-chooser',
  templateUrl: './opponent-chooser.component.html',
  styleUrls: ['./opponent-chooser.component.scss']
})
export class OpponentChooserComponent implements OnInit {
  constructor() { }
  ngOnInit(): void {}

  selectedDifficultystring = "Easy";
  selectDifficulty(e:MouseEvent):void {
    this.selectedDifficulty = (e.target as HTMLSpanElement).innerText;
  }
}


this function will catch a click event and set a component property to the text of the source html element, next lets open up the corresponding html file and paste in the following

<div class="opponent-container">
  <div class="grid">
    <div id="difficultyEasy" (click)="selectDifficulty($event)">
      <span class="difficulty-content" [ngClass]="{'selected':selectedDifficulty == 'Easy'}">
        Easy
      </span>
    </div>
    <div id="difficultyMedium" (click)="selectDifficulty($event)">
      <span class="difficulty-content" [ngClass]="{'selected':selectedDifficulty == 'Medium'}">
        Medium
      </span>
    </div>
    <div id="difficultyDifficult" (click)="selectDifficulty($event)">
      <span class="difficulty-content"[ngClass]="{'selected':selectedDifficulty == 'Difficult'}">
        Difficult
      </span>
    </div>
    <div id="human" (click)="selectDifficulty($event)">
      <span class="difficulty-content" [ngClass]="{'selected':selectedDifficulty == 'Human'}">
        Human
      </span>
    </div>
  </div>
</div>

the above gives us four clickable options options which will let us track which difficulty is selected, now let's add the SCSS to make it look like something understandable.

.opponent-container{
  justify-contentcenter;
  displayflex;
  .grid{
    flex-directionrow;
    width:70vh;
    > div {
      width25%;
      padding-top25%;
      display:inline-flex;
      positionrelative;
      cursorpointer;
      text-aligncenter;
      
      &:hover{
        color:#555;  
      }
      
      .difficulty-content{
        positionabsolute;
        top:0;
        bottom:0;
        left:0;
        right:0;
        font-size1.5em;
        displayflex;
        flex-directioncolumn;
        justify-contentcenter;

        &.selected{
          background-color#ababab;
        }
      }
    }
  }
}

and what we should result with is


now as we toggle through each value it will be marked as selected by adjusting which one is highlighted in dark grey.








Thursday, 24 September 2020

Azure Powershell

 First to install the azure module in power shell type in

install-module -name az -AllowClobber

now that you have the module installed you can get a list of all the available modules using 

get-module az.* -list

this will list out all of the available azure powershell modules





Now if you would like to get all the commands within a particular module you could simply execute the following command

get-command -module az.signalr


Which would list all of the azure commands related to SignalR, now if you would want to say learn specifically about the Test-AzSignalR command you can execute

get-help new-azsignalr


which would provide you valuable information on how to use the specified command. Notice the remarks section allowing you to get more detailed information as well as examples.

finally to execute any of these command we have to connect to our azure account which we can do by executing 

connect-azaccount



Tuesday, 22 September 2020

Angular Service

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{
  symbolString;
  xNumber;
  yNumber;
}

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 { HttpClientHttpHeaders } 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 gameUrlstring = 'https://app-api-tiktaktoe-dev.azurewebsites.net/api/v1';
  
constructor(private httpHttpClient) { }

  getHello(): Observable<String> {
    let url = `${this.gameUrl}/game/helloworld`;
    return this.http.get<String>(urlhttpOption);
  }

  getRandomMove(movesstring[]): Observable<IMove> {
    let m = moves.reduce((resultcurrent=> `${result}&m=${current}`)
    let url = `${this.gameUrl}/game/randomMove?m=${m}`;

    return this.http.get<IMove>(url);
  }

  getGoodMove(movesstring[]): Observable<IMove> {
    throw new Error('Method not implemented.');
  }

  getExpertMove(movesstring[]): 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 { ComponentOnInit } 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 serviceGameService) { }

  ngOnInit(): void { }

  onMark(eventMouseEvent ,cellIdnumber): 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 { ComponentOnInit } 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 _movesstring[] = [];
  private _openboolean = true;
  
  constructor(private serviceGameService) { }

  ngOnInit(): void { }

  onMark(eventMouseEvent ,cellIdnumber): 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.