From 6ebe0850ec5ebd15aaf6296feabaa0a595d126a6 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Fri, 11 Sep 2020 18:04:23 +0300 Subject: [PATCH 1/3] fix: remove node and its children in TreeAdapter --- .../components/tree/src/lib/utils/nz-tree-adapter.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/npm/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts b/npm/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts index 5d042602e2..6c66315e69 100644 --- a/npm/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts +++ b/npm/ng-packs/packages/components/tree/src/lib/utils/nz-tree-adapter.ts @@ -41,17 +41,15 @@ export class TreeAdapter { return this.tree; } - handleDrop(node: TreeNode) { - const { key, parentNode } = node; + handleDrop({ key, parentNode }: TreeNode) { const index = this.list.findIndex(({ id }) => id === key); this.list[index].parentId = parentNode ? parentNode.key : null; this.tree = createTreeFromList(this.list); } - handleRemove(node: TreeNode) { - const { key, parentNode } = node; - this.list = this.list.filter(({ id, parentId }) => id !== key && parentId !== key); - this.tree = createTreeFromList(this.list); + handleRemove({ key }: TreeNode) { + this.tree = createTreeFromList(this.list.filter(item => item.id !== key)); + this.list = createListFromTree(this.tree); } } @@ -66,6 +64,7 @@ function createTreeFromList(list: T[]): TreeNode[] { const node = map.get(row.id); if (parentId) { const parent = map.get(parentId); + if (!parent) return; parent.children.push(node); parent.isLeaf = false; } else { From cc7b272f814bc01c1f3ab309e41b496acf006444 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Fri, 11 Sep 2020 19:18:31 +0300 Subject: [PATCH 2/3] refactor: improve tree util types --- npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts index d4c0ba0ab3..ba6e3a5b90 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts @@ -1,7 +1,7 @@ export class BaseTreeNode { children: TreeNode[] = []; isLeaf = true; - parent: TreeNode; + parent?: TreeNode; constructor(props: T) { Object.assign(this, props); @@ -14,7 +14,7 @@ export class BaseTreeNode { export function createTreeFromList( 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( 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( export function createMapFromList( list: T[], - keySelector: (item: T) => number | string | symbol, + keySelector: (item: T) => NodeKey, valueMapper: (item: T) => R, ) { type Key = ReturnType; @@ -60,6 +62,8 @@ export type TreeNode = { parent?: TreeNode; }; +type NodeKey = number | string | symbol | undefined | null; + type NodeValue any> = F extends undefined ? TreeNode : ReturnType; From df7bb4a06d67373fe66d62d494c57ffc6ff23aa1 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Fri, 11 Sep 2020 19:19:00 +0300 Subject: [PATCH 3/3] fix: remove node and its children in AbstractTreeService --- .../core/src/lib/services/routes.service.ts | 16 +++++++++---- .../core/src/lib/tests/routes.service.spec.ts | 24 ++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index 16fc4c238b..c19628760c 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -51,9 +51,16 @@ export abstract class AbstractTreeService { } private filterWith(setOrMap: Set | Map): 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): Set { + 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 { const set = new Set(); 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); diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts index aa5e51cb17..6b7ae3f50a 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts +++ b/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');