diff --git a/packages/angular/common/src/directives/navigation/nav-delegate.ts b/packages/angular/common/src/directives/navigation/nav-delegate.ts index 009c2f17da..58429961db 100644 --- a/packages/angular/common/src/directives/navigation/nav-delegate.ts +++ b/packages/angular/common/src/directives/navigation/nav-delegate.ts @@ -37,7 +37,6 @@ export class NavDelegate { ref: ElementRef, environmentInjector: EnvironmentInjector, injector: Injector, - // TODO FW-4766: Remove AngularDelegate angularDelegate: AngularDelegate, protected z: NgZone ) { diff --git a/packages/angular/src/directives/navigation/nav-delegate.ts b/packages/angular/src/directives/navigation/nav-delegate.ts index d8c6d1231d..9d61cd5d65 100644 --- a/packages/angular/src/directives/navigation/nav-delegate.ts +++ b/packages/angular/src/directives/navigation/nav-delegate.ts @@ -10,7 +10,6 @@ export class NavDelegate extends NavDelegateBase { ref: ElementRef, environmentInjector: EnvironmentInjector, injector: Injector, - // TODO FW-4766: Remove AngularDelegate angularDelegate: AngularDelegate, z: NgZone ) { diff --git a/packages/angular/standalone/src/index.ts b/packages/angular/standalone/src/index.ts index a1cdeac984..4f012c4cd3 100644 --- a/packages/angular/standalone/src/index.ts +++ b/packages/angular/standalone/src/index.ts @@ -4,7 +4,7 @@ export { IonPopover } from './overlays/popover'; export { IonRouterOutlet } from './navigation/router-outlet'; export { IonRouterLink, IonRouterLinkWithHref } from './navigation/router-link-delegate'; export { IonNav } from './navigation/nav-delegate'; - +export { provideIonicAngular } from './providers/ionic-angular'; export { ActionSheetController, AlertController, diff --git a/packages/angular/standalone/src/navigation/nav-delegate.ts b/packages/angular/standalone/src/navigation/nav-delegate.ts index 62ad35144a..410f45e635 100644 --- a/packages/angular/standalone/src/navigation/nav-delegate.ts +++ b/packages/angular/standalone/src/navigation/nav-delegate.ts @@ -15,7 +15,6 @@ export class IonNav extends NavDelegateBase { ref: ElementRef, environmentInjector: EnvironmentInjector, injector: Injector, - // TODO FW-4766: Remove AngularDelegate angularDelegate: AngularDelegate, z: NgZone ) { diff --git a/packages/angular/standalone/src/providers/ionic-angular.ts b/packages/angular/standalone/src/providers/ionic-angular.ts new file mode 100644 index 0000000000..8dd0053cc0 --- /dev/null +++ b/packages/angular/standalone/src/providers/ionic-angular.ts @@ -0,0 +1,51 @@ +import { DOCUMENT } from '@angular/common'; +import { APP_INITIALIZER } from '@angular/core'; +import type { Provider } from '@angular/core'; +import { + AngularDelegate, + ConfigToken, + ModalController, + PopoverController, + provideComponentInputBinding, +} from '@ionic/angular/common'; +import { initialize } from '@ionic/core/components'; +import type { IonicConfig } from '@ionic/core/components'; + +export const provideIonicAngular = (config?: IonicConfig): Provider[] => { + /** + * TODO FW-4967 + * Use makeEnvironmentProviders once Angular 14 support is dropped. + * This prevents provideIonicAngular from being accidentally referenced in an @Component. + */ + return [ + { + provide: ConfigToken, + useValue: config, + }, + { + provide: APP_INITIALIZER, + useFactory: initializeIonicAngular, + multi: true, + deps: [ConfigToken, DOCUMENT], + }, + provideComponentInputBinding(), + AngularDelegate, + ModalController, + PopoverController, + ]; +}; + +const initializeIonicAngular = (config: IonicConfig, doc: Document) => { + return () => { + /** + * By default Ionic Framework hides elements that + * are not hydrated, but in the CE build there is no + * hydration. + * TODO FW-2797: Remove when all integrations have been + * migrated to CE build. + */ + doc.documentElement.classList.add('ion-ce'); + + initialize(config); + }; +}; diff --git a/packages/angular/test/apps/ng14/src/main-standalone.ts b/packages/angular/test/apps/ng14/src/main-standalone.ts new file mode 100644 index 0000000000..18e84fb933 --- /dev/null +++ b/packages/angular/test/apps/ng14/src/main-standalone.ts @@ -0,0 +1,23 @@ +import { importProvidersFrom } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { RouteReuseStrategy } from '@angular/router'; +import { provideIonicAngular, IonicRouteStrategy } from '@ionic/angular/standalone'; + +import { AppComponentStandalone } from './app/app-standalone.component'; +import { AppRoutingModule } from './app/app-routing.module'; + +import { routes } from './app/app.routes'; + +export const bootstrapStandalone = () => { + bootstrapApplication(AppComponentStandalone, { + providers: [ + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, + /** + * provideRouter is not available in Angular 14, so + * we fallback to using AppRoutingModule + */ + importProvidersFrom(AppRoutingModule), + provideIonicAngular({ keyboardHeight: 12345 }) + ], + }); +} diff --git a/packages/angular/test/base/e2e/src/standalone/overlay-controllers.spec.ts b/packages/angular/test/base/e2e/src/standalone/overlay-controllers.spec.ts new file mode 100644 index 0000000000..e48982c7fe --- /dev/null +++ b/packages/angular/test/base/e2e/src/standalone/overlay-controllers.spec.ts @@ -0,0 +1,17 @@ +describe('Overlay Controllers', () => { + beforeEach(() => { + cy.visit('/standalone/overlay-controllers'); + }) + + it('should present a modal', () => { + cy.get('button#open-modal').click(); + + cy.get('ion-modal app-dialog-content').should('be.visible'); + }); + + it('should present a popover', () => { + cy.get('button#open-popover').click(); + + cy.get('ion-popover app-dialog-content').should('be.visible'); + }); +}) diff --git a/packages/angular/test/base/e2e/src/standalone/providers.spec.ts b/packages/angular/test/base/e2e/src/standalone/providers.spec.ts new file mode 100644 index 0000000000..71aca1c635 --- /dev/null +++ b/packages/angular/test/base/e2e/src/standalone/providers.spec.ts @@ -0,0 +1,11 @@ +describe('Providers', () => { + beforeEach(() => { + cy.visit('/standalone/providers'); + }) + + it('provideIonicAngular should initialize Ionic and set config correctly', () => { + cy.ionPageVisible('app-providers'); + + cy.get('#keyboard-height').should('have.text', '12345'); + }); +}) diff --git a/packages/angular/test/base/src/app/app-standalone.component.ts b/packages/angular/test/base/src/app/app-standalone.component.ts new file mode 100644 index 0000000000..e39f6d3661 --- /dev/null +++ b/packages/angular/test/base/src/app/app-standalone.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + standalone: true, + imports: [RouterModule] +}) +export class AppComponentStandalone { +} diff --git a/packages/angular/test/base/src/app/standalone/app-standalone/app.component.ts b/packages/angular/test/base/src/app/standalone/app-standalone/app.component.ts index 280de0d8c7..e105848648 100644 --- a/packages/angular/test/base/src/app/standalone/app-standalone/app.component.ts +++ b/packages/angular/test/base/src/app/standalone/app-standalone/app.component.ts @@ -1,15 +1,6 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { IonRouterOutlet } from '@ionic/angular/standalone'; -/** - * This temporary code initialized Ionic and ensures components are visible. - * TODO FW-4766 Can be removed when ticket is implemented - */ -import { initialize } from '@ionic/core/components'; -initialize(); - -document.querySelector('html')!.classList.add('ion-ce') - @Component({ selector: 'app-root-standalone', 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 c43c86dd0f..fb027e34f9 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 @@ -12,6 +12,8 @@ export const routes: Routes = [ { path: 'back-button', loadComponent: () => import('../back-button/back-button.component').then(m => m.BackButtonComponent) }, { path: 'router-link', loadComponent: () => import('../router-link/router-link.component').then(m => m.RouterLinkComponent) }, { path: 'nav', loadComponent: () => import('../nav/nav.component').then(m => m.NavComponent) }, + { path: 'providers', loadComponent: () => import('../providers/providers.component').then(m => m.ProvidersComponent) }, + { path: 'overlay-controllers', loadComponent: () => import('../overlay-controllers/overlay-controllers.component').then(m => m.OverlayControllersComponent) }, ] }, ]; diff --git a/packages/angular/test/base/src/app/standalone/nav/nav.component.ts b/packages/angular/test/base/src/app/standalone/nav/nav.component.ts index e056feca74..b969f7683c 100644 --- a/packages/angular/test/base/src/app/standalone/nav/nav.component.ts +++ b/packages/angular/test/base/src/app/standalone/nav/nav.component.ts @@ -8,9 +8,7 @@ import { PageOneComponent } from './page-one.component'; selector: 'app-nav', templateUrl: './nav.component.html', standalone: true, - imports: [IonNav], - // TODO FW-4766: Remove AngularDelegate from providers - providers: [AngularDelegate] + imports: [IonNav] }) export class NavComponent { component = PageOneComponent; diff --git a/packages/angular/test/base/src/app/standalone/overlay-controllers/overlay-controllers.component.html b/packages/angular/test/base/src/app/standalone/overlay-controllers/overlay-controllers.component.html new file mode 100644 index 0000000000..b05f9b2530 --- /dev/null +++ b/packages/angular/test/base/src/app/standalone/overlay-controllers/overlay-controllers.component.html @@ -0,0 +1,4 @@ +