Почему следующий флажок автоматически установлен? - PullRequest
1 голос
/ 25 апреля 2020

У меня есть сгенерированная таблица в Angular, и я привязал ее к таблице HTML, с флажками в каждой ячейке, там у меня есть метод Change (), который изменяет таблицу

 change(newValue, i, j) {
    console.log(newValue, i, j);
    this.scoreTable[i][j] = newValue;
    this.scoreTable[j][i] = !newValue;
    console.table(this.scoreTable);
  }
          <input
            type="checkbox"
            *ngIf="cell !== 'x'"
            [checked]="scoreTable[i][j]"
            (change)="change($event.checked, i, j)"
          />

Работает нормально, но зачем проверять следующий флажок в той же строке ...?

https://stackblitz.com/edit/angular-uwuert

1 Ответ

1 голос
/ 25 апреля 2020

Я точно не знаю, какова конечная цель, но я вижу несколько вещей, которые могут быть изменены.

Если вы просто хотите переключить каждый флажок, тогда снимите

(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;
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...