mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 10:01:59 +08:00
fix(router-outlet): navigating to same route with different params now activates component (#24760)
resolves #24653
This commit is contained in:
@ -1045,7 +1045,7 @@ ion-router,none
|
||||
ion-router,prop,root,string,'/',false,false
|
||||
ion-router,prop,useHash,boolean,true,false,false
|
||||
ion-router,method,back,back() => Promise<void>
|
||||
ion-router,method,push,push(url: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>
|
||||
ion-router,method,push,push(path: string, direction?: RouterDirection, animation?: AnimationBuilder | undefined) => Promise<boolean>
|
||||
ion-router,event,ionRouteDidChange,RouterEventDetail,true
|
||||
ion-router,event,ionRouteWillChange,RouterEventDetail,true
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AnimationBuilder, ComponentProps, FrameworkDelegate, NavComponentWithProps } from '../../interface';
|
||||
import { attachComponent } from '../../utils/framework-delegate';
|
||||
import { assert } from '../../utils/helpers';
|
||||
import { assert, shallowEqualStringMap } from '../../utils/helpers';
|
||||
|
||||
export const VIEW_STATE_NEW = 1;
|
||||
export const VIEW_STATE_ATTACHED = 2;
|
||||
@ -54,29 +54,8 @@ export const matches = (view: ViewController | undefined, id: string, params: Co
|
||||
if (view.component !== id) {
|
||||
return false;
|
||||
}
|
||||
const currentParams = view.params;
|
||||
if (currentParams === params) {
|
||||
return true;
|
||||
}
|
||||
if (!currentParams && !params) {
|
||||
return true;
|
||||
}
|
||||
if (!currentParams || !params) {
|
||||
return false;
|
||||
}
|
||||
const keysA = Object.keys(currentParams);
|
||||
const keysB = Object.keys(params);
|
||||
if (keysA.length !== keysB.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test for A's keys different from B.
|
||||
for (const key of keysA) {
|
||||
if (currentParams[key] !== params[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return shallowEqualStringMap(view.params, params);
|
||||
};
|
||||
|
||||
export const convertToView = (page: any, params: ComponentProps | undefined): ViewController | null => {
|
||||
|
@ -5,6 +5,7 @@ import { getIonMode } from '../../global/ionic-global';
|
||||
import { Animation, AnimationBuilder, ComponentProps, ComponentRef, FrameworkDelegate, Gesture, NavOutlet, RouteID, RouteWrite, RouterDirection, RouterOutletOptions, SwipeGestureHandler } from '../../interface';
|
||||
import { getTimeGivenProgression } from '../../utils/animation/cubic-bezier';
|
||||
import { attachComponent, detachComponent } from '../../utils/framework-delegate';
|
||||
import { shallowEqualStringMap } from '../../utils/helpers';
|
||||
import { transition } from '../../utils/transition';
|
||||
|
||||
@Component({
|
||||
@ -159,7 +160,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
|
||||
}
|
||||
|
||||
private async setRoot(component: ComponentRef, params?: ComponentProps, opts?: RouterOutletOptions): Promise<boolean> {
|
||||
if (this.activeComponent === component) {
|
||||
if (this.activeComponent === component && shallowEqualStringMap(params, this.activeParams)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -26,4 +26,18 @@ test('getRouteId() should return the route parameters', async () => {
|
||||
|
||||
expect(routeId.id).toEqual('PAGE-THREE');
|
||||
expect(routeId.params).toEqual({ param: 'route' });
|
||||
});
|
||||
|
||||
test('it should be possible to activate the same component provided parameters are different', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/router-outlet/test/basic?ionic:_testing=true'
|
||||
});
|
||||
|
||||
await page.$eval('ion-item[href="#/page-4.1/foo"] ion-label', (el: any) => el.click());
|
||||
await page.waitForChanges();
|
||||
expect(await page.$eval('ion-router-outlet', (el) => el.textContent)).toMatch(/text = foo/);
|
||||
|
||||
await page.$eval('ion-item[href="#/page-4.2/bar"] ion-label', (el: any) => el.click());
|
||||
await page.waitForChanges();
|
||||
expect(await page.$eval('ion-router-outlet', (el) => el.textContent)).toMatch(/text = bar/);
|
||||
});
|
@ -58,9 +58,24 @@
|
||||
`;
|
||||
}
|
||||
}
|
||||
class PageFour extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Page Four</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<h1>text = ${this.text}</h1>
|
||||
</ion-content>
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define('page-one', PageOne);
|
||||
customElements.define('page-two', PageTwo);
|
||||
customElements.define('page-three', PageThree);
|
||||
customElements.define('page-four', PageFour);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
@ -70,6 +85,8 @@
|
||||
<ion-route url="/" component="page-one"> </ion-route>
|
||||
<ion-route url="/two/:param" component="page-two"> </ion-route>
|
||||
<ion-route url="/page-3" component="page-three"> </ion-route>
|
||||
<ion-route url="/page-4.1/:text" component="page-four"> </ion-route>
|
||||
<ion-route url="/page-4.2/:text" component="page-four"> </ion-route>
|
||||
</ion-router>
|
||||
|
||||
<ion-split-pane content-id="main">
|
||||
@ -89,6 +106,12 @@
|
||||
<ion-item href="#/page-3">
|
||||
<ion-label>Page 3</ion-label>
|
||||
</ion-item>
|
||||
<ion-item href="#/page-4.1/foo">
|
||||
<ion-label>Page 4 (foo)</ion-label>
|
||||
</ion-item>
|
||||
<ion-item href="#/page-4.2/bar">
|
||||
<ion-label>Page 4 (bar)</ion-label>
|
||||
</ion-item>
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
<ion-router-outlet id="main"></ion-router-outlet>
|
||||
|
@ -348,3 +348,36 @@ export const debounce = (func: (...args: any[]) => void, wait = 0) => {
|
||||
timer = setTimeout(func, wait, ...args);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the two string maps are shallow equal.
|
||||
*
|
||||
* undefined is treated as an empty map.
|
||||
*
|
||||
* @returns whether the keys are the same and the values are shallow equal.
|
||||
*/
|
||||
export const shallowEqualStringMap = (map1: {[k: string]: any} | undefined, map2: {[k: string]: any} | undefined): boolean => {
|
||||
map1 ??= {};
|
||||
map2 ??= {};
|
||||
|
||||
if (map1 === map2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const keys1 = Object.keys(map1);
|
||||
|
||||
if (keys1.length !== Object.keys(map2).length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const k1 of keys1) {
|
||||
if (!(k1 in map2)) {
|
||||
return false;
|
||||
}
|
||||
if (map1[k1] !== map2[k1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
Reference in New Issue
Block a user