Skip to content
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [8.8.8](https://github.com/ionic-team/ionic-framework/compare/v8.8.7...v8.8.8) (2026-05-20)


### Bug Fixes

* **react:** bind events properly for overlays rendered within a nav ([#31159](https://github.com/ionic-team/ionic-framework/issues/31159)) ([fa4593d](https://github.com/ionic-team/ionic-framework/commit/fa4593d8a4d61a583dbf6fa551cd846fe258624f)), closes [#27843](https://github.com/ionic-team/ionic-framework/issues/27843)
* **tabs:** preserve query params and fragment from tab button href ([#31154](https://github.com/ionic-team/ionic-framework/issues/31154)) ([0182bba](https://github.com/ionic-team/ionic-framework/commit/0182bba06d6171dd2faf80556fd2131abf03fa93)), closes [#25470](https://github.com/ionic-team/ionic-framework/issues/25470)
* **vue-router:** prevent out-of-bounds index when popping across tabs ([#31148](https://github.com/ionic-team/ionic-framework/issues/31148)) ([c88c0de](https://github.com/ionic-team/ionic-framework/commit/c88c0de3ade92469fa1f37e1b8220911adf113e0)), closes [#29413](https://github.com/ionic-team/ionic-framework/issues/29413)





## [8.8.7](https://github.com/ionic-team/ionic-framework/compare/v8.8.6...v8.8.7) (2026-05-13)


Expand Down
8 changes: 8 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [8.8.8](https://github.com/ionic-team/ionic-framework/compare/v8.8.7...v8.8.8) (2026-05-20)

**Note:** Version bump only for package @ionic/core





## [8.8.7](https://github.com/ionic-team/ionic-framework/compare/v8.8.6...v8.8.7) (2026-05-13)


Expand Down
4 changes: 2 additions & 2 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "8.8.7",
"version": "8.8.8",
"description": "Base components for Ionic",
"engines": {
"node": ">= 16"
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"core",
"packages/*"
],
"version": "8.8.7"
"version": "8.8.8"
}
8 changes: 8 additions & 0 deletions packages/angular-server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [8.8.8](https://github.com/ionic-team/ionic-framework/compare/v8.8.7...v8.8.8) (2026-05-20)

**Note:** Version bump only for package @ionic/angular-server





## [8.8.7](https://github.com/ionic-team/ionic-framework/compare/v8.8.6...v8.8.7) (2026-05-13)

**Note:** Version bump only for package @ionic/angular-server
Expand Down
18 changes: 9 additions & 9 deletions packages/angular-server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/angular-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ionic/angular-server",
"version": "8.8.7",
"version": "8.8.8",
"description": "Angular SSR Module for Ionic",
"keywords": [
"ionic",
Expand Down Expand Up @@ -62,6 +62,6 @@
},
"prettier": "@ionic/prettier-config",
"dependencies": {
"@ionic/core": "^8.8.7"
"@ionic/core": "^8.8.8"
}
}
11 changes: 11 additions & 0 deletions packages/angular/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [8.8.8](https://github.com/ionic-team/ionic-framework/compare/v8.8.7...v8.8.8) (2026-05-20)


### Bug Fixes

* **tabs:** preserve query params and fragment from tab button href ([#31154](https://github.com/ionic-team/ionic-framework/issues/31154)) ([0182bba](https://github.com/ionic-team/ionic-framework/commit/0182bba06d6171dd2faf80556fd2131abf03fa93)), closes [#25470](https://github.com/ionic-team/ionic-framework/issues/25470)





## [8.8.7](https://github.com/ionic-team/ionic-framework/compare/v8.8.6...v8.8.7) (2026-05-13)

**Note:** Version bump only for package @ionic/angular
Expand Down
72 changes: 63 additions & 9 deletions packages/angular/common/src/directives/navigation/tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,54 @@ import {
AfterViewInit,
QueryList,
} from '@angular/core';
import type { Params } from '@angular/router';

import { NavController } from '../../providers/nav-controller';

import { StackDidChangeEvent, StackWillChangeEvent } from './stack-utils';

/**
* Extracts `queryParams` and `fragment` from a tab button's href for use
* as Angular `NavigationExtras`. Returns `undefined` when neither is present.
*/
const parseHrefExtras = (href: string | undefined): { queryParams?: Params; fragment?: string } | undefined => {
if (!href) {
return undefined;
}

const hashIndex = href.indexOf('#');
// Treat a bare `#` (no fragment text) as no fragment.
const fragment = hashIndex >= 0 && hashIndex < href.length - 1 ? href.slice(hashIndex + 1) : undefined;
const beforeHash = hashIndex >= 0 ? href.slice(0, hashIndex) : href;

const queryIndex = beforeHash.indexOf('?');
const search = queryIndex >= 0 ? beforeHash.slice(queryIndex + 1) : '';

let queryParams: Params | undefined;
if (search) {
const params = new URLSearchParams(search);
queryParams = {};
for (const key of new Set(params.keys())) {
const all = params.getAll(key);
queryParams[key] = all.length > 1 ? all : all[0];
}
}

if (!queryParams && fragment === undefined) {
return undefined;
}

/**
* Build the result with only the populated keys so that a spread of the
* returned object does not overwrite saved `queryParams`/`fragment` with
* `undefined` (which `Object.assign`/spread would copy as a real key).
*/
const extras: { queryParams?: Params; fragment?: string } = {};
if (queryParams) extras.queryParams = queryParams;
if (fragment !== undefined) extras.fragment = fragment;
return extras;
};

@Directive({
selector: 'ion-tabs',
})
Expand Down Expand Up @@ -103,23 +146,26 @@ export abstract class IonTabs implements AfterViewInit, AfterContentInit, AfterC
*
* a. Get the saved root view from the router outlet. If the saved root view
* matches the tabRootUrl, set the route view to this view including the
* navigation extras.
* b. If the saved root view from the router outlet does
* not match, navigate to the tabRootUrl. No navigation extras are
* included.
* navigation extras. Any `queryParams` or `fragment` declared on the tab
* button's `href` are also forwarded.
* b. If the saved root view from the router outlet does not match, navigate
* to the tabRootUrl, forwarding any `queryParams`/`fragment` declared on
* the tab button's `href`.
*
* 2. If the current tab tab is not currently selected, get the last route
* view from the router outlet.
*
* a. If the last route view exists, navigate to that view including any
* navigation extras
* b. If the last route view doesn't exist, then navigate
* to the default tabRootUrl
* navigation extras.
* b. If the last route view doesn't exist, then navigate to the default
* tabRootUrl, forwarding any `queryParams`/`fragment` declared on the
* tab button's `href`.
*/
@HostListener('ionTabButtonClick', ['$event'])
select(tabOrEvent: string | CustomEvent): Promise<boolean> | undefined {
const isTabString = typeof tabOrEvent === 'string';
const tab = isTabString ? tabOrEvent : (tabOrEvent as CustomEvent).detail.tab;
const href: string | undefined = isTabString ? undefined : (tabOrEvent as CustomEvent).detail.href;

/**
* If the tabs are not using the router, then
Expand All @@ -136,6 +182,12 @@ export abstract class IonTabs implements AfterViewInit, AfterContentInit, AfterC
const alreadySelected = this.outlet.getActiveStackId() === tab;
const tabRootUrl = `${this.outlet.tabsPrefix}/${tab}`;

/**
* The href pathname is ignored here; tab routing is driven by `tabsPrefix/tab`.
* Only the query and fragment are forwarded as navigation extras.
*/
const hrefExtras = parseHrefExtras(href);

/**
* If this is a nested tab, prevent the event
* from bubbling otherwise the outer tabs
Expand All @@ -159,17 +211,19 @@ export abstract class IonTabs implements AfterViewInit, AfterContentInit, AfterC
const navigationExtras = rootView && tabRootUrl === rootView.url && rootView.savedExtras;
return this.navCtrl.navigateRoot(tabRootUrl, {
...navigationExtras,
...hrefExtras,
animated: true,
animationDirection: 'back',
});
} else {
const lastRoute = this.outlet.getLastRouteView(tab);
/**
* If there is a lastRoute, goto that, otherwise goto the fallback url of the
* selected tab
* selected tab. When falling back to the tab root, honor query params and
* fragment declared on the tab button's href.
*/
const url = lastRoute?.url || tabRootUrl;
const navigationExtras = lastRoute?.savedExtras;
const navigationExtras = lastRoute?.savedExtras ?? (url === tabRootUrl ? hrefExtras : undefined);

return this.navCtrl.navigateRoot(url, {
...navigationExtras,
Expand Down
12 changes: 6 additions & 6 deletions packages/angular/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/angular/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "8.8.7",
"version": "8.8.8",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
Expand Down Expand Up @@ -48,7 +48,7 @@
}
},
"dependencies": {
"@ionic/core": "^8.8.7",
"@ionic/core": "^8.8.8",
"ionicons": "^8.0.13",
"jsonc-parser": "^3.0.0",
"tslib": "^2.3.0"
Expand Down
Loading
Loading