mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-15 01:03:03 +08:00
fix(angular): remove form control side effects (#28359)
Issue number: resolves #28358
---------
<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->
<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->
28f2ec9c62
exposed a (possible) `ng-packagr` bug where the form control components
were being re-assigned, which breaks treeshaking. These components were
considered side effects and were always being pulled into the bundle.
This resulted in a higher than expected bundle size. This issue appears
to be caused by using 2 decorators **and** referring to the class in
`useExisting` (for providers). Doing just one of these does not
reproduce the issue.
The compiled output looks something like this:
```typescript
let IonToggle = IonToggle_1 = /*@__PURE__*/ class IonToggle extends ValueAccessor {
constructor(c, r, z, injector) {
super(injector, r);
this.z = z;
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
}
writeValue(value) {
this.elementRef.nativeElement.checked = this.lastValue = value;
setIonicClasses(this.elementRef);
}
handleIonChange(el) {
this.handleValueChange(el, el.checked);
}
};
/** @nocollapse */ IonToggle.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonToggle, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ IonToggle.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: IonToggle, isStandalone: true, selector: "ion-toggle", inputs: { checked: "checked", color: "color", disabled: "disabled", enableOnOffLabels: "enableOnOffLabels", justify: "justify", labelPlacement: "labelPlacement", legacy: "legacy", mode: "mode", name: "name", value: "value" }, host: { listeners: { "ionChange": "handleIonChange($event.target)" } }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: IonToggle_1,
multi: true,
},
], usesInheritance: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
IonToggle = IonToggle_1 = __decorate([
ProxyCmp({
defineCustomElementFn: defineCustomElement$1i,
inputs: TOGGLE_INPUTS,
})
], IonToggle);
```
## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
- Removed the `ProxyCmp` usage in favor of manually calling proxyInputs
and proxyMethods.
- Also saw that select was missing a form control test, so I added one
The compiled code now looks something like this:
```typescript
class IonToggle extends ValueAccessor {
constructor(c, r, z, injector) {
super(injector, r);
this.z = z;
defineCustomElement$1i();
proxyInputs(IonToggle, TOGGLE_INPUTS);
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
}
writeValue(value) {
this.elementRef.nativeElement.checked = this.lastValue = value;
setIonicClasses(this.elementRef);
}
handleIonChange(el) {
this.handleValueChange(el, el.checked);
}
}
/** @nocollapse */ IonToggle.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: IonToggle, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ IonToggle.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: IonToggle, isStandalone: true, selector: "ion-toggle", inputs: { checked: "checked", color: "color", disabled: "disabled", enableOnOffLabels: "enableOnOffLabels", justify: "justify", labelPlacement: "labelPlacement", legacy: "legacy", mode: "mode", name: "name", value: "value" }, host: { listeners: { "ionChange": "handleIonChange($event.target)" } }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: IonToggle,
multi: true,
},
], usesInheritance: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
```
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->
## Other information
<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
Ryan provided some context on a related Stencil bug where doing
reassignments broke treeshaking in Webpack. While the source of this bug
is not Stencil, understanding the Stencil bug helped me better
understand this issue:
https://github.com/ionic-team/stencil/issues/3191
https://github.com/ionic-team/stencil/pull/3248
https://github.com/ionic-team/stencil/pull/4188 (fixes an issue
introduced in the above stencil PR)
Dev build: `7.5.1-dev.11697480817.10fa2601`
This commit is contained in:
@ -8,12 +8,25 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
||||||
import type { CheckboxChangeEventDetail, Components } from '@ionic/core/components';
|
import type { CheckboxChangeEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-checkbox.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-checkbox.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const CHECKBOX_INPUTS = [
|
const CHECKBOX_INPUTS = [
|
||||||
'checked',
|
'checked',
|
||||||
@ -28,10 +41,6 @@ const CHECKBOX_INPUTS = [
|
|||||||
'value',
|
'value',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: CHECKBOX_INPUTS,
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-checkbox',
|
selector: 'ion-checkbox',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -47,15 +56,25 @@ const CHECKBOX_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonCheckbox extends ValueAccessor {
|
export class IonCheckbox extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
|
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 {
|
writeValue(value: boolean): void {
|
||||||
this.elementRef.nativeElement.checked = this.lastValue = value;
|
this.elementRef.nativeElement.checked = this.lastValue = value;
|
||||||
setIonicClasses(this.elementRef);
|
setIonicClasses(this.elementRef);
|
||||||
|
@ -8,12 +8,25 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type { DatetimeChangeEventDetail, Components } from '@ionic/core/components';
|
import type { DatetimeChangeEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-datetime.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-datetime.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const DATETIME_INPUTS = [
|
const DATETIME_INPUTS = [
|
||||||
'cancelText',
|
'cancelText',
|
||||||
@ -48,11 +61,8 @@ const DATETIME_INPUTS = [
|
|||||||
'yearValues',
|
'yearValues',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
const DATETIME_METHODS = ['confirm', 'reset', 'cancel'];
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: DATETIME_INPUTS,
|
|
||||||
methods: ['confirm', 'reset', 'cancel'],
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-datetime',
|
selector: 'ion-datetime',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -68,15 +78,26 @@ const DATETIME_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonDatetime extends ValueAccessor {
|
export class IonDatetime extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionCancel', 'ionChange', 'ionFocus', 'ionBlur']);
|
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'])
|
@HostListener('ionChange', ['$event.target'])
|
||||||
handleIonChange(el: HTMLIonDatetimeElement): void {
|
handleIonChange(el: HTMLIonDatetimeElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type {
|
import type {
|
||||||
@ -17,7 +18,19 @@ import type {
|
|||||||
} from '@ionic/core/components';
|
} from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-input.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-input.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const INPUT_INPUTS = [
|
const INPUT_INPUTS = [
|
||||||
'accept',
|
'accept',
|
||||||
@ -59,11 +72,8 @@ const INPUT_INPUTS = [
|
|||||||
'value',
|
'value',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
const INPUT_METHODS = ['setFocus', 'getInputElement'];
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: INPUT_INPUTS,
|
|
||||||
methods: ['setFocus', 'getInputElement'],
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-input',
|
selector: 'ion-input',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -79,15 +89,26 @@ const INPUT_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonInput extends ValueAccessor {
|
export class IonInput extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionBlur', 'ionFocus']);
|
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'])
|
@HostListener('ionInput', ['$event.target'])
|
||||||
handleIonInput(el: HTMLIonInputElement): void {
|
handleIonInput(el: HTMLIonInputElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,19 +8,28 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type { RadioGroupChangeEventDetail, Components } from '@ionic/core/components';
|
import type { RadioGroupChangeEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-radio-group.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-radio-group.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const RADIO_GROUP_INPUTS = ['allowEmptySelection', 'name', 'value'];
|
const RADIO_GROUP_INPUTS = ['allowEmptySelection', 'name', 'value'];
|
||||||
|
|
||||||
@ProxyCmp({
|
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: RADIO_GROUP_INPUTS,
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-radio-group',
|
selector: 'ion-radio-group',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -36,15 +45,25 @@ const RADIO_GROUP_INPUTS = ['allowEmptySelection', 'name', 'value'];
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonRadioGroup extends ValueAccessor {
|
export class IonRadioGroup extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionChange']);
|
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'])
|
@HostListener('ionChange', ['$event.target'])
|
||||||
handleIonChange(el: HTMLIonRadioGroupElement): void {
|
handleIonChange(el: HTMLIonRadioGroupElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,19 +8,28 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type { Components } from '@ionic/core/components';
|
import type { Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-radio.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-radio.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const RADIO_INPUTS = ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'];
|
const RADIO_INPUTS = ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'];
|
||||||
|
|
||||||
@ProxyCmp({
|
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: RADIO_INPUTS,
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-radio',
|
selector: 'ion-radio',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -36,15 +45,25 @@ const RADIO_INPUTS = ['color', 'disabled', 'justify', 'labelPlacement', 'legacy'
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonRadio extends ValueAccessor {
|
export class IonRadio extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionFocus', 'ionBlur']);
|
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'])
|
@HostListener('ionSelect', ['$event.target'])
|
||||||
handleIonSelect(el: any): void {
|
handleIonSelect(el: any): void {
|
||||||
/**
|
/**
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type {
|
import type {
|
||||||
@ -18,7 +19,19 @@ import type {
|
|||||||
} from '@ionic/core/components';
|
} from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-range.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-range.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const RANGE_INPUTS = [
|
const RANGE_INPUTS = [
|
||||||
'activeBarStart',
|
'activeBarStart',
|
||||||
@ -41,10 +54,6 @@ const RANGE_INPUTS = [
|
|||||||
'value',
|
'value',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: RANGE_INPUTS,
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-range',
|
selector: 'ion-range',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -60,15 +69,25 @@ const RANGE_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonRange extends ValueAccessor {
|
export class IonRange extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionFocus', 'ionBlur', 'ionKnobMoveStart', 'ionKnobMoveEnd']);
|
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'])
|
@HostListener('ionChange', ['$event.target'])
|
||||||
handleIonChange(el: HTMLIonRangeElement): void {
|
handleIonChange(el: HTMLIonRangeElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,12 +8,25 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type { SearchbarInputEventDetail, SearchbarChangeEventDetail, Components } from '@ionic/core/components';
|
import type { SearchbarInputEventDetail, SearchbarChangeEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-searchbar.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-searchbar.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const SEARCHBAR_INPUTS = [
|
const SEARCHBAR_INPUTS = [
|
||||||
'animated',
|
'animated',
|
||||||
@ -38,11 +51,8 @@ const SEARCHBAR_INPUTS = [
|
|||||||
'value',
|
'value',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
const SEARCHBAR_METHODS = ['setFocus', 'getInputElement'];
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: SEARCHBAR_INPUTS,
|
|
||||||
methods: ['setFocus', 'getInputElement'],
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-searchbar',
|
selector: 'ion-searchbar',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -58,15 +68,26 @@ const SEARCHBAR_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonSearchbar extends ValueAccessor {
|
export class IonSearchbar extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionCancel', 'ionClear', 'ionBlur', 'ionFocus']);
|
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'])
|
@HostListener('ionInput', ['$event.target'])
|
||||||
handleIonInput(el: HTMLIonSearchbarElement): void {
|
handleIonInput(el: HTMLIonSearchbarElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,19 +8,28 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type { SegmentChangeEventDetail, Components } from '@ionic/core/components';
|
import type { SegmentChangeEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-segment.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-segment.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const SEGMENT_INPUTS = ['color', 'disabled', 'mode', 'scrollable', 'selectOnFocus', 'swipeGesture', 'value'];
|
const SEGMENT_INPUTS = ['color', 'disabled', 'mode', 'scrollable', 'selectOnFocus', 'swipeGesture', 'value'];
|
||||||
|
|
||||||
@ProxyCmp({
|
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: SEGMENT_INPUTS,
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-segment',
|
selector: 'ion-segment',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -36,15 +45,25 @@ const SEGMENT_INPUTS = ['color', 'disabled', 'mode', 'scrollable', 'selectOnFocu
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonSegment extends ValueAccessor {
|
export class IonSegment extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionChange']);
|
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'])
|
@HostListener('ionChange', ['$event.target'])
|
||||||
handleIonChange(el: HTMLIonSegmentElement): void {
|
handleIonChange(el: HTMLIonSegmentElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,12 +8,25 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type { SelectChangeEventDetail, Components } from '@ionic/core/components';
|
import type { SelectChangeEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-select.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-select.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const SELECT_INPUTS = [
|
const SELECT_INPUTS = [
|
||||||
'cancelText',
|
'cancelText',
|
||||||
@ -39,11 +52,8 @@ const SELECT_INPUTS = [
|
|||||||
'value',
|
'value',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
const SELECT_METHODS = ['open'];
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: SELECT_INPUTS,
|
|
||||||
methods: ['open'],
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-select',
|
selector: 'ion-select',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -59,15 +69,26 @@ const SELECT_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonSelect extends ValueAccessor {
|
export class IonSelect extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionChange', 'ionCancel', 'ionDismiss', 'ionFocus', 'ionBlur']);
|
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'])
|
@HostListener('ionChange', ['$event.target'])
|
||||||
handleIonChange(el: HTMLIonSelectElement): void {
|
handleIonChange(el: HTMLIonSelectElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,12 +8,25 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor } from '@ionic/angular/common';
|
import { ValueAccessor } from '@ionic/angular/common';
|
||||||
import type { TextareaChangeEventDetail, TextareaInputEventDetail, Components } from '@ionic/core/components';
|
import type { TextareaChangeEventDetail, TextareaInputEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-textarea.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-textarea.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const TEXTAREA_INPUTS = [
|
const TEXTAREA_INPUTS = [
|
||||||
'autoGrow',
|
'autoGrow',
|
||||||
@ -48,11 +61,8 @@ const TEXTAREA_INPUTS = [
|
|||||||
'wrap',
|
'wrap',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
const TEXTAREA_METHODS = ['setFocus', 'getInputElement'];
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: TEXTAREA_INPUTS,
|
|
||||||
methods: ['setFocus', 'getInputElement'],
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-textarea',
|
selector: 'ion-textarea',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -68,15 +78,26 @@ const TEXTAREA_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonTextarea extends ValueAccessor {
|
export class IonTextarea extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionChange', 'ionInput', 'ionBlur', 'ionFocus']);
|
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'])
|
@HostListener('ionInput', ['$event.target'])
|
||||||
handleIonInput(el: HTMLIonTextareaElement): void {
|
handleIonInput(el: HTMLIonTextareaElement): void {
|
||||||
this.handleValueChange(el, el.value);
|
this.handleValueChange(el, el.value);
|
||||||
|
@ -8,12 +8,25 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import type { OnInit } from '@angular/core';
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
||||||
import type { ToggleChangeEventDetail, Components } from '@ionic/core/components';
|
import type { ToggleChangeEventDetail, Components } from '@ionic/core/components';
|
||||||
import { defineCustomElement } from '@ionic/core/components/ion-toggle.js';
|
import { defineCustomElement } from '@ionic/core/components/ion-toggle.js';
|
||||||
|
|
||||||
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
const TOGGLE_INPUTS = [
|
const TOGGLE_INPUTS = [
|
||||||
'checked',
|
'checked',
|
||||||
@ -28,10 +41,6 @@ const TOGGLE_INPUTS = [
|
|||||||
'value',
|
'value',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ProxyCmp({
|
|
||||||
defineCustomElementFn: defineCustomElement,
|
|
||||||
inputs: TOGGLE_INPUTS,
|
|
||||||
})
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-toggle',
|
selector: 'ion-toggle',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
@ -47,15 +56,25 @@ const TOGGLE_INPUTS = [
|
|||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class IonToggle extends ValueAccessor {
|
export class IonToggle extends ValueAccessor implements OnInit {
|
||||||
protected el: HTMLElement;
|
protected el: HTMLElement;
|
||||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone, injector: Injector) {
|
||||||
super(injector, r);
|
super(injector, r);
|
||||||
|
defineCustomElement();
|
||||||
c.detach();
|
c.detach();
|
||||||
this.el = r.nativeElement;
|
this.el = r.nativeElement;
|
||||||
proxyOutputs(this, this.el, ['ionChange', 'ionFocus', 'ionBlur']);
|
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 {
|
writeValue(value: boolean): void {
|
||||||
this.elementRef.nativeElement.checked = this.lastValue = value;
|
this.elementRef.nativeElement.checked = this.lastValue = value;
|
||||||
setIonicClasses(this.elementRef);
|
setIonicClasses(this.elementRef);
|
||||||
|
@ -14,6 +14,18 @@ describe('Value Accessors', () => {
|
|||||||
cy.get('ion-checkbox').should('have.class', 'ion-dirty');
|
cy.get('ion-checkbox').should('have.class', 'ion-dirty');
|
||||||
cy.get('ion-checkbox').should('have.class', 'ion-valid');
|
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', () => {
|
describe('Input', () => {
|
||||||
@ -48,6 +60,10 @@ describe('Value Accessors', () => {
|
|||||||
cy.get('ion-input[formControlName="inputNumber"]').should('have.class', 'ion-valid');
|
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', () => {
|
describe('Radio Group', () => {
|
||||||
@ -63,8 +79,21 @@ describe('Value Accessors', () => {
|
|||||||
cy.get('ion-radio-group').should('have.class', 'ion-dirty');
|
cy.get('ion-radio-group').should('have.class', 'ion-dirty');
|
||||||
cy.get('ion-radio-group').should('have.class', 'ion-valid');
|
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', () => {
|
describe('Searchbar', () => {
|
||||||
beforeEach(() => cy.visit('/standalone/value-accessors/searchbar'));
|
beforeEach(() => cy.visit('/standalone/value-accessors/searchbar'));
|
||||||
|
|
||||||
@ -80,6 +109,10 @@ describe('Value Accessors', () => {
|
|||||||
cy.get('ion-searchbar').should('have.class', 'ion-dirty');
|
cy.get('ion-searchbar').should('have.class', 'ion-dirty');
|
||||||
cy.get('ion-searchbar').should('have.class', 'ion-valid');
|
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', () => {
|
describe('Segment', () => {
|
||||||
@ -95,6 +128,32 @@ describe('Value Accessors', () => {
|
|||||||
cy.get('ion-segment').should('have.class', 'ion-dirty');
|
cy.get('ion-segment').should('have.class', 'ion-dirty');
|
||||||
cy.get('ion-segment').should('have.class', 'ion-valid');
|
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', () => {
|
||||||
|
beforeEach(() => cy.visit('/standalone/value-accessors/select'));
|
||||||
|
|
||||||
|
it('should update the form value', () => {
|
||||||
|
cy.get('#formValue').should('have.text', JSON.stringify({ select: 'bananas' }, null, 2));
|
||||||
|
cy.get('ion-select').should('have.class', 'ion-pristine');
|
||||||
|
|
||||||
|
cy.get('ion-select').click();
|
||||||
|
cy.get('ion-popover').should('be.visible');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-radio-group ion-radio').first().click();
|
||||||
|
|
||||||
|
cy.get('#formValue').should('have.text', JSON.stringify({ select: 'apples' }, null, 2));
|
||||||
|
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', () => {
|
describe('Textarea', () => {
|
||||||
@ -112,6 +171,10 @@ describe('Value Accessors', () => {
|
|||||||
cy.get('ion-textarea').should('have.class', 'ion-dirty');
|
cy.get('ion-textarea').should('have.class', 'ion-dirty');
|
||||||
cy.get('ion-textarea').should('have.class', 'ion-valid');
|
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', () => {
|
describe('Toggle', () => {
|
||||||
@ -127,6 +190,10 @@ describe('Value Accessors', () => {
|
|||||||
cy.get('ion-toggle').should('have.class', 'ion-dirty');
|
cy.get('ion-toggle').should('have.class', 'ion-dirty');
|
||||||
cy.get('ion-toggle').should('have.class', 'ion-valid');
|
cy.get('ion-toggle').should('have.class', 'ion-valid');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should proxy inputs on load', () => {
|
||||||
|
cy.get('ion-toggle').should('have.prop', 'color', 'danger');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -37,6 +37,7 @@ export const routes: Routes = [
|
|||||||
{ path: 'range', loadComponent: () => import('../value-accessors/range/range.component').then(c => c.RangeComponent) },
|
{ path: 'range', loadComponent: () => import('../value-accessors/range/range.component').then(c => c.RangeComponent) },
|
||||||
{ path: 'searchbar', loadComponent: () => import('../value-accessors/searchbar/searchbar.component').then(c => c.SearchbarComponent) },
|
{ path: 'searchbar', loadComponent: () => import('../value-accessors/searchbar/searchbar.component').then(c => c.SearchbarComponent) },
|
||||||
{ path: 'segment', loadComponent: () => import('../value-accessors/segment/segment.component').then(c => c.SegmentComponent) },
|
{ path: 'segment', loadComponent: () => import('../value-accessors/segment/segment.component').then(c => c.SegmentComponent) },
|
||||||
|
{ path: 'select', loadComponent: () => import('../value-accessors/select/select.component').then(c => c.SelectComponent) },
|
||||||
{ path: 'textarea', loadComponent: () => import('../value-accessors/textarea/textarea.component').then(c => c.TextareaComponent) },
|
{ path: 'textarea', loadComponent: () => import('../value-accessors/textarea/textarea.component').then(c => c.TextareaComponent) },
|
||||||
{ path: 'toggle', loadComponent: () => import('../value-accessors/toggle/toggle.component').then(c => c.ToggleComponent) },
|
{ path: 'toggle', loadComponent: () => import('../value-accessors/toggle/toggle.component').then(c => c.ToggleComponent) },
|
||||||
{ path: '**', redirectTo: 'checkbox' }
|
{ path: '**', redirectTo: 'checkbox' }
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-checkbox formControlName="checkbox"></ion-checkbox>
|
<ion-checkbox color="danger" formControlName="checkbox"></ion-checkbox>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-datetime formControlName="datetime"></ion-datetime>
|
<ion-datetime color="danger" formControlName="datetime"></ion-datetime>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-input label="String" formControlName="inputString"></ion-input>
|
<ion-input color="danger" label="String" formControlName="inputString"></ion-input>
|
||||||
<ion-input label="Number" type="number" formControlName="inputNumber"></ion-input>
|
<ion-input label="Number" type="number" formControlName="inputNumber"></ion-input>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-radio-group formControlName="radioGroup">
|
<ion-radio-group formControlName="radioGroup">
|
||||||
<ion-radio value="1">One</ion-radio>
|
<ion-radio color="danger" value="1">One</ion-radio>
|
||||||
<ion-radio value="2">Two</ion-radio>
|
<ion-radio value="2">Two</ion-radio>
|
||||||
</ion-radio-group>
|
</ion-radio-group>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
This test checks the form integrations with ion-range to make sure values are correctly assigned to the form group.
|
This test checks the form integrations with ion-range to make sure values are correctly assigned to the form group.
|
||||||
</p>
|
</p>
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-range formControlName="range"></ion-range>
|
<ion-range color="danger" formControlName="range"></ion-range>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
group.
|
group.
|
||||||
</p>
|
</p>
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-searchbar label="String" formControlName="searchbar"></ion-searchbar>
|
<ion-searchbar color="danger" label="String" formControlName="searchbar"></ion-searchbar>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-segment formControlName="segment">
|
<ion-segment color="danger" formControlName="segment">
|
||||||
<ion-segment-button value="Paid">
|
<ion-segment-button value="Paid">
|
||||||
<ion-label>Paid</ion-label>
|
<ion-label>Paid</ion-label>
|
||||||
</ion-segment-button>
|
</ion-segment-button>
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
<div>
|
||||||
|
<h1>IonSelect Value Accessors</h1>
|
||||||
|
<p>
|
||||||
|
This test checks the form integrations with ion-select to make sure values are correctly assigned to the form
|
||||||
|
group.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<app-value-accessor-test [formGroup]="form">
|
||||||
|
<ion-select color="danger" label="Favorite Fruit" formControlName="select" interface="popover">
|
||||||
|
<ion-select-option value="apples">Apples</ion-select-option>
|
||||||
|
<ion-select-option value="bananas">Bananas</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
</app-value-accessor-test>
|
||||||
|
</div>
|
@ -0,0 +1,26 @@
|
|||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
|
import { IonSelect, IonSelectOption } from "@ionic/angular/standalone";
|
||||||
|
import { ValueAccessorTestComponent } from "../value-accessor-test/value-accessor-test.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-select',
|
||||||
|
templateUrl: 'select.component.html',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
IonSelect,
|
||||||
|
IonSelectOption,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormsModule,
|
||||||
|
ValueAccessorTestComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SelectComponent {
|
||||||
|
|
||||||
|
form = this.fb.group({
|
||||||
|
select: ['bananas', Validators.required],
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) { }
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,6 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-textarea label="String" formControlName="textarea"></ion-textarea>
|
<ion-textarea color="danger" label="String" formControlName="textarea"></ion-textarea>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<app-value-accessor-test [formGroup]="form">
|
<app-value-accessor-test [formGroup]="form">
|
||||||
<ion-toggle formControlName="toggle"></ion-toggle>
|
<ion-toggle color="danger" formControlName="toggle"></ion-toggle>
|
||||||
</app-value-accessor-test>
|
</app-value-accessor-test>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user