diff --git a/packages/angular/standalone/src/directives/checkbox.ts b/packages/angular/standalone/src/directives/checkbox.ts index 218c0483ee1..7dad42fefc3 100644 --- a/packages/angular/standalone/src/directives/checkbox.ts +++ b/packages/angular/standalone/src/directives/checkbox.ts @@ -7,26 +7,14 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor, setIonicClasses } from '@ionic/angular/common'; import type { CheckboxChangeEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-checkbox.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonCheckbox = IonCheckbox_1 = class IonCheckbox extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonCheckbox extends ValueAccessor { - */ -import { proxyInputs, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const CHECKBOX_INPUTS = [ 'checked', @@ -41,40 +29,42 @@ const CHECKBOX_INPUTS = [ 'value', ]; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonCheckbox), + multi: true, +}; + +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: CHECKBOX_INPUTS, +}) @Component({ selector: 'ion-checkbox', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: CHECKBOX_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonCheckbox, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonCheckbox extends ValueAccessor implements OnInit { +export class IonCheckbox extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonCheckbox, CHECKBOX_INPUTS); - } - writeValue(value: boolean): void { this.elementRef.nativeElement.checked = this.lastValue = value; setIonicClasses(this.elementRef); diff --git a/packages/angular/standalone/src/directives/datetime.ts b/packages/angular/standalone/src/directives/datetime.ts index 4f476d7b4e2..94af3a10903 100644 --- a/packages/angular/standalone/src/directives/datetime.ts +++ b/packages/angular/standalone/src/directives/datetime.ts @@ -7,26 +7,14 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { DatetimeChangeEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-datetime.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonDatetime = IonDatetime_1 = class IonDatetime extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonDatetime extends ValueAccessor { - */ -import { proxyInputs, proxyMethods, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const DATETIME_INPUTS = [ 'cancelText', @@ -61,43 +49,44 @@ const DATETIME_INPUTS = [ 'yearValues', ]; -const DATETIME_METHODS = ['confirm', 'reset', 'cancel']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonDatetime), + multi: true, +}; +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: DATETIME_INPUTS, + methods: ['confirm', 'reset', 'cancel'], +}) @Component({ selector: 'ion-datetime', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: DATETIME_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonDatetime, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonDatetime extends ValueAccessor implements OnInit { +export class IonDatetime extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionCancel', 'ionChange', 'ionFocus', 'ionBlur']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonDatetime, DATETIME_INPUTS); - proxyMethods(IonDatetime, DATETIME_METHODS); - } - @HostListener('ionChange', ['$event.target']) handleIonChange(el: HTMLIonDatetimeElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/input.ts b/packages/angular/standalone/src/directives/input.ts index 8fb920eddbb..fd468746eb0 100644 --- a/packages/angular/standalone/src/directives/input.ts +++ b/packages/angular/standalone/src/directives/input.ts @@ -7,8 +7,8 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { @@ -18,19 +18,7 @@ import type { } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-input.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonInput = IonInput_1 = class IonInput extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonInput extends ValueAccessor { - */ -import { proxyInputs, proxyMethods, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const INPUT_INPUTS = [ 'accept', @@ -72,43 +60,43 @@ const INPUT_INPUTS = [ 'value', ]; -const INPUT_METHODS = ['setFocus', 'getInputElement']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonInput), + multi: true, +}; +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: INPUT_INPUTS, + methods: ['setFocus', 'getInputElement'], +}) @Component({ selector: 'ion-input', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: INPUT_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonInput, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonInput extends ValueAccessor implements OnInit { +export class IonInput extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionBlur', 'ionFocus']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonInput, INPUT_INPUTS); - proxyMethods(IonInput, INPUT_METHODS); - } - @HostListener('ionInput', ['$event.target']) handleIonInput(el: HTMLIonInputElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/radio-group.ts b/packages/angular/standalone/src/directives/radio-group.ts index 2c6870aa16e..ecde568d444 100644 --- a/packages/angular/standalone/src/directives/radio-group.ts +++ b/packages/angular/standalone/src/directives/radio-group.ts @@ -7,63 +7,53 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { RadioGroupChangeEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-radio-group.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonRadioGroup = IonRadioGroup_1 = class IonRadioGroup extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonRadioGroup extends ValueAccessor { - */ -import { proxyInputs, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const RADIO_GROUP_INPUTS = ['allowEmptySelection', 'name', 'value']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonRadioGroup), + multi: true, +}; + +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: RADIO_GROUP_INPUTS, +}) @Component({ selector: 'ion-radio-group', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: RADIO_GROUP_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonRadioGroup, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonRadioGroup extends ValueAccessor implements OnInit { +export class IonRadioGroup extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonRadioGroup, RADIO_GROUP_INPUTS); - } - @HostListener('ionChange', ['$event.target']) handleIonChange(el: HTMLIonRadioGroupElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/radio.ts b/packages/angular/standalone/src/directives/radio.ts index d70ea8d01df..f063d22eea4 100644 --- a/packages/angular/standalone/src/directives/radio.ts +++ b/packages/angular/standalone/src/directives/radio.ts @@ -7,63 +7,53 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-radio.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonRadio = IonRadio_1 = class IonRadio extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonRadio extends ValueAccessor { - */ -import { proxyInputs, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const RADIO_INPUTS = ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonRadio), + multi: true, +}; + +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: RADIO_INPUTS, +}) @Component({ selector: 'ion-radio', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: RADIO_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonRadio, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonRadio extends ValueAccessor implements OnInit { +export class IonRadio extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionFocus', 'ionBlur']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonRadio, RADIO_INPUTS); - } - @HostListener('ionSelect', ['$event.target']) handleIonSelect(el: any): void { /** diff --git a/packages/angular/standalone/src/directives/range.ts b/packages/angular/standalone/src/directives/range.ts index f77beecbe4b..3224fa647ed 100644 --- a/packages/angular/standalone/src/directives/range.ts +++ b/packages/angular/standalone/src/directives/range.ts @@ -7,8 +7,8 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { @@ -19,19 +19,7 @@ import type { } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-range.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonRange = IonRange_1 = class IonRange extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonRange extends ValueAccessor { - */ -import { proxyInputs, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const RANGE_INPUTS = [ 'activeBarStart', @@ -54,40 +42,42 @@ const RANGE_INPUTS = [ 'value', ]; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonRange), + multi: true, +}; + +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: RANGE_INPUTS, +}) @Component({ selector: 'ion-range', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: RANGE_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonRange, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonRange extends ValueAccessor implements OnInit { +export class IonRange extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionFocus', 'ionBlur', 'ionKnobMoveStart', 'ionKnobMoveEnd']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonRange, RANGE_INPUTS); - } - @HostListener('ionChange', ['$event.target']) handleIonChange(el: HTMLIonRangeElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/searchbar.ts b/packages/angular/standalone/src/directives/searchbar.ts index 75f1d7da671..ca869982ca0 100644 --- a/packages/angular/standalone/src/directives/searchbar.ts +++ b/packages/angular/standalone/src/directives/searchbar.ts @@ -7,26 +7,14 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { SearchbarInputEventDetail, SearchbarChangeEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-searchbar.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonSearchbar = IonSearchbar_1 = class IonSearchbar extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonSearchbar extends ValueAccessor { - */ -import { proxyInputs, proxyMethods, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const SEARCHBAR_INPUTS = [ 'animated', @@ -51,43 +39,43 @@ const SEARCHBAR_INPUTS = [ 'value', ]; -const SEARCHBAR_METHODS = ['setFocus', 'getInputElement']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonSearchbar), + multi: true, +}; +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: SEARCHBAR_INPUTS, + methods: ['setFocus', 'getInputElement'], +}) @Component({ selector: 'ion-searchbar', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: SEARCHBAR_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonSearchbar, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonSearchbar extends ValueAccessor implements OnInit { +export class IonSearchbar extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionCancel', 'ionClear', 'ionBlur', 'ionFocus']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonSearchbar, SEARCHBAR_INPUTS); - proxyMethods(IonSearchbar, SEARCHBAR_METHODS); - } - @HostListener('ionInput', ['$event.target']) handleIonInput(el: HTMLIonSearchbarElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/segment.ts b/packages/angular/standalone/src/directives/segment.ts index 5d6a16f77c0..cbdf5a81843 100644 --- a/packages/angular/standalone/src/directives/segment.ts +++ b/packages/angular/standalone/src/directives/segment.ts @@ -7,63 +7,53 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { SegmentChangeEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-segment.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonSegment = IonSegment_1 = class IonSegment extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonSegment extends ValueAccessor { - */ -import { proxyInputs, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const SEGMENT_INPUTS = ['color', 'disabled', 'mode', 'scrollable', 'selectOnFocus', 'swipeGesture', 'value']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonSegment), + multi: true, +}; + +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: SEGMENT_INPUTS, +}) @Component({ selector: 'ion-segment', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: SEGMENT_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonSegment, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonSegment extends ValueAccessor implements OnInit { +export class IonSegment extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonSegment, SEGMENT_INPUTS); - } - @HostListener('ionChange', ['$event.target']) handleIonChange(el: HTMLIonSegmentElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/select.ts b/packages/angular/standalone/src/directives/select.ts index 0041f91d863..fa194b6ad50 100644 --- a/packages/angular/standalone/src/directives/select.ts +++ b/packages/angular/standalone/src/directives/select.ts @@ -7,26 +7,14 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { SelectChangeEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-select.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonSelect = IonSelect_1 = class IonSelect extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonSelect extends ValueAccessor { - */ -import { proxyInputs, proxyMethods, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const SELECT_INPUTS = [ 'cancelText', @@ -52,43 +40,43 @@ const SELECT_INPUTS = [ 'value', ]; -const SELECT_METHODS = ['open']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonSelect), + multi: true, +}; +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: SELECT_INPUTS, + methods: ['open'], +}) @Component({ selector: 'ion-select', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: SELECT_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonSelect, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonSelect extends ValueAccessor implements OnInit { +export class IonSelect extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionCancel', 'ionDismiss', 'ionFocus', 'ionBlur']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonSelect, SELECT_INPUTS); - proxyMethods(IonSelect, SELECT_METHODS); - } - @HostListener('ionChange', ['$event.target']) handleIonChange(el: HTMLIonSelectElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/textarea.ts b/packages/angular/standalone/src/directives/textarea.ts index af5d8b49967..f538bfefec2 100644 --- a/packages/angular/standalone/src/directives/textarea.ts +++ b/packages/angular/standalone/src/directives/textarea.ts @@ -7,26 +7,14 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor } from '@ionic/angular/common'; import type { TextareaChangeEventDetail, TextareaInputEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-textarea.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonTextarea = IonTextarea_1 = class IonTextarea extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonTextarea extends ValueAccessor { - */ -import { proxyInputs, proxyMethods, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const TEXTAREA_INPUTS = [ 'autoGrow', @@ -61,43 +49,43 @@ const TEXTAREA_INPUTS = [ 'wrap', ]; -const TEXTAREA_METHODS = ['setFocus', 'getInputElement']; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonTextarea), + multi: true, +}; +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: TEXTAREA_INPUTS, + methods: ['setFocus', 'getInputElement'], +}) @Component({ selector: 'ion-textarea', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: TEXTAREA_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonTextarea, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonTextarea extends ValueAccessor implements OnInit { +export class IonTextarea extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionBlur', 'ionFocus']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonTextarea, TEXTAREA_INPUTS); - proxyMethods(IonTextarea, TEXTAREA_METHODS); - } - @HostListener('ionInput', ['$event.target']) handleIonInput(el: HTMLIonTextareaElement): void { this.handleValueChange(el, el.value); diff --git a/packages/angular/standalone/src/directives/toggle.ts b/packages/angular/standalone/src/directives/toggle.ts index 1736200650f..7bd3db96546 100644 --- a/packages/angular/standalone/src/directives/toggle.ts +++ b/packages/angular/standalone/src/directives/toggle.ts @@ -7,26 +7,14 @@ import { HostListener, Injector, NgZone, + forwardRef, } from '@angular/core'; -import type { OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ValueAccessor, setIonicClasses } from '@ionic/angular/common'; import type { ToggleChangeEventDetail, Components } from '@ionic/core/components'; import { defineCustomElement } from '@ionic/core/components/ion-toggle.js'; -/** - * Value accessor components should not use ProxyCmp - * and should call defineCustomElement and proxyInputs - * manually instead. Using both the @ProxyCmp and @Component - * decorators and useExisting (where useExisting refers to the - * class) causes ng-packagr to output multiple component variables - * which breaks treeshaking. - * For example, the following would be generated: - * let IonToggle = IonToggle_1 = class IonToggle extends ValueAccessor { - * Instead, we want only want the class generated: - * class IonToggle extends ValueAccessor { - */ -import { proxyInputs, proxyOutputs } from './angular-component-lib/utils'; +import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils'; const TOGGLE_INPUTS = [ 'checked', @@ -41,40 +29,42 @@ const TOGGLE_INPUTS = [ 'value', ]; +/** + * Pulling the provider into an object and using PURE works + * around an ng-packagr issue that causes + * components with multiple decorators and + * a provider to be re-assigned. This re-assignment + * is not supported by Webpack and causes treeshaking + * to not work on these kinds of components. + */ +const accessorProvider = { + provide: NG_VALUE_ACCESSOR, + useExisting: /*@__PURE__*/ forwardRef(() => IonToggle), + multi: true, +}; + +@ProxyCmp({ + defineCustomElementFn: defineCustomElement, + inputs: TOGGLE_INPUTS, +}) @Component({ selector: 'ion-toggle', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: TOGGLE_INPUTS, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: IonToggle, - multi: true, - }, - ], + providers: [accessorProvider], standalone: true, }) -export class IonToggle extends ValueAccessor implements OnInit { +export class IonToggle extends ValueAccessor { protected el: HTMLElement; constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) { super(injector, r); - defineCustomElement(); c.detach(); this.el = r.nativeElement; proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']); } - ngOnInit(): void { - /** - * Data-bound input properties are set - * by Angular after the constructor, so - * we need to run the proxy in ngOnInit. - */ - proxyInputs(IonToggle, TOGGLE_INPUTS); - } - writeValue(value: boolean): void { this.elementRef.nativeElement.checked = this.lastValue = value; setIonicClasses(this.elementRef); diff --git a/packages/angular/test/base/e2e/src/standalone/value-accessors.spec.ts b/packages/angular/test/base/e2e/src/standalone/value-accessors.spec.ts index 1e82274c801..ab03da596a1 100644 --- a/packages/angular/test/base/e2e/src/standalone/value-accessors.spec.ts +++ b/packages/angular/test/base/e2e/src/standalone/value-accessors.spec.ts @@ -14,18 +14,6 @@ describe('Value Accessors', () => { cy.get('ion-checkbox').should('have.class', 'ion-dirty'); cy.get('ion-checkbox').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-checkbox').should('have.prop', 'color', 'danger'); - }); - }); - - describe('Datetime', () => { - beforeEach(() => cy.visit('/standalone/value-accessors/datetime')); - - it('should proxy inputs on load', () => { - cy.get('ion-datetime').should('have.prop', 'color', 'danger'); - }); }); describe('Input', () => { @@ -60,10 +48,6 @@ describe('Value Accessors', () => { cy.get('ion-input[formControlName="inputNumber"]').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-input').first().should('have.prop', 'color', 'danger'); - }); }); describe('Radio Group', () => { @@ -79,21 +63,8 @@ describe('Value Accessors', () => { cy.get('ion-radio-group').should('have.class', 'ion-dirty'); cy.get('ion-radio-group').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-radio').first().should('have.prop', 'color', 'danger'); - }); }); - describe('Range', () => { - beforeEach(() => cy.visit('/standalone/value-accessors/range')); - - it('should proxy inputs on load', () => { - cy.get('ion-range').should('have.prop', 'color', 'danger'); - }); - }); - - describe('Searchbar', () => { beforeEach(() => cy.visit('/standalone/value-accessors/searchbar')); @@ -109,10 +80,6 @@ describe('Value Accessors', () => { cy.get('ion-searchbar').should('have.class', 'ion-dirty'); cy.get('ion-searchbar').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-searchbar').should('have.prop', 'color', 'danger'); - }); }); describe('Segment', () => { @@ -128,10 +95,6 @@ describe('Value Accessors', () => { cy.get('ion-segment').should('have.class', 'ion-dirty'); cy.get('ion-segment').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-segment').should('have.prop', 'color', 'danger'); - }); }); describe('Select', () => { @@ -150,10 +113,6 @@ describe('Value Accessors', () => { cy.get('ion-select').should('have.class', 'ion-dirty'); cy.get('ion-select').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-select').should('have.prop', 'color', 'danger'); - }); }); describe('Textarea', () => { @@ -171,10 +130,6 @@ describe('Value Accessors', () => { cy.get('ion-textarea').should('have.class', 'ion-dirty'); cy.get('ion-textarea').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-textarea').should('have.prop', 'color', 'danger'); - }); }); describe('Toggle', () => { @@ -190,10 +145,6 @@ describe('Value Accessors', () => { cy.get('ion-toggle').should('have.class', 'ion-dirty'); cy.get('ion-toggle').should('have.class', 'ion-valid'); }); - - it('should proxy inputs on load', () => { - cy.get('ion-toggle').should('have.prop', 'color', 'danger'); - }); }); }); diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/checkbox/checkbox.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/checkbox/checkbox.component.html index b3d621b0aeb..2921e06b917 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/checkbox/checkbox.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/checkbox/checkbox.component.html @@ -6,6 +6,6 @@

IonCheckbox Value Accessors

- + diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/datetime/datetime.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/datetime/datetime.component.html index 6d682257c88..22c36339717 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/datetime/datetime.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/datetime/datetime.component.html @@ -6,6 +6,6 @@

IonDatetime Value Accessors

- + diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/input/input.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/input/input.component.html index aa9a2feeff6..e63235cea5a 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/input/input.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/input/input.component.html @@ -5,7 +5,7 @@

IonInput Value Accessors

- + diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/radio-group/radio-group.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/radio-group/radio-group.component.html index 274dfd24788..a511dbbbe50 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/radio-group/radio-group.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/radio-group/radio-group.component.html @@ -7,7 +7,7 @@

IonRadioGroup Value Accessors

- One + One Two diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/range/range.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/range/range.component.html index 969f4b13c7f..9a496fbcd3c 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/range/range.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/range/range.component.html @@ -4,6 +4,6 @@

IonRange Value Accessors

This test checks the form integrations with ion-range to make sure values are correctly assigned to the form group.

- + diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/searchbar/searchbar.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/searchbar/searchbar.component.html index c667a787d24..9a6137c65ba 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/searchbar/searchbar.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/searchbar/searchbar.component.html @@ -5,6 +5,6 @@

IonSearchbar Value Accessors

group.

- + diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/segment/segment.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/segment/segment.component.html index fca9dba8bcc..98016fcde68 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/segment/segment.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/segment/segment.component.html @@ -6,7 +6,7 @@

IonSegment Value Accessors

- + Paid diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/textarea/textarea.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/textarea/textarea.component.html index 05ab51817a1..678d42d391a 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/textarea/textarea.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/textarea/textarea.component.html @@ -6,6 +6,6 @@

IonTextarea Value Accessors

- + diff --git a/packages/angular/test/base/src/app/standalone/value-accessors/toggle/toggle.component.html b/packages/angular/test/base/src/app/standalone/value-accessors/toggle/toggle.component.html index b7d74781c55..08888059f99 100644 --- a/packages/angular/test/base/src/app/standalone/value-accessors/toggle/toggle.component.html +++ b/packages/angular/test/base/src/app/standalone/value-accessors/toggle/toggle.component.html @@ -5,6 +5,6 @@

IonToggle Value Accessors

- +