diff --git a/packages/angular/common/src/index.ts b/packages/angular/common/src/index.ts index 6e5c909acf..447fb3e1c1 100644 --- a/packages/angular/common/src/index.ts +++ b/packages/angular/common/src/index.ts @@ -1,34 +1,34 @@ -export { MenuController } from './providers/menu-controller'; export { DomController } from './providers/dom-controller'; +export { MenuController } from './providers/menu-controller'; export { NavController } from './providers/nav-controller'; export { Config, ConfigToken } from './providers/config'; export { Platform } from './providers/platform'; -export { bindLifecycleEvents, AngularDelegate } from './providers/angular-delegate'; +export { AngularDelegate, bindLifecycleEvents, IonModalToken } from './providers/angular-delegate'; export type { IonicWindow } from './types/interfaces'; -export type { ViewWillEnter, ViewWillLeave, ViewDidEnter, ViewDidLeave } from './types/ionic-lifecycle-hooks'; +export type { ViewDidEnter, ViewDidLeave, ViewWillEnter, ViewWillLeave } from './types/ionic-lifecycle-hooks'; export { NavParams } from './directives/navigation/nav-params'; -export { IonPopover } from './overlays/popover'; export { IonModal } from './overlays/modal'; +export { IonPopover } from './overlays/popover'; export { IonRouterOutlet, provideComponentInputBinding } from './directives/navigation/router-outlet'; +export * from './directives/control-value-accessors'; export { IonBackButton } from './directives/navigation/back-button'; +export { IonNav } from './directives/navigation/nav'; export { RouterLinkDelegateDirective, RouterLinkWithHrefDelegateDirective, } from './directives/navigation/router-link-delegate'; -export { IonNav } from './directives/navigation/nav'; export { IonTabs } from './directives/navigation/tabs'; -export * from './directives/control-value-accessors'; export { ProxyCmp } from './utils/proxy'; -export { IonicRouteStrategy } from './utils/routing'; export { OverlayBaseController } from './utils/overlay'; +export { IonicRouteStrategy } from './utils/routing'; export { raf } from './utils/util'; diff --git a/packages/angular/common/src/providers/angular-delegate.ts b/packages/angular/common/src/providers/angular-delegate.ts index fc794c0e34..dd4964ef1c 100644 --- a/packages/angular/common/src/providers/angular-delegate.ts +++ b/packages/angular/common/src/providers/angular-delegate.ts @@ -1,13 +1,13 @@ import { ApplicationRef, - NgZone, - Injectable, - Injector, + ComponentRef, + createComponent, EnvironmentInjector, inject, - createComponent, + Injectable, InjectionToken, - ComponentRef, + Injector, + NgZone, } from '@angular/core'; import { FrameworkDelegate, @@ -22,6 +22,9 @@ import { NavParams } from '../directives/navigation/nav-params'; import { ConfigToken } from './config'; +// Token for injecting the modal element +export const IonModalToken = new InjectionToken('IonModalToken'); + // TODO(FW-2827): types @Injectable() @@ -142,8 +145,19 @@ export const attachView = ( * The modern approach is to access the data directly * from the component's class instance. */ + const providers = getProviders(params); + + // If this is an ion-modal, provide the modal element as an injectable + // so components inside the modal can inject it directly + if (container.tagName.toLowerCase() === 'ion-modal') { + providers.push({ + provide: IonModalToken, + useValue: container, + }); + } + const childInjector = Injector.create({ - providers: getProviders(params), + providers, parent: injector, }); diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index 3ee4a74ee1..3eefb6c46e 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -26,6 +26,7 @@ export { AngularDelegate, NavParams, IonicRouteStrategy, + IonModalToken, ViewWillEnter, ViewWillLeave, ViewDidEnter, diff --git a/packages/angular/standalone/src/index.ts b/packages/angular/standalone/src/index.ts index db9a8a57da..4dc766c1d4 100644 --- a/packages/angular/standalone/src/index.ts +++ b/packages/angular/standalone/src/index.ts @@ -21,6 +21,7 @@ export { Config, Platform, NavParams, + IonModalToken, IonicRouteStrategy, ViewWillEnter, ViewDidEnter, diff --git a/packages/angular/test/apps/ng18/package.json b/packages/angular/test/apps/ng18/package.json index ac2241a158..ee2648f26e 100644 --- a/packages/angular/test/apps/ng18/package.json +++ b/packages/angular/test/apps/ng18/package.json @@ -29,8 +29,8 @@ "@angular/platform-server": "^18.0.0", "@angular/router": "^18.0.0", "@angular/ssr": "^18.0.0", - "@ionic/angular": "^8.0.0", - "@ionic/angular-server": "^8.0.0", + "@ionic/angular": "^8.6.0", + "@ionic/angular-server": "^8.6.0", "core-js": "^3.33.2", "express": "^4.15.2", "ionicons": "^7.2.0", diff --git a/packages/angular/test/apps/ng19/package.json b/packages/angular/test/apps/ng19/package.json index c7b6eff1c6..198bdc39f1 100644 --- a/packages/angular/test/apps/ng19/package.json +++ b/packages/angular/test/apps/ng19/package.json @@ -29,8 +29,8 @@ "@angular/platform-server": "^19.0.0", "@angular/router": "^19.0.0", "@angular/ssr": "^19.0.2", - "@ionic/angular": "^8.4.0", - "@ionic/angular-server": "^8.4.0", + "@ionic/angular": "^8.6.0", + "@ionic/angular-server": "^8.6.0", "core-js": "^3.33.2", "express": "^4.18.2", "ionicons": "^7.2.0", diff --git a/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/modal/modal.component.html b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/modal/modal.component.html new file mode 100644 index 0000000000..4a57a4808f --- /dev/null +++ b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/modal/modal.component.html @@ -0,0 +1,4 @@ +

+ modal works! + Close Modal +

diff --git a/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/modal/modal.component.ts b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/modal/modal.component.ts new file mode 100644 index 0000000000..1e68a941fc --- /dev/null +++ b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/modal/modal.component.ts @@ -0,0 +1,20 @@ +import { Component, Inject } from '@angular/core'; +import { IonButton, IonModalToken } from "@ionic/angular/standalone"; + +@Component({ + selector: 'app-modal', + templateUrl: './modal.component.html', + imports: [IonButton], + standalone: true, +}) +export class ModalComponent { + constructor(@Inject(IonModalToken) private modalToken: HTMLIonModalElement) { + this.modalToken.onDidDismiss().then(() => { + console.log('Modal dismissed'); + }); + } + + public close() { + this.modalToken.dismiss(); + } +} diff --git a/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.component.html b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.component.html new file mode 100644 index 0000000000..d614bd4d2b --- /dev/null +++ b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.component.html @@ -0,0 +1,2 @@ +Open Modal + diff --git a/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.component.ts b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.component.ts new file mode 100644 index 0000000000..04473823e2 --- /dev/null +++ b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { IonButton } from '@ionic/angular/standalone'; +import { ProgrammaticModalService } from './programmatic-modal.service'; + +@Component({ + selector: 'app-test', + templateUrl: './programmatic-modal.component.html', + standalone: true, + imports: [IonButton] +}) +export class ProgrammaticModalComponent { + constructor(private modalService: ProgrammaticModalService) {} + + public open() { + this.modalService.open(); + } +} + diff --git a/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.service.ts b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.service.ts new file mode 100644 index 0000000000..0a2e34ccdb --- /dev/null +++ b/packages/angular/test/apps/ng19/src/app/standalone/programmatic-modal/programmatic-modal.service.ts @@ -0,0 +1,18 @@ +import { inject, Injectable } from '@angular/core'; +import { ModalController } from "@ionic/angular/standalone"; +import { ModalComponent } from "./modal/modal.component"; + +@Injectable({ + providedIn: 'root' +}) +export class ProgrammaticModalService { + #modalController = inject(ModalController); + + async open() { + const modal = await this.#modalController.create({ + component: ModalComponent, + focusTrap: true, + }); + await modal.present(); + } +} diff --git a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts index 596aee68fd..8b267987eb 100644 --- a/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts +++ b/packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts @@ -11,6 +11,7 @@ export const routes: Routes = [ { path: 'action-sheet-controller', loadComponent: () => import('../action-sheet-controller/action-sheet-controller.component').then(c => c.ActionSheetControllerComponent) }, { path: 'popover', loadComponent: () => import('../popover/popover.component').then(c => c.PopoverComponent) }, { path: 'modal', loadComponent: () => import('../modal/modal.component').then(c => c.ModalComponent) }, + { path: 'programmatic-modal', loadComponent: () => import('../programmatic-modal/programmatic-modal.component').then(c => c.ProgrammaticModalComponent) }, { path: 'router-outlet', loadComponent: () => import('../router-outlet/router-outlet.component').then(c => c.RouterOutletComponent) }, { path: 'back-button', loadComponent: () => import('../back-button/back-button.component').then(c => c.BackButtonComponent) }, { path: 'router-link', loadComponent: () => import('../router-link/router-link.component').then(c => c.RouterLinkComponent) }, diff --git a/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html b/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html index 6c60543a83..cc99a1439d 100644 --- a/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html +++ b/packages/angular/test/base/src/app/standalone/home-page/home-page.component.html @@ -85,6 +85,11 @@ Modal Test + + + Programmatic Modal Test + + Overlay Controllers Test diff --git a/packages/angular/test/base/src/app/standalone/programmatic-modal/modal/modal.component.html b/packages/angular/test/base/src/app/standalone/programmatic-modal/modal/modal.component.html new file mode 100644 index 0000000000..0b81c38f7c --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/programmatic-modal/modal/modal.component.html @@ -0,0 +1,3 @@ +

+ modal works! +

diff --git a/packages/angular/test/base/src/app/standalone/programmatic-modal/modal/modal.component.ts b/packages/angular/test/base/src/app/standalone/programmatic-modal/modal/modal.component.ts new file mode 100644 index 0000000000..8fef9eb921 --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/programmatic-modal/modal/modal.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { IonButton } from "@ionic/angular/standalone"; + +@Component({ + selector: 'app-modal', + templateUrl: './modal.component.html', + imports: [IonButton], + standalone: true, +}) +export class ModalComponent { +} diff --git a/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.component.html b/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.component.html new file mode 100644 index 0000000000..d614bd4d2b --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.component.html @@ -0,0 +1,2 @@ +Open Modal + diff --git a/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.component.ts b/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.component.ts new file mode 100644 index 0000000000..04473823e2 --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { IonButton } from '@ionic/angular/standalone'; +import { ProgrammaticModalService } from './programmatic-modal.service'; + +@Component({ + selector: 'app-test', + templateUrl: './programmatic-modal.component.html', + standalone: true, + imports: [IonButton] +}) +export class ProgrammaticModalComponent { + constructor(private modalService: ProgrammaticModalService) {} + + public open() { + this.modalService.open(); + } +} + diff --git a/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.service.ts b/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.service.ts new file mode 100644 index 0000000000..0a2e34ccdb --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/programmatic-modal/programmatic-modal.service.ts @@ -0,0 +1,18 @@ +import { inject, Injectable } from '@angular/core'; +import { ModalController } from "@ionic/angular/standalone"; +import { ModalComponent } from "./modal/modal.component"; + +@Injectable({ + providedIn: 'root' +}) +export class ProgrammaticModalService { + #modalController = inject(ModalController); + + async open() { + const modal = await this.#modalController.create({ + component: ModalComponent, + focusTrap: true, + }); + await modal.present(); + } +}