看起来很简单,但我不知道如何有效地解决这个问题。
我有两个数组activeClasses
和doneClasses
,每个数组都包含JavaScript对象作为它们的元素。
每个元素都应该能够标记为"活动"或"完成",并且应该从当前数组中删除,如果单击"保存"后其状态发生变化,则应将其添加到另一个数组中。如何在不混淆数组索引的情况下实现这一点?除了选择多个元素时,行为与预期一致:
https://stackblitz.com/edit/angular-etzocz?file=src%2Fapp%2Fapp.component.html
TS
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
activeChanged:Array<boolean> = [];
doneChanged:Array<boolean> = [];
toggleActive(i) {
this.activeChanged[i] = !this.activeChanged[i];
console.log('activeChanged:');
console.log(this.activeChanged);
}
toggleDone(i) {
this.doneChanged[i] = !this.doneChanged[i];
console.log('doneChanged:');
console.log(this.doneChanged);
}
save() {
var activeToBeDeleted:Array<number> = [];
var doneToBeDeleted:Array<number> = [];
//Check if active classes have changed
this.activeChanged.forEach(function (elem, index) {
//Has changed
if (elem) {
this.doneClasses.push(this.activeClasses[index]);
//Add to activeToBeDeleted
activeToBeDeleted.push(index)
}
}.bind(this))
//Check if done classes have changed
this.doneChanged.forEach(function (elem, index) {
//Has changed
if (elem) {
this.activeClasses.push(this.doneClasses[index]);
//Add to doneToBeDeleted
doneToBeDeleted.push(index)
}
}.bind(this))
console.log('before deletion')
console.log(this.activeClasses)
console.log(this.doneClasses)
//Delete array elements that were changed
activeToBeDeleted.forEach(function(elem) {
this.activeClasses.splice(elem,1)
}.bind(this))
doneToBeDeleted.forEach(function(elem) {
this.doneClasses.splice(elem,1);
}.bind(this))
console.log('after deletion')
console.log(this.activeClasses)
console.log(this.doneClasses)
//Rewrite activeChanged and doneChanged arrays again with false
this.activeChanged = new Array(this.activeClasses.length).fill(false)
this.doneChanged = new Array(this.doneClasses.length).fill(false)
}
//As from database
activeClasses:Array<Object> = [
{
name: 'test1'
},
{
name: 'test2'
}
];
doneClasses:Array<Object> = [
{
name: 'test3'
},
{
name: 'test4'
}
];
ngOnInit() {
//Fill activeChanged and doneChanged with false by default
this.activeChanged = new Array(this.activeClasses.length).fill(false)
this.doneChanged = new Array(this.doneClasses.length).fill(false)
}
}
HTML
<div *ngFor="let active_class of activeClasses; let i = index" style="background-color: blue; text-align: center; padding: 20px; color: white;">
<button *ngIf="!activeChanged[i]" (click)="toggleActive(i)">Mark as done</button>
<button *ngIf="activeChanged[i]" (click)="toggleActive(i)">Mark as active</button>
{{ active_class.name }}
</div>
<div *ngFor="let done_class of doneClasses; let i = index" style="background-color: red; text-align: center; padding: 20px; color: white;">
<button *ngIf="!doneChanged[i]" (click)="toggleDone(i)">Mark as active</button>
<button *ngIf="doneChanged[i]" (click)="toggleDone(i)">Mark as done</button>
{{ done_class.name }}
</div>
<button (click)="save()">Save</button>
这是因为当您按自然排序顺序拼接项目时,在删除第一个项目后,项目的数组索引会发生变化。
解决方案是在拼接之前调用reverse()
,这样可以在不影响索引的情况下向下遍历数组。
这修复了它:
//Delete array elements that were changed
activeToBeDeleted.reverse().forEach(function(elem) {
this.activeClasses.splice(elem,1)
}.bind(this))
doneToBeDeleted.reverse().forEach(function(elem) {
this.doneClasses.splice(elem,1);
}.bind(this))
为什么它有效?
首先,activeChanged
和doneChanged
是在修改的项的索引处存储布尔值的数组(活动或完成,请参见toggle
方法(。当您在Save
方法中第一次在这些数组上循环时,它会按升序在项上循环,因此,您将按升序将索引存储到activeToBeDeleted
和doneToBeDeleted
数组中。
因此,在那之后,当您在activeToBeDeleted
和doneToBeDeleted
数组上循环并从activeClasses
或doneClasses
中删除时,虽然第一次删除有效,但其他删除都不起作用,因为第一次删除操作从数组的开头删除了一个项,并导致以下所有索引移位和不正确。
该解决方案之所以有效,是因为通过反转索引列表(按降序排列(,可以从数组的末尾开始删除,从而自然地保留所有索引。我建议你用钢笔和铅笔,实际上这是一种经典的图案。