diff --git a/packages/angular/common/src/directives/navigation/nav-params.ts b/packages/angular/common/src/directives/navigation/nav-params.ts index a5af4b9d631..23f4125aff4 100644 --- a/packages/angular/common/src/directives/navigation/nav-params.ts +++ b/packages/angular/common/src/directives/navigation/nav-params.ts @@ -19,7 +19,11 @@ * ``` */ export class NavParams { - constructor(public data: { [key: string]: any } = {}) {} + constructor(public data: { [key: string]: any } = {}) { + console.warn( + `[Ionic Warning]: NavParams has been deprecated in favor of using Angular's input API. Developers should migrate to either the @Input decorator or the Signals-based input API.` + ); + } /** * Get the value of a nav-parameter for the current view diff --git a/packages/angular/common/src/providers/angular-delegate.ts b/packages/angular/common/src/providers/angular-delegate.ts index c1524e8b72c..fc794c0e34b 100644 --- a/packages/angular/common/src/providers/angular-delegate.ts +++ b/packages/angular/common/src/providers/angular-delegate.ts @@ -20,12 +20,15 @@ import { import { NavParams } from '../directives/navigation/nav-params'; +import { ConfigToken } from './config'; + // TODO(FW-2827): types @Injectable() export class AngularDelegate { private zone = inject(NgZone); private applicationRef = inject(ApplicationRef); + private config = inject(ConfigToken); create( environmentInjector: EnvironmentInjector, @@ -37,7 +40,8 @@ export class AngularDelegate { injector, this.applicationRef, this.zone, - elementReferenceKey + elementReferenceKey, + this.config.useSetInputAPI ?? false ); } } @@ -51,7 +55,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate { private injector: Injector, private applicationRef: ApplicationRef, private zone: NgZone, - private elementReferenceKey?: string + private elementReferenceKey?: string, + private enableSignalsSupport?: boolean ) {} attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise { @@ -84,7 +89,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate { component, componentProps, cssClasses, - this.elementReferenceKey + this.elementReferenceKey, + this.enableSignalsSupport ); resolve(el); }); @@ -121,7 +127,8 @@ export const attachView = ( component: any, params: any, cssClasses: string[] | undefined, - elementReferenceKey: string | undefined + elementReferenceKey: string | undefined, + enableSignalsSupport: boolean | undefined ): any => { /** * Wraps the injector with a custom injector that @@ -164,7 +171,38 @@ export const attachView = ( ); } - Object.assign(instance, params); + /** + * Angular 14.1 added support for setInput + * so we need to fall back to Object.assign + * for Angular 14.0. + */ + if (enableSignalsSupport === true && componentRef.setInput !== undefined) { + const { modal, popover, ...otherParams } = params; + /** + * Any key/value pairs set in componentProps + * must be set as inputs on the component instance. + */ + for (const key in otherParams) { + componentRef.setInput(key, otherParams[key]); + } + + /** + * Using setInput will cause an error when + * setting modal/popover on a component that + * does not define them as an input. For backwards + * compatibility purposes we fall back to using + * Object.assign for these properties. + */ + if (modal !== undefined) { + Object.assign(instance, { modal }); + } + + if (popover !== undefined) { + Object.assign(instance, { popover }); + } + } else { + Object.assign(instance, params); + } } if (cssClasses) { for (const cssClass of cssClasses) { diff --git a/packages/angular/src/ionic-module.ts b/packages/angular/src/ionic-module.ts index 13656fc3269..acd7745294b 100644 --- a/packages/angular/src/ionic-module.ts +++ b/packages/angular/src/ionic-module.ts @@ -52,14 +52,18 @@ const DECLARATIONS = [ IonMaxValidator, ]; +type OptInAngularFeatures = { + useSetInputAPI?: boolean; +}; + @NgModule({ declarations: DECLARATIONS, exports: DECLARATIONS, - providers: [AngularDelegate, ModalController, PopoverController], + providers: [ModalController, PopoverController], imports: [CommonModule], }) export class IonicModule { - static forRoot(config?: IonicConfig): ModuleWithProviders { + static forRoot(config: IonicConfig & OptInAngularFeatures = {}): ModuleWithProviders { return { ngModule: IonicModule, providers: [ @@ -73,6 +77,7 @@ export class IonicModule { multi: true, deps: [ConfigToken, DOCUMENT, NgZone], }, + AngularDelegate, provideComponentInputBinding(), ], }; diff --git a/packages/angular/standalone/src/providers/ionic-angular.ts b/packages/angular/standalone/src/providers/ionic-angular.ts index 7aa1747bf98..d810ef4d6b9 100644 --- a/packages/angular/standalone/src/providers/ionic-angular.ts +++ b/packages/angular/standalone/src/providers/ionic-angular.ts @@ -8,7 +8,11 @@ import type { IonicConfig } from '@ionic/core/components'; import { ModalController } from './modal-controller'; import { PopoverController } from './popover-controller'; -export const provideIonicAngular = (config?: IonicConfig): EnvironmentProviders => { +type OptInAngularFeatures = { + useSetInputAPI?: boolean; +}; + +export const provideIonicAngular = (config: IonicConfig & OptInAngularFeatures = {}): EnvironmentProviders => { return makeEnvironmentProviders([ { provide: ConfigToken, diff --git a/packages/angular/test/apps/ng16/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts b/packages/angular/test/apps/ng16/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts index ae545093dc7..765a594950c 100644 --- a/packages/angular/test/apps/ng16/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts +++ b/packages/angular/test/apps/ng16/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts @@ -1,5 +1,5 @@ import { JsonPipe } from "@angular/common"; -import { Component } from "@angular/core"; +import { Component, Input } from "@angular/core"; import { IonicModule } from "@ionic/angular"; @@ -23,7 +23,7 @@ let rootParamsException = false; }) export class NavRootComponent { - params: any; + @Input() params: any = {}; ngOnInit() { if (this.params === undefined) { diff --git a/packages/angular/test/apps/ng17/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts b/packages/angular/test/apps/ng17/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts index ae545093dc7..13bca637f3e 100644 --- a/packages/angular/test/apps/ng17/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts +++ b/packages/angular/test/apps/ng17/src/app/lazy/version-test/modal-nav-params/nav-root.component.ts @@ -1,5 +1,5 @@ import { JsonPipe } from "@angular/common"; -import { Component } from "@angular/core"; +import { Component, Input } from "@angular/core"; import { IonicModule } from "@ionic/angular"; @@ -23,7 +23,7 @@ let rootParamsException = false; }) export class NavRootComponent { - params: any; + @Input() params: any; ngOnInit() { if (this.params === undefined) { diff --git a/packages/angular/test/base/src/app/lazy/alert/alert.component.ts b/packages/angular/test/base/src/app/lazy/alert/alert.component.ts index 4a5b9e10703..d92f9339d2d 100644 --- a/packages/angular/test/base/src/app/lazy/alert/alert.component.ts +++ b/packages/angular/test/base/src/app/lazy/alert/alert.component.ts @@ -1,6 +1,5 @@ import { Component, NgZone } from '@angular/core'; import { AlertController } from '@ionic/angular'; -import { NavComponent } from '../nav/nav.component'; @Component({ selector: 'app-alert', diff --git a/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.html b/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.html index 1420dd5b804..92a20746c99 100644 --- a/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.html +++ b/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.html @@ -11,7 +11,7 @@

Value

{{value}}

-

{{valueFromParams}}

+

{{prop}}

modal is defined: {{ !!modal }}

ngOnInit: {{onInit}}

ionViewWillEnter: {{willEnter}}

diff --git a/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.ts b/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.ts index d26fff7bd33..495ae1412c9 100644 --- a/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.ts +++ b/packages/angular/test/base/src/app/lazy/modal-example/modal-example.component.ts @@ -1,6 +1,6 @@ import { Component, Input, NgZone, OnInit, Optional } from '@angular/core'; import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { ModalController, NavParams, IonNav, ViewWillLeave, ViewDidEnter, ViewDidLeave } from '@ionic/angular'; +import { ModalController, IonNav, ViewWillLeave, ViewDidEnter, ViewDidLeave } from '@ionic/angular'; @Component({ selector: 'app-modal-example', @@ -9,12 +9,12 @@ import { ModalController, NavParams, IonNav, ViewWillLeave, ViewDidEnter, ViewDi export class ModalExampleComponent implements OnInit, ViewWillLeave, ViewDidEnter, ViewWillLeave, ViewDidLeave { @Input() value?: string; + @Input() prop?: string; form = new UntypedFormGroup({ select: new UntypedFormControl([]) }); - valueFromParams: string; onInit = 0; willEnter = 0; didEnter = 0; @@ -25,11 +25,8 @@ export class ModalExampleComponent implements OnInit, ViewWillLeave, ViewDidEnte constructor( private modalCtrl: ModalController, - @Optional() public nav: IonNav, - navParams: NavParams - ) { - this.valueFromParams = navParams.get('prop'); - } + @Optional() public nav: IonNav + ) {} ngOnInit() { NgZone.assertInAngularZone(); diff --git a/packages/angular/test/base/src/app/lazy/nav/nav.component.ts b/packages/angular/test/base/src/app/lazy/nav/nav.component.ts index 735f4f4b250..7c5fec01f5a 100644 --- a/packages/angular/test/base/src/app/lazy/nav/nav.component.ts +++ b/packages/angular/test/base/src/app/lazy/nav/nav.component.ts @@ -1,6 +1,5 @@ -import { Component } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { ModalExampleComponent } from '../modal-example/modal-example.component'; -import { NavParams } from '@ionic/angular'; @Component({ selector: 'app-nav', @@ -10,11 +9,13 @@ export class NavComponent { rootPage = ModalExampleComponent; rootParams: any; - constructor( - params: NavParams - ) { + @Input() value?: string; + @Input() prop?: string; + + ngOnInit() { this.rootParams = { - ...params.data + value: this.value, + prop: this.prop }; } }