Search code examples
javascriptangularcomponents

Minesweeper - Angular


I'm making angular version of MineSweeper, but I've got the problem with event. I mean, I've got my field component like that:

import {Component, Input, OnInit} from '@angular/core';
import {style} from '@angular/animations';

@Component({
  selector: 'app-pool',
  templateUrl: './field.component.html',
  styleUrls: ['./field.component.sass']
})

export class FieldComponent implements OnInit {
  isBomb = false;
  isInfo = false;
  isClickable = true;
  touchesBombCount = 0;
  isMarkedAsBomb = false;
  isExpanded = false;
  poolID: number;
  isBombExpanded = false;

  @Input() display: string;

  constructor() {
  }

  ngOnInit() {
  }

  expandMe() {
    this.isExpanded = !this.isExpanded;
    if (this.isBomb && this.isExpanded) {
      this.isBombExpanded = !this.isBombExpanded;
    }
    console.log(this);
  }

  onFieldClick(event: any) {
    this.expandMe();
    this.isClickable = false;
    console.log(event);
  }

  onRightClick(event: any) {
    event.preventDefault();
    this.isMarkedAsBomb = !this.isMarkedAsBomb;
  }

}

Then I'm creating fields in board component:

import {Component, Input, OnInit} from '@angular/core';
import {FieldComponent} from '../field/field.component';

@Component({
  selector: 'app-board',
  templateUrl: './board.component.html',
  styleUrls: ['./board.component.sass']
})
export class BoardComponent implements OnInit {
  fieldList: FieldComponent[] = [];
  bombCount: number = 10;
  poolBombList: FieldComponent[] = [];
  private allBombsPlanted = false;
  isGameOver = false;

  @Input() poolCount: number;

  constructor() {
  }

  ngOnInit() {
    this.createBoardFromPools();
    while (!this.allBombsPlanted) {
      this.plantTheBombs();
    }
    console.log(this);
  }

  createBoardFromPools() {
    for (let i = 0; i < this.poolCount; i++) {
      const pool = new FieldComponent();
      pool.poolID = i;
      this.fieldList.push(pool);
    }
  }

  plantTheBombs() {
    const poolCount = this.fieldList.length - 1;
    const poolToPlant = Math.floor((Math.random() * poolCount));
    if (!this.checkIfHasBomb(poolToPlant)) {
      this.poolBombList.push(this.fieldList[poolToPlant]);
      this.fieldList[poolToPlant].isBomb = true;
    }
    if (this.poolBombList.length === this.bombCount) {
      this.allBombsPlanted = true;
    }
  }

  checkIfHasBomb(poolToPlant): boolean {

    for (let i = 0, iMax = this.poolBombList.length; i < iMax; i++) {
      if (this.poolBombList[i].poolID === poolToPlant.poolID) {
        return true;
      }
    }
    return false;
  }

}

My question is:

why when I console.log (this) of field component, then the bomb is marked as false, even, when on my general list of fields in board component ( fieldList: FieldComponent[] = []) shows it is a bomb actually?

I'm moving from vanilla to angular, maybe I've created fields in board component not correctly?


Solution

  • I think a better solution would be to create a model for your Field like:

    export interface FieldModel{
      poolID: number;
      isBomb:boolean;
      isInfo:boolean;
      isClickable:boolean;
      touchesBombCount:boolean;
      isMarkedAsBomb:boolean;
      isExpanded:boolean;
      isBombExpanded:boolean;
    }
    
    

    Now in your BoardComponent you would simply create FieldModel objects and push them into the list of fields.

    In the template of BoardComponent you would create the FieldComponent via the declarative approach in a *ngFor iterating over the FieldModels and passing them as input properties to your FieldComponent.

    Other Solution

    If you want to dynamically create components, like you want to with your FieldComponent inside the BoardComponent you should use a ComponentFactory. You will also need a ViewContainerRef where your components can get rendered inside.

    In the template of your BoardComponent you need some container for your fields:

    <ng-template #boardContainer></ng-template>
    

    In BoardComponent you need to get the container with the help of the ViewChild decorator and inject a ComponentFactoryResolver:

    export class BoardComponent implements OnInit {
      ...
      @ViewChild('boardContainer',{read:ViewContainerRef,static:false}) boardContainer:ViewContainerRef;
    
      constructor(private componentFactoryResolver:ComponentFactoryResolver) {
      }
      ...
    }
    

    Now you can create FieldComponents like shown below:

    createFieldComponent(){
     const componentFactory = this.componentFactoryResolver.resolveComponentFactory(FieldComponent);
     const componentRef:ComponentRef<FieldComponent> = this.boardContainer.createComponent(componentFactory);
    }
    

    The componentRef then offers you access to the component instance.