Skip to content

Commit

Permalink
fix(abc:reuse-tab): fix keep open order (#1743)
Browse files Browse the repository at this point in the history
  • Loading branch information
cipchk authored Jan 1, 2024
1 parent 69bb0bb commit e17f5e7
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 68 deletions.
32 changes: 22 additions & 10 deletions packages/abc/reuse-tab/reuse-tab.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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);
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 6 additions & 6 deletions packages/abc/reuse-tab/reuse-tab.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,13 @@ 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;
Expand All @@ -186,7 +185,10 @@ export class ReuseTabComponent implements OnInit, OnChanges {
}

if (addCurrent) {
ls.splice(this.pos + 1, 0, this.genCurItem());
const addPos = this.pos + 1;
ls.splice(addPos, 0, this.genCurItem());
// Attach to cache
this.srv.saveCache(this.route.snapshot, null, addPos);
}

ls.forEach((item, index) => (item.index = index));
Expand Down Expand Up @@ -256,9 +258,7 @@ export class ReuseTabComponent implements OnInit, OnChanges {
if (!res) return;
this.item = item;
this.change.emit(item);
if (cb) {
cb();
}
cb?.();
});
}

Expand Down
46 changes: 25 additions & 21 deletions packages/abc/reuse-tab/reuse-tab.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class MockMenuService {
}
}
class MockRouter {
navigateByUrl = jasmine.createSpy();
navigateByUrl = jasmine.createSpy().and.returnValue(Promise.resolve(true));
get events(): NzSafeAny {
return {
subscribe: () => {
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('abc: reuse-tab(service)', () => {
Array(count)
.fill({})
.forEach((_item: NzSafeAny, index: number) => {
srv.store(getSnapshot(index + 1, urlTpl), { a: 1 });
srv.saveCache(getSnapshot(index + 1, urlTpl), { a: 1 });
});
}

Expand All @@ -95,21 +95,21 @@ describe('abc: reuse-tab(service)', () => {
});
it('should be close oldest page', () => {
srv.max = 2;
srv.store(getSnapshot(1), {});
srv.store(getSnapshot(2), {});
srv.store(getSnapshot(3), {});
srv.saveCache(getSnapshot(1), {});
srv.saveCache(getSnapshot(2), {});
srv.saveCache(getSnapshot(3), {});
expect(srv.count).toBe(2);
srv.store(getSnapshot(4), {});
srv.saveCache(getSnapshot(4), {});
expect(srv.count).toBe(2);
});
it('should be ingore close when all is not closable', () => {
srv.max = 2;
srv.store(getSnapshot(1), {});
srv.store(getSnapshot(2), {});
srv.saveCache(getSnapshot(1), {});
srv.saveCache(getSnapshot(2), {});
srv.items.forEach(i => (i.closable = false));
srv.store(getSnapshot(3), {});
srv.saveCache(getSnapshot(3), {});
expect(srv.count).toBe(3);
srv.store(getSnapshot(4), {});
srv.saveCache(getSnapshot(4), {});
expect(srv.count).toBe(3);
});
});
Expand Down Expand Up @@ -313,7 +313,7 @@ describe('abc: reuse-tab(service)', () => {
destroy: jasmine.createSpy('destroy')
}
};
srv.store(getSnapshot(3), instance);
srv.saveCache(getSnapshot(3), instance);
srv.close('/a/3');
expect(instance.componentRef.destroy).toHaveBeenCalled();
});
Expand Down Expand Up @@ -377,17 +377,19 @@ describe('abc: reuse-tab(service)', () => {
srv.refresh(true);
});
describe('#replace', () => {
it('should be navigate to new url', () => {
it('should be navigate to new url', fakeAsync(() => {
expect(router.navigateByUrl).not.toHaveBeenCalled();
srv.replace('/a/1');
tick();
expect(router.navigateByUrl).toHaveBeenCalled();
});
it('should be closed current router after navigate to new url', () => {
}));
it('should be closed current router after navigate to new url', fakeAsync(() => {
genCached(1, '');
expect(router.navigateByUrl).not.toHaveBeenCalled();
srv.replace('/b');
tick();
expect(router.navigateByUrl).toHaveBeenCalled();
});
}));
});
describe('#keepingScroll', () => {
it('should get keepingScroll from service', () => {
Expand Down Expand Up @@ -451,7 +453,7 @@ describe('abc: reuse-tab(service)', () => {
it(`can't hit when remove current page`, () => {
const snapshot = getSnapshot(1);
expect(srv.shouldDetach(snapshot)).toBe(true);
srv.store(snapshot, {});
srv.saveCache(snapshot, {});
srv.close(srv.getUrl(snapshot));
expect(srv.shouldDetach(snapshot)).toBe(false);
});
Expand All @@ -465,18 +467,18 @@ describe('abc: reuse-tab(service)', () => {
});
it(`should be store a new route`, () => {
expect(srv.count).toBe(2);
srv.store(getSnapshot(3), {});
srv.saveCache(getSnapshot(3));
expect(srv.count).toBe(3);
});
it(`should be store a exists route`, () => {
expect(srv.count).toBe(2);
srv.store(getSnapshot(1), {});
srv.saveCache(getSnapshot(1));
expect(srv.count).toBe(2);
});
it(`should be store a route when out of cache count`, () => {
srv.max = 2;
expect(srv.count).toBe(2);
srv.store(getSnapshot(3), { componentRef: {} });
srv.saveCache(getSnapshot(3), { componentRef: {} });
expect(srv.count).toBe(2);
});
it(`should be run _onReuseDestroy event hook`, () => {
Expand All @@ -487,7 +489,9 @@ describe('abc: reuse-tab(service)', () => {
}
}
};
srv.store(getSnapshot(3), handle);
const snapshot = getSnapshot(3);
srv.saveCache(snapshot, handle);
srv.store(snapshot, handle);
expect(handle.componentRef.instance._onReuseDestroy).toHaveBeenCalled();
});
});
Expand Down Expand Up @@ -537,7 +541,7 @@ describe('abc: reuse-tab(service)', () => {
};
const snapshot = getSnapshot(3);
// handle
srv.store(snapshot, handle);
srv.saveCache(snapshot, handle);
// mock activate router
srv.store(snapshot, null);
tick(101);
Expand Down
84 changes: 57 additions & 27 deletions packages/abc/reuse-tab/reuse-tab.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
Router,
ROUTER_CONFIGURATION
} from '@angular/router';
import { BehaviorSubject, Observable, timer, Unsubscribable } from 'rxjs';
import { BehaviorSubject, Observable, take, timer, Unsubscribable } from 'rxjs';

import { Menu, MenuService } from '@delon/theme';
import { ScrollService } from '@delon/util/browser';
Expand Down Expand Up @@ -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>(Router).navigateByUrl(newUrl);
this.injector
.get(Router)
.navigateByUrl(newUrl)
.then(() => {
if (this.exists(url)) {
this.close(url, true);
} else {
this.removeUrlBuffer = url;
}
});
}
/**
* 获取标题,顺序如下:
Expand Down Expand Up @@ -437,13 +441,48 @@ export class ReuseTabService implements OnDestroy {
return this.can(route);
}

saveCache(snapshot: ActivatedRouteSnapshot, _handle?: NzSafeAny, pos?: number): void {
const snapshotTrue = this.getTruthRoute(snapshot);
const url = this.getUrl(snapshot);
const idx = this.index(url);
const item: ReuseTabCached = {
title: this.getTitle(url, snapshotTrue),
url,
closable: this.getClosable(url, snapshot),
_snapshot: snapshot,
_handle
};
if (idx < 0) {
this.items.splice(pos ?? this.items.length, 0, item);
if (this.count > this._max) {
// Get the oldest closable location
const closeIdx = this.items.findIndex(w => w.url !== url && w.closable!);
if (closeIdx !== -1) {
const closeItem = this.items[closeIdx];
this.remove(closeIdx, false);
timer(1)
.pipe(take(1))
.subscribe(() => this._cachedChange.next({ active: 'close', url: closeItem.url, list: this.cached.list }));
}
}
} else {
this.items[idx] = item;
}
}

/**
* 存储
*/
store(_snapshot: ActivatedRouteSnapshot, _handle: NzSafeAny): void {
const url = this.getUrl(_snapshot);
const idx = this.index(url);
const isAdd = idx === -1;
if (idx === -1) return;

if (_handle != null) {
this.saveCache(_snapshot, _handle);
}

const list = this.cached.list;

const item: ReuseTabCached = {
title: this.getTitle(url, _snapshot),
Expand All @@ -453,33 +492,24 @@ 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)
.pipe(take(1))
.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 });
}

/**
Expand Down
Loading

0 comments on commit e17f5e7

Please sign in to comment.