Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(angular): setting props on a signal works #29453

Merged
merged 13 commits into from
May 6, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 43 additions & 5 deletions packages/angular/common/src/providers/angular-delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -37,7 +40,8 @@ export class AngularDelegate {
injector,
this.applicationRef,
this.zone,
elementReferenceKey
elementReferenceKey,
this.config.useSetInputAPI ?? false
);
}
}
Expand All @@ -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<any> {
Expand Down Expand Up @@ -84,7 +89,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
component,
componentProps,
cssClasses,
this.elementReferenceKey
this.elementReferenceKey,
this.enableSignalsSupport
);
resolve(el);
});
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 7 additions & 2 deletions packages/angular/src/ionic-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<IonicModule> {
static forRoot(config: IonicConfig & OptInAngularFeatures = {}): ModuleWithProviders<IonicModule> {
return {
ngModule: IonicModule,
providers: [
Expand All @@ -73,6 +77,7 @@ export class IonicModule {
multi: true,
deps: [ConfigToken, DOCUMENT, NgZone],
},
AngularDelegate,
provideComponentInputBinding(),
],
};
Expand Down
6 changes: 5 additions & 1 deletion packages/angular/standalone/src/providers/ionic-angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -23,7 +23,7 @@ let rootParamsException = false;
})
export class NavRootComponent {

params: any;
@Input() params: any = {};

ngOnInit() {
if (this.params === undefined) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -23,7 +23,7 @@ let rootParamsException = false;
})
export class NavRootComponent {

params: any;
@Input() params: any;

ngOnInit() {
if (this.params === undefined) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ion-content class="ion-padding">
<h1>Value</h1>
<h2>{{value}}</h2>
<h3>{{valueFromParams}}</h3>
<h3>{{prop}}</h3>
<p>modal is defined: <span id="modalInstance">{{ !!modal }}</span></p>
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
Expand Down
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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;
Expand All @@ -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();
Expand Down
13 changes: 7 additions & 6 deletions packages/angular/test/base/src/app/lazy/nav/nav.component.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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
};
}
}