Since it's a seat selection, better way would be to define an array of object with x, y and selected properties. It can then be toggled directly in the template without using any event handler. Try the following
Controller
export class ClickSVGComponent implements OnInit {
public seats: Array<Array<{x: number, y: number, selected: boolean}>> = [];
range = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
public toggleStyle: boolean = false;
constructor() {
for (let i = 0; i < 10; i++) {
this.seats[i] = [];
for (let j = 0; j < 10; j++) {
this.seats[i].push({x: (i * 10), y: (j * 10), selected: false});
}
}
}
ngOnInit() {
}
}
Template
<svg width="210mm" height="297mm" version="1.1" viewBox="0 0 210 297" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<g>
<ng-container *ngFor="let i of range">
<ng-container *ngFor="let j of range">
<rect
[attr.fill]="seats[i][j].selected ? 'red': ''" <!-- use ternary operator to set the fill value -->
id="{{i}}{{j}}a"
[attr.x]="seats[i][j].x"
[attr.y]="seats[i][j].y"
width="8"
height="8"
(click)="seats[i][j].selected = !seats[i][j].selected"
/>
</ng-container>
</ng-container>
</g>
</svg>
I've modified your Stackblitz
Update: max selection condition
I've changed the object structure to introduce a maxSelected boolean that denotes if the maximum no. of selections has been reached. To check for the condition multiple array maps and an array concat is applied in a counter() event handler for click event.
The quickest way to understand it would be to dissect the conditions and observe of the output of each statement. It is a fairly straight-forward condition written as a single statement.
The template is also adjusted for the condition. We need to allow re-selection if the maxReached is again set to false.
Controller
import { Component, OnInit } from '@angular/core';
export const MAX_SELECTION = 9; // <-- max no. of seats allowed to select
interface Seats { // <-- a collection of seats
maxReached: boolean,
attr: Array<Array<Attribute>> // array of array - `x` denotes the row, `y` denotes the column
}
interface Attribute { // <-- properties of each seat
x: number,
y: number,
selected: boolean
}
@Component({
selector: 'app-click-svg',
templateUrl: './click-svg.component.html',
styleUrls: ['./click-svg.component.css']
})
export class ClickSVGComponent implements OnInit {
public seats: Seats = {maxReached: false, attr: []};
range = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
public toggleStyle: boolean = false;
constructor() {
for (let i = 0; i < 10; i++) { // <-- loop through row of seats
this.seats.attr[i] = []; // <-- each row is initially empty
for (let j = 0; j < 10; j++) { // <-- loop through each seat of the row
this.seats.attr[i].push({x: (i * 10), y: (j * 10), selected: false});
// ^ ^ ^
// | | |
// use row value to set x --- | |
// use column value to set y coord. ------- |
// by default the seat is not selected ----------------
}
}
}
ngOnInit() {
}
counter() {
/*
Do the following to understand what each statement does
const seatsAll = this.seats.attr.map(row => {
console.log('row': row)
row.map(seat => {
console.log('seat': seat);
seat.selected;
})
})
console.log('all seats': seatsAll);
const seatsSelectedConcat = [].concat.apply([], seatsAll);
console.log('all seats single array': seatsSelectedConcat);
const seatsSelectedTrue = seatsSelectedConcat.filter(status => status);
console.log('all seats single array': seatsSelectedTrue);
*/
const selected = (
[].concat.apply([], ( // <-- output (Array(100)): [true, false, true, false, true,...]
this.seats.attr.map( // <-- output (Array<Array(10)>(10)): [[true, false...], [false, true,...], ...]
row => row.map(
seat => seat.selected // <-- output (Array(10)): [true, false, true...]
)
)
))
)
.filter(status => status) // <-- output only true: [true, true, true]
.length; // <-- number of seats selected
if (selected === MAX_SELECTION) {
this.seats.maxReached = true;
} else {
this.seats.maxReached = false;
}
}
}
Template
<p>
Please select a maximum of 9 seats. <br>
<span style="color: red" *ngIf="seats.maxReached">
Maximum number of seats selected.
</span>
</p>
<svg width="210mm" height="297mm" version="1.1" viewBox="0 0 210 297" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<g>
<ng-container *ngFor="let i of range"> <!-- same loop as the controller - row -->
<ng-container *ngFor="let j of range"> <!-- column -->
<rect
[attr.fill]="seats.attr[i][j].selected ? 'red': ''"
id="{{i}}{{j}}a"
[attr.x]="seats.attr[i][j].x"
[attr.y]="seats.attr[i][j].y"
width="8"
height="8"
(click)="
seats.attr[i][j].selected ?
seats.attr[i][j].selected = !seats.attr[i][j].selected : <!-- if seat selected already deselect it -->
!seats.maxReached ?
seats.attr[i][j].selected = !seats.attr[i][j].selected : <!-- selected unselected seat only if max condition `false` -->
''; <!-- don't select the seat if max condition `true` -->
counter()"
/>
</ng-container>
</ng-container>
</g>
</svg>
Updated Stackblitz