使用带有 Angular CDK DnD 的地图时如何更新视图?
How to update the view when using maps with Angular CDK DnD?
我有一个小清单,其中包含一些可以重新排序的项目 (see stackblitz)。在内部,该列表是作为地图实现的。方便的是,Angular 提供了管道键值,它允许一种简单的方法来遍历映射,如下所示:
*ngFor="let item of doc.items | keyvalue:sortHash"
您可以提供一个函数 sortHash 来负责对列表进行排序。
我想使用 cdkDropList
来为列表提供 DnD 排序。这在使用数组时很简单:
(cdkDropListDropped)="dropItem($event, doc.items, doc)
您只需将一个函数传递给 cdkDropListDropped
即可,该函数将负责在数组中移动项目。 Angular 提供了一个内置函数 moveItemInArray
这样做:
import { moveItemInArray } from '@angular/cdk/drag-drop';
...
async dropItem(event: CdkDragDrop<string[]>, list: any, doc: any) {
moveItemInArray(list, event.previousIndex, event.currentIndex);
}
这对数组按预期工作,但在我的例子中,我依赖于顺序由 属性“顺序”定义的地图,请参阅我的数据结构:
doc = {
meta: {
text: 'title',
...
},
items: {
SEC_000000: {
meta: {
text: 'Episode I - The Phantom Menace',
order: '0',
...
},
},
SEC_111111: {
meta: {
text: 'Episode II - Attack of the Clones',
order: '1',
...
},
},
SEC_222222: {
meta: {
text: 'Episode III - Revenge of the Sith',
order: '2',
...
},
},
},
};
因此我的 dropItem
函数有点不同,它
- 将我的地图 (
doc.items
) 转换为数组
- 然后使用内置的
moveItemInArray
函数有效地移动数组中的项目
- 然后更新所有项目的“顺序”属性,最后
- 将数组转换回映射
排序功能正常,但UI在DnD时没有更新。
这里有一个stackblitz带有简化的示例代码。我在这里错过了什么?
答案就在Angular 核心深处。
您正在使用 KeyValuePipe
。 Source here.
我们感兴趣的管道部分是这样的:
const differChanges: KeyValueChanges<K, V>|null = this.differ.diff(input as any);
const compareFnChanged = compareFn !== this.compareFn;
if (differChanges || compareFnChanged) {
this.keyValues.sort(compareFn);
this.compareFn = compareFn;
}
如果diff发现输入对象有差异,排序函数为运行。
您可以控制台日志 differChanges
并在视图初始化后看到它始终 returns null
。为什么?我们需要看看 the differ code:
// Add the record or a given key to the list of changes only when the value has actually changed
private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void {
if (!Object.is(newValue, record.currentValue)) {
record.previousValue = record.currentValue;
record.currentValue = newValue;
this._addToChanges(record);
}
}
不同之处在于使用 Object.is(newValue, record.currentValue)
来确定值是否已更改。这种情况下要diff的对象的值是一个对象本身,Object.is()
不计算深度相等。
所以你至少有两个选择:
- 按照您希望的方式编写您自己的键值管道。
- 使用不同的数据结构来保存您的电影信息
I've created a working StackBlitz 使用自定义 brute-force 求助策略 keyvalue
管道。
我有一个小清单,其中包含一些可以重新排序的项目 (see stackblitz)。在内部,该列表是作为地图实现的。方便的是,Angular 提供了管道键值,它允许一种简单的方法来遍历映射,如下所示:
*ngFor="let item of doc.items | keyvalue:sortHash"
您可以提供一个函数 sortHash 来负责对列表进行排序。
我想使用 cdkDropList
来为列表提供 DnD 排序。这在使用数组时很简单:
(cdkDropListDropped)="dropItem($event, doc.items, doc)
您只需将一个函数传递给 cdkDropListDropped
即可,该函数将负责在数组中移动项目。 Angular 提供了一个内置函数 moveItemInArray
这样做:
import { moveItemInArray } from '@angular/cdk/drag-drop';
...
async dropItem(event: CdkDragDrop<string[]>, list: any, doc: any) {
moveItemInArray(list, event.previousIndex, event.currentIndex);
}
这对数组按预期工作,但在我的例子中,我依赖于顺序由 属性“顺序”定义的地图,请参阅我的数据结构:
doc = {
meta: {
text: 'title',
...
},
items: {
SEC_000000: {
meta: {
text: 'Episode I - The Phantom Menace',
order: '0',
...
},
},
SEC_111111: {
meta: {
text: 'Episode II - Attack of the Clones',
order: '1',
...
},
},
SEC_222222: {
meta: {
text: 'Episode III - Revenge of the Sith',
order: '2',
...
},
},
},
};
因此我的 dropItem
函数有点不同,它
- 将我的地图 (
doc.items
) 转换为数组 - 然后使用内置的
moveItemInArray
函数有效地移动数组中的项目 - 然后更新所有项目的“顺序”属性,最后
- 将数组转换回映射
排序功能正常,但UI在DnD时没有更新。
这里有一个stackblitz带有简化的示例代码。我在这里错过了什么?
答案就在Angular 核心深处。
您正在使用 KeyValuePipe
。 Source here.
我们感兴趣的管道部分是这样的:
const differChanges: KeyValueChanges<K, V>|null = this.differ.diff(input as any);
const compareFnChanged = compareFn !== this.compareFn;
if (differChanges || compareFnChanged) {
this.keyValues.sort(compareFn);
this.compareFn = compareFn;
}
如果diff发现输入对象有差异,排序函数为运行。
您可以控制台日志 differChanges
并在视图初始化后看到它始终 returns null
。为什么?我们需要看看 the differ code:
// Add the record or a given key to the list of changes only when the value has actually changed
private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void {
if (!Object.is(newValue, record.currentValue)) {
record.previousValue = record.currentValue;
record.currentValue = newValue;
this._addToChanges(record);
}
}
不同之处在于使用 Object.is(newValue, record.currentValue)
来确定值是否已更改。这种情况下要diff的对象的值是一个对象本身,Object.is()
不计算深度相等。
所以你至少有两个选择:
- 按照您希望的方式编写您自己的键值管道。
- 使用不同的数据结构来保存您的电影信息
I've created a working StackBlitz 使用自定义 brute-force 求助策略 keyvalue
管道。