From 70db9dbc8688ec61df3e51da734f69739d5c6b78 Mon Sep 17 00:00:00 2001 From: cipchk Date: Tue, 26 Dec 2023 21:38:09 +0800 Subject: [PATCH] fix(abc:reuse-tab): fix keep open order - close https://github.com/ng-alain/ng-alain/issues/2464 --- .../abc/reuse-tab/reuse-tab.component.spec.ts | 32 ++++++++---- packages/abc/reuse-tab/reuse-tab.component.ts | 20 ++++--- packages/abc/reuse-tab/reuse-tab.service.ts | 52 +++++++++---------- src/dev/pages/page.component.ts | 17 ++++-- src/dev/router.ts | 1 + 5 files changed, 75 insertions(+), 47 deletions(-) diff --git a/packages/abc/reuse-tab/reuse-tab.component.spec.ts b/packages/abc/reuse-tab/reuse-tab.component.spec.ts index 22027021fc..efe6e30e33 100644 --- a/packages/abc/reuse-tab/reuse-tab.component.spec.ts +++ b/packages/abc/reuse-tab/reuse-tab.component.spec.ts @@ -139,6 +139,26 @@ describe('abc: reuse-tab', () => { .expectCount(3) .end(); })); + it('should be keep open order', fakeAsync(() => { + srv.max = 10; + page + .to('#b') + .expectUrl(0, '/a') + .expectUrl(1, '/b/1') + .to('#a') + .expectUrl(0, '/a') + .expectUrl(1, '/b/1') + .to('#c') + .expectUrl(0, '/a') + .expectUrl(1, '/c') + .expectUrl(2, '/b/1') + .to('#d') + .expectUrl(0, '/a') + .expectUrl(1, '/c') + .expectUrl(2, '/d') + .expectUrl(3, '/b/1') + .end(); + })); }); describe('#close', () => { @@ -149,7 +169,6 @@ describe('abc: reuse-tab', () => { })); it('should show next tab when closed a has next tab', fakeAsync(() => { srv.max = 10; - // debugger; page.to('#b'); page.to('#c'); page.go(1); @@ -210,14 +229,7 @@ describe('abc: reuse-tab', () => { fixture.detectChanges(); }); it('should working', fakeAsync(() => { - page - .to('#b') - .expectCount(MAX) - .to('#c') - .expectCount(MAX + 1) // +1 => current page - .to('#d') - .expectCount(MAX + 1) - .end(); + page.to('#b').expectCount(MAX).to('#c').expectCount(MAX).to('#d').expectCount(MAX).end(); })); }); describe('#allowClose', () => { @@ -798,7 +810,7 @@ describe('abc: reuse-tab', () => { this.cd(); return this; } - openContextMenu(pos: number, eventArgs?: NzSafeAny): this { + openContextMenu(pos: number, eventArgs?: MouseEventInit): this { const ls = document.querySelectorAll('.reuse-tab__name'); if (pos > ls.length) { expect(false).withContext(`the pos muse be 0-${ls.length}`).toBe(true); diff --git a/packages/abc/reuse-tab/reuse-tab.component.ts b/packages/abc/reuse-tab/reuse-tab.component.ts index dfa2aa45b9..2cd308fd69 100644 --- a/packages/abc/reuse-tab/reuse-tab.component.ts +++ b/packages/abc/reuse-tab/reuse-tab.component.ts @@ -160,18 +160,17 @@ export class ReuseTabComponent implements OnInit, OnChanges { ({ url: item.url, title: this.genTit(item.title), - closable: this.allowClose && item.closable && this.srv.count > 0, + closable: this.allowClose && this.srv.count > 0 && this.srv.getClosable(item.url, item._snapshot), position: item.position, index, active: false, last: false }) as ReuseItem ); - // debugger; const url = this.curUrl; let addCurrent = ls.findIndex(w => w.url === url) === -1; - if (notify && notify.active === 'close' && notify.url === url) { + if (ls.length && notify && notify.active === 'close' && notify.url === url) { addCurrent = false; let toPos = 0; const curItem = this.list.find(w => w.url === url)!; @@ -186,7 +185,16 @@ export class ReuseTabComponent implements OnInit, OnChanges { } if (addCurrent) { - ls.splice(this.pos + 1, 0, this.genCurItem()); + const addPos = this.pos + 1; + const curItem = this.genCurItem(); + ls.splice(addPos, 0, curItem); + // Attach to cache + const snapshotTrue = this.srv.getTruthRoute(this.route.snapshot); + this.srv.items.splice(addPos, 0, { + title: this.srv.getTitle(url, snapshotTrue), + url, + closable: curItem.closable + }); } ls.forEach((item, index) => (item.index = index)); @@ -256,9 +264,7 @@ export class ReuseTabComponent implements OnInit, OnChanges { if (!res) return; this.item = item; this.change.emit(item); - if (cb) { - cb(); - } + cb?.(); }); } diff --git a/packages/abc/reuse-tab/reuse-tab.service.ts b/packages/abc/reuse-tab/reuse-tab.service.ts index cc63f57253..ee369b1998 100644 --- a/packages/abc/reuse-tab/reuse-tab.service.ts +++ b/packages/abc/reuse-tab/reuse-tab.service.ts @@ -219,12 +219,16 @@ export class ReuseTabService implements OnDestroy { */ replace(newUrl: string): void { const url = this.curUrl; - if (this.exists(url)) { - this.close(url, true); - } else { - this.removeUrlBuffer = url; - } - this.injector.get(Router).navigateByUrl(newUrl); + this.injector + .get(Router) + .navigateByUrl(newUrl) + .then(() => { + if (this.exists(url)) { + this.close(url, true); + } else { + this.removeUrlBuffer = url; + } + }); } /** * 获取标题,顺序如下: @@ -443,7 +447,9 @@ export class ReuseTabService implements OnDestroy { store(_snapshot: ActivatedRouteSnapshot, _handle: NzSafeAny): void { const url = this.getUrl(_snapshot); const idx = this.index(url); - const isAdd = idx === -1; + if (idx === -1) return; + + const list = this.cached.list; const item: ReuseTabCached = { title: this.getTitle(url, _snapshot), @@ -453,33 +459,22 @@ export class ReuseTabService implements OnDestroy { _snapshot, _handle }; - if (isAdd) { - if (this.count >= this._max) { - // Get the oldest closable location - const closeIdx = this.cached.list.findIndex(w => w.closable!); - if (closeIdx !== -1) this.remove(closeIdx, false); - } - this.cached.list.push(item); - } else { - // Current handler is null when activate routes - // For better reliability, we need to wait for the component to be attached before call _onReuseInit - const cahcedComponentRef = this.cached.list[idx]._handle?.componentRef; - if (_handle == null && cahcedComponentRef != null) { - timer(100).subscribe(() => this.runHook('_onReuseInit', cahcedComponentRef)); - } - this.cached.list[idx] = item; + // Current handler is null when activate routes + // For better reliability, we need to wait for the component to be attached before call _onReuseInit + const cahcedComponentRef = list[idx]._handle?.componentRef; + if (_handle == null && cahcedComponentRef != null) { + timer(100).subscribe(() => this.runHook('_onReuseInit', cahcedComponentRef)); } + list[idx] = item; this.removeUrlBuffer = null; - this.di('#store', isAdd ? '[new]' : '[override]', url); + this.di('#store', '[override]', url); if (_handle && _handle.componentRef) { this.runHook('_onReuseDestroy', _handle.componentRef); } - if (!isAdd) { - this._cachedChange.next({ active: 'override', item, list: this.cached.list }); - } + this._cachedChange.next({ active: 'override', item, list }); } /** @@ -492,6 +487,11 @@ export class ReuseTabService implements OnDestroy { const ret = !!(data && data._handle); this.di('#shouldAttach', ret, url); if (!ret) { + if (this.count >= this._max) { + // Get the oldest closable location + const closeIdx = this.items.findIndex(w => w.url !== url && w.closable!); + if (closeIdx !== -1) this.remove(closeIdx, false); + } this._cachedChange.next({ active: 'add', url, list: this.cached.list }); } return ret; diff --git a/src/dev/pages/page.component.ts b/src/dev/pages/page.component.ts index d3f644ad21..32a9e22413 100644 --- a/src/dev/pages/page.component.ts +++ b/src/dev/pages/page.component.ts @@ -1,10 +1,12 @@ import { JsonPipe } from '@angular/common'; -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { format } from 'date-fns'; import { PageHeaderModule } from '@delon/abc/page-header'; +import { ReuseTabService } from '@delon/abc/reuse-tab'; +import { NzButtonModule } from 'ng-zorro-antd/button'; @Component({ selector: 'dev-page', @@ -13,17 +15,20 @@ import { PageHeaderModule } from '@delon/abc/page-header';

first: {{ first | json }},now: {{ now | json }}

id: {{ id | json }}

page: {{ route.url | json }} +
+ +
`, standalone: true, - imports: [PageHeaderModule, JsonPipe] + imports: [PageHeaderModule, JsonPipe, NzButtonModule] }) export class DevPageComponent implements OnInit { + readonly route = inject(ActivatedRoute); + readonly srv = inject(ReuseTabService); first = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); now = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); id = 0; - constructor(public route: ActivatedRoute) {} - ngOnInit(): void { this.route.params.subscribe(params => (this.id = +params.id)); } @@ -32,4 +37,8 @@ export class DevPageComponent implements OnInit { this.now = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); console.log('by _onReuseInit', this.id); } + + replace(url: string): void { + this.srv.replace(url); + } } diff --git a/src/dev/router.ts b/src/dev/router.ts index dbf94ec4d8..e802cf446c 100644 --- a/src/dev/router.ts +++ b/src/dev/router.ts @@ -24,6 +24,7 @@ export const routes: Routes = [ { path: 'l7', component: DevPageComponent }, { path: 'l8', component: DevPageComponent }, { path: 'login', component: DevPageComponent }, + { path: 'view/1', component: DevPageComponent, data: { reuseClosable: false } }, { path: 'view/:id', component: DevPageComponent }, { path: 'lazy',