Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(abc:reuse-tab): fix keep open order #1743

Merged
merged 2 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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