Я точно не знаю, какова конечная цель, но я вижу несколько вещей, которые могут быть изменены.
Если вы просто хотите переключить каждый флажок, тогда снимите
(change)="change($event.checked, i, j)"
так что шаблон выглядит следующим образом
`
<div class="table">
<div class="row">
<div class="cell"></div>
<div *ngFor="let player of players" class="cell player">
{{ player.userName }}
</div>
</div>
<div *ngFor="let row of scoreTable; index as i" class="row">
<div class="cell player">
{{ players[i].userName }}
</div>
<div *ngFor="let cell of row; index as j" class="cell">
<input
type="checkbox"
*ngIf="cell !== 'x'"
[checked]="scoreTable[i][j]"
/>
</div>
</div>
</div>
`
будет работать
Также вы были близки с передачей значения чека в (изменение), но оно должно быть
(change)="change($event.currentTarget.checked, i, j)"
Причина обновления нескольких ячеек в вашем коде TS
change(newValue, i, j) {
console.log(newValue, i, j);
// This modifies one cell
this.scoreTable[i][j] = newValue;
// This modifies a different cell
this.scoreTable[j][i] = !newValue;
console.table(this.scoreTable);
}
В зависимости от того, что вы пытаетесь сделать, эта функция изменения может не понадобиться. Я выкладываю Stackblitz без него. Если он не достиг sh того, что вам нужно, пожалуйста, объясните подробнее, и я посмотрю, смогу ли я помочь.
Я обновил StackBlitz и добавил новый компонент, используя EventEmitter и Subject для обработки переключения.
https://stackblitz.com/edit/angular-y1cntr
app.component.ts
import { Component } from "@angular/core";
import { Subject } from "rxjs";
export interface IPlayer {
id: number;
userName: string;
}
type TableCell = "x" | boolean;
@Component({
selector: "my-app",
template: `
<div class="table">
<div class="row">
<div class="cell"></div>
<div *ngFor="let player of players" class="cell player">
{{ player.userName }}
</div>
</div>
<div *ngFor="let row of scoreTable; index as i" class="row">
<div class="cell player">
{{ players[i].userName }}
</div>
<div *ngFor="let cell of row; index as j" class="cell">
<app-player-checkbox [deselectMatchUp$]="deselectMatchUp$" [scoreTable]="scoreTable" [cell]="cell" [row]="i" [column]="j" (matchUpSelected$)="matchUpSelected($event)"></app-player-checkbox>
</div>
</div>
</div>
`,
styleUrls: ["./app.component.css"]
})
export class AppComponent {
deselectMatchUp$: Subject<boolean> = new Subject();
constructor() {
this.scoreTable = this.generateInitialTable();
}
matchUpSelected($event) {
if ($event.checked) {
this.deselectMatchUp$.next($event);
}
}
players: IPlayer[] = [
{ id: 1, userName: "a1" },
{ id: 2, userName: "b1" },
{ id: 3, userName: "c1" },
{ id: 4, userName: "d1" }
];
scoreTable: TableCell[][];
// generating table
generateInitialTable(): TableCell[][] {
const table: TableCell[][] = [];
for (let i = 0; i < this.players.length; ++i) {
table.push([]);
for (let j = 0; j < this.players.length; ++j) {
const value = i === j ? "x" : false;
table[i].push(value);
}
}
return table;
}
}
player-checkbox.component.ts
import { Component, OnInit, Input, EventEmitter, Output, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, tap } from 'rxjs/operators';
@Component({
selector: 'app-player-checkbox',
template: `<input
type="checkbox"
*ngIf="cell !== 'x'"
[(ngModel)]="checked"
(change)="matchUpSelected(row, column, checked)"
/>`,
styleUrls: ['./player-checkbox.component.css']
})
export class PlayerCheckboxComponent implements OnInit, OnDestroy {
@Input() row: number;
@Input() column: number;
@Input() cell: any;
@Input() scoreTable: any;
@Input() deselectMatchUp$: Subject<boolean>;
@Output() matchUpSelected$: EventEmitter<any> = new EventEmitter();
checked = false;
private onDestroy$: Subject<null> = new Subject();
constructor() { }
ngOnInit() {
this.deselectMatchUp$.pipe(
takeUntil(this.onDestroy$),
tap((x: any) => {
if (this.row === x.column && this.column === x.row) {
this.checked = false;
}
})
).subscribe()
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
matchUpSelected(row: number, column: number, checked: boolean) {
this.matchUpSelected$.emit({ row, column, checked});
}
}
Обновление для обеспечения запрошенного отслеживания.
Вернулось к одному компоненту, но воспользовалось преимуществом JS отслеживания по ссылке на сложные типы, чтобы обеспечить правильное переключение. Кроме того, я не был уверен, какой формат вам нужен, поэтому я использовал пару карт для преобразования массива массивов объектов в массив массивов логических и строк.
https://stackblitz.com/edit/angular-cpuuba
import { Component } from "@angular/core";
interface IPlayer {
id: number;
userName: string;
}
type TableCell = {
row: number;
column: number;
checked: boolean | string
};
@Component({
selector: "my-app",
template: `
<div class="table">
<div class="row">
<div class="cell"></div>
<div *ngFor="let player of players" class="cell player">
{{ player.userName }}
</div>
</div>
<div *ngFor="let row of scoreTable; index as i" class="row">
<div class="cell player">
{{ players[i].userName }}
</div>
<div *ngFor="let cell of row; index as j" class="cell">
<input type="checkbox"
*ngIf="cell.checked !== 'x'"
[(ngModel)]="cell.checked"
(ngModelChange)="playerUpdated(cell)">
</div>
</div>
</div>
`,
styleUrls: ["./app.component.css"]
})
export class AppComponent {
constructor() {
this.scoreTable = this.generateInitialTable();
}
playerUpdated(cell: TableCell) {
if (cell.checked) {
this.scoreTable[cell.column][cell.row].checked = false;
}
const arrayOfBooleans = this.scoreTable.map(x => x.map(y => y.checked));
console.log('arrayOfBooleans', arrayOfBooleans);
console.log('this.scoreTable', this.scoreTable);
}
players: IPlayer[] = [
{ id: 1, userName: "a1" },
{ id: 2, userName: "b1" },
{ id: 3, userName: "c1" },
{ id: 4, userName: "d1" }
];
scoreTable: TableCell[][];
// generating table
generateInitialTable(): TableCell[][] {
const table: TableCell[][] = [];
for (let i = 0; i < this.players.length; ++i) {
table.push([]);
for (let j = 0; j < this.players.length; ++j) {
const value = i === j ? "x" : false;
const obj: TableCell = {
row: i,
column: j,
checked: i === j ? "x" : false
}
table[i].push(obj);
}
}
return table;
}
}