当数组元素相同时,为什么Splice只删除最后一个元素?

3

请点击这个 StackBlitz 链接不可用

请点击这个 StackBlitz 链接正常工作

我的问题与 Array.splice 有关。当数组数据不同的时候,Array.splice 可以正常工作,但是当数组数据相同的时候,Array.splice 只会删除最后一个索引,并忽略提供的索引以从数组中删除元素。

两个示例都共用相同的 HTML 模板

<div class="container">
    <table class="table table-dark table-striped table-hover">
        <thead>
           <tr>
               <td scope="col"> Index </td>
               <td scope="col"> Value </td>
               <td scope="col"> Action </td>
           </tr>
        </thead>
        <tbody>
            <tr *ngFor="let row of rows; let i = index"  [@itemAnim]>
                <td> {{i}} </td>
                <td>{{row}}</td>
                <td><button class="btn btn-danger" (click)="deleteRow(i)">Delete</button></td>
           </tr>
        </tbody>
    </table>
    <button class="btn btn-primary " (click)="addRow()">Add row</button>
</div>

看这个例子按预期工作:
index = 1;
rows = [1]; 

addRow() {   
   this.rows.length > 0 ?  this.index ++ : this.index = 1;
   this.rows.push(this.index)
}

deleteRow(row: number) {
   return this.rows.length > 0 ? this.rows.splice(row, 1) : this.index = 1;
}

这个无法工作:

rows = [1]; 

addRow() {   
  this.rows.push(1)
}

deleteRow(row: number) {
   this.rows.splice(row, 1) ;
}

在上述代码中,当单击“addButton”时,我只推入1。而且删除按钮仅从最后一个索引中进行删除,而不提供行号索引。 我的问题是为什么会发生这种情况? 上面的stackBlitz链接显示了相同的模糊性。

我认为这里有一些误解。尝试每次推入一个新数字,这样你就可以看到正在被删除的内容。这个 stackblitz 是否消除了你的困惑?(派生自你的) - Nicholas K
你说得对。我已经在粗体字的“查看此示例按预期工作”部分的问题中提到了这一点。我的问题是,在第二个部分“而且这不起作用:”中,为什么将相同的元素推入splice中不起作用。 - Developer
你说的“不工作”是什么意思?能详细说明一下吗?分叉的 Stackblitz 使用了你提到的代码,但似乎不起作用。 - Nicholas K
如果您总是推送相同的元素,那么您如何知道被删除的是哪一个,这就是我的意思。 - Nicholas K
你要传递哪个参数给 deleteRow 函数?虽然引用外部网站的链接可能会有所帮助,但请确保所有相关信息都在问题中,而不是在链接后面。 - trincot
@trincot 我已经编辑了问题,并且也包含了模板文件。 - Developer
1个回答

5

因为你实际上是在删除所选的索引,但是Angular没有关于值之间差异的任何信息,让我们看一个例子:

我们有这个数组:[1, 1, 1, 1] ,你删除第二项,然后在删除之前,Angular将会看到:

[1, 1, 1, 1]

并且在之后:

[1, 1, 1]

此时Angular只知道有一个项目被移除,但不知道是哪个项目,因此Angular只会移除最后一个项目。

您需要确保传递的值或引用不同(建议使用引用),这样Angular才能区分数组项。

您可以通过使用对象,如:{ data: 1 }来解决此问题,您的数组将如下所示:

[
  { data: 1 },
  { data: 1 },
  { data: 1 },
  { data: 1 }
]

为什么会有效?因为引用不同 (值类型VS引用类型),即使数据相同,angular也会看到不同的引用。

这个方法适用于您:

rows = [{ data: 1 }]; 

addRow() {   
  this.rows.push({ data: 1 });
}

deleteRow(row: number) {
  this.rows.splice(row, 1);
}

在这个例子中,您总是传递一个具有其自己引用的新对象。
这另一个例子将无法工作。
sameReference = { data: 1 };

rows = [this.sameReference]; 

addRow() {   
  this.rows.push(this.sameReference);
}

deleteRow(row: number) {
  this.rows.splice(row, 1);
}

因为sameReference变量存储的是引用而非对象,Angular将无法(再次)区分项目之间的差异,并且当您更改一个元素的数据值时,所有元素都将得到该值,因为在数组中只有一个对象被引用了N次。
这样也可以工作。
sameReference = { data: 1 };

rows = [{ ...this.sameReference }]; 

addRow() {   
  this.rows.push({ ...this.sameReference });
}

deleteRow(row: number) {
  this.rows.splice(row, 1);
}

因为在这里我们正在将对象的值复制到一个具有不同引用的新对象中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接