Browse Source

Merge pull request #5376 from abpframework/fix/5374

Fixed TreeAdapter to remove nested nodes properly
pull/5385/head
Bunyamin Coskuner 6 years ago
committed by GitHub
parent
commit
247a2c7f51
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      npm/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts
  2. 16
      npm/ng-packs/packages/core/src/lib/services/routes.service.ts
  3. 24
      npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts
  4. 10
      npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts

11
npm/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts

@ -41,17 +41,15 @@ export class TreeAdapter<T extends BaseNode = BaseNode> {
return this.tree;
}
handleDrop(node: TreeNode<T>) {
const { key, parentNode } = node;
handleDrop({ key, parentNode }: TreeNode<T>) {
const index = this.list.findIndex(({ id }) => id === key);
this.list[index].parentId = parentNode ? parentNode.key : null;
this.tree = createTreeFromList(this.list);
}
handleRemove(node: TreeNode<T>) {
const { key, parentNode } = node;
this.list = this.list.filter(({ id, parentId }) => id !== key && parentId !== key);
this.tree = createTreeFromList(this.list);
handleRemove({ key }: TreeNode<T>) {
this.tree = createTreeFromList(this.list.filter(item => item.id !== key));
this.list = createListFromTree(this.tree);
}
}
@ -66,6 +64,7 @@ function createTreeFromList<T extends BaseNode>(list: T[]): TreeNode<T>[] {
const node = map.get(row.id);
if (parentId) {
const parent = map.get(parentId);
if (!parent) return;
parent.children.push(node);
parent.isLeaf = false;
} else {

16
npm/ng-packs/packages/core/src/lib/services/routes.service.ts

@ -51,9 +51,16 @@ export abstract class AbstractTreeService<T extends object> {
}
private filterWith(setOrMap: Set<string> | Map<string, T>): T[] {
return this._flat$.value.filter(
item => !setOrMap.has(item[this.id]) && !setOrMap.has(item[this.parentId]),
);
return this._flat$.value.filter(item => !setOrMap.has(item[this.id]));
}
private findItemsToRemove(set: Set<string>): Set<string> {
return this._flat$.value.reduce((acc, item) => {
if (!acc.has(item[this.parentId])) return acc;
const childSet = new Set([item[this.id]]);
const children = this.findItemsToRemove(childSet);
return new Set([...acc, ...children]);
}, set);
}
private publish(flatItems: T[], visibleItems: T[]): T[] {
@ -104,7 +111,8 @@ export abstract class AbstractTreeService<T extends object> {
const set = new Set<string>();
identifiers.forEach(id => set.add(id));
const flatItems = this.filterWith(set);
const setToRemove = this.findItemsToRemove(set);
const flatItems = this.filterWith(setToRemove);
const visibleItems = flatItems.filter(item => !this.hide(item));
return this.publish(flatItems, visibleItems);

24
npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts

@ -10,6 +10,7 @@ describe('Routes Service', () => {
{ path: '/foo', name: 'foo' },
{ path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 },
{ path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 },
{ path: '/foo/bar/baz/qux', name: 'qux', parentName: 'baz', order: 1 },
{ path: '/foo/x', name: 'x', parentName: 'foo', order: 1 },
];
@ -32,11 +33,12 @@ describe('Routes Service', () => {
const tree = await service.tree$.pipe(take(1)).toPromise();
const visible = await service.visible$.pipe(take(1)).toPromise();
expect(flat.length).toBe(4);
expect(flat[3].name).toBe('foo');
expect(flat.length).toBe(5);
expect(flat[0].name).toBe('baz');
expect(flat[1].name).toBe('x');
expect(flat[2].name).toBe('bar');
expect(flat[1].name).toBe('qux');
expect(flat[2].name).toBe('x');
expect(flat[3].name).toBe('bar');
expect(flat[4].name).toBe('foo');
expect(tree.length).toBe(1);
expect(tree[0].name).toBe('foo');
@ -44,6 +46,7 @@ describe('Routes Service', () => {
expect(tree[0].children[0].name).toBe('x');
expect(tree[0].children[1].name).toBe('bar');
expect(tree[0].children[1].children[0].name).toBe('baz');
expect(tree[0].children[1].children[0].children[0].name).toBe('qux');
expect(visible.length).toBe(1);
expect(visible[0].name).toBe('foo');
@ -74,7 +77,8 @@ describe('Routes Service', () => {
expect(service.hasChildren('foo')).toBe(true);
expect(service.hasChildren('bar')).toBe(true);
expect(service.hasChildren('baz')).toBe(false);
expect(service.hasChildren('baz')).toBe(true);
expect(service.hasChildren('qux')).toBe(false);
});
});
@ -123,11 +127,12 @@ describe('Routes Service', () => {
const tree = service.tree;
const visible = service.visible;
expect(flat.length).toBe(4);
expect(flat[3].name).toBe('foo');
expect(flat.length).toBe(5);
expect(flat[0].name).toBe('baz');
expect(flat[1].name).toBe('x');
expect(flat[2].name).toBe('bar');
expect(flat[1].name).toBe('qux');
expect(flat[2].name).toBe('x');
expect(flat[3].name).toBe('bar');
expect(flat[4].name).toBe('foo');
expect(tree.length).toBe(1);
expect(tree[0].name).toBe('foo');
@ -135,6 +140,7 @@ describe('Routes Service', () => {
expect(tree[0].children[0].name).toBe('x');
expect(tree[0].children[1].name).toBe('bar');
expect(tree[0].children[1].children[0].name).toBe('baz');
expect(tree[0].children[1].children[0].children[0].name).toBe('qux');
expect(visible.length).toBe(1);
expect(visible[0].name).toBe('foo');

10
npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts

@ -1,7 +1,7 @@
export class BaseTreeNode<T extends object> {
children: TreeNode<T>[] = [];
isLeaf = true;
parent: TreeNode<T>;
parent?: TreeNode<T>;
constructor(props: T) {
Object.assign(this, props);
@ -14,7 +14,7 @@ export class BaseTreeNode<T extends object> {
export function createTreeFromList<T extends object, R extends unknown>(
list: T[],
keySelector: (item: T) => number | string | symbol,
keySelector: (item: T) => NodeKey,
parentKeySelector: typeof keySelector,
valueMapper: (item: T) => R,
) {
@ -26,6 +26,8 @@ export function createTreeFromList<T extends object, R extends unknown>(
const parentId = parentKeySelector(row);
const node = map.get(id);
if (!node) return;
if (parentId) {
const parent = map.get(parentId);
if (!parent) return;
@ -42,7 +44,7 @@ export function createTreeFromList<T extends object, R extends unknown>(
export function createMapFromList<T extends object, R extends unknown>(
list: T[],
keySelector: (item: T) => number | string | symbol,
keySelector: (item: T) => NodeKey,
valueMapper: (item: T) => R,
) {
type Key = ReturnType<typeof keySelector>;
@ -60,6 +62,8 @@ export type TreeNode<T extends object> = {
parent?: TreeNode<T>;
};
type NodeKey = number | string | symbol | undefined | null;
type NodeValue<T extends object, F extends (...args: any) => any> = F extends undefined
? TreeNode<T>
: ReturnType<F>;

Loading…
Cancel
Save