refactor(angular): apply range to numeric value accessor (#29029)

Issue number: Internal

---------

<!-- 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. -->

The ticket describes renaming `TextValueAccessorDirective` since
`ion-range` is not a text-based control, however I think this was an
incorrect assumption made during the original implementation.

`ion-range` is a numeric based value accessor (either as a single number
or an object accepting a numeric start/end value).

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Migrates the usage of `ion-range` value accessor implementation to the
`NumericValueAccessorDirective`
- Adds tests for validating the value accessor is functioning as
expected

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

---------

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
This commit is contained in:
Sean Perkins
2024-02-15 17:39:26 -05:00
committed by GitHub
parent 58c795f315
commit 308f396389
5 changed files with 24 additions and 11 deletions

View File

@ -3,7 +3,7 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from '@ionic/angular/common'; import { ValueAccessor } from '@ionic/angular/common';
@Directive({ @Directive({
selector: 'ion-input[type=number]', selector: 'ion-input[type=number],ion-range',
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
@ -13,18 +13,22 @@ import { ValueAccessor } from '@ionic/angular/common';
], ],
}) })
export class NumericValueAccessorDirective extends ValueAccessor { export class NumericValueAccessorDirective extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) { constructor(injector: Injector, private el: ElementRef<HTMLInputElement | HTMLIonRangeElement>) {
super(injector, el); super(injector, el);
} }
@HostListener('ionInput', ['$event.target']) @HostListener('ionInput', ['$event.target'])
handleInputEvent(el: HTMLIonInputElement): void { handleInputEvent(el: HTMLIonInputElement | HTMLIonRangeElement): void {
this.handleValueChange(el, el.value); this.handleValueChange(el, el.value);
} }
registerOnChange(fn: (_: number | null) => void): void { registerOnChange(fn: (_: number | null) => void): void {
if (this.el.nativeElement.tagName === 'ION-INPUT') {
super.registerOnChange((value: string) => { super.registerOnChange((value: string) => {
fn(value === '' ? null : parseFloat(value)); fn(value === '' ? null : parseFloat(value));
}); });
} else {
super.registerOnChange(fn);
}
} }
} }

View File

@ -2,9 +2,8 @@ import { ElementRef, Injector, Directive, HostListener } 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';
// TODO(FW-5495): rename class since range isn't a text component
@Directive({ @Directive({
selector: 'ion-input:not([type=number]),ion-textarea,ion-searchbar,ion-range', selector: 'ion-input:not([type=number]),ion-textarea,ion-searchbar',
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
@ -19,9 +18,7 @@ export class TextValueAccessorDirective extends ValueAccessor {
} }
@HostListener('ionInput', ['$event.target']) @HostListener('ionInput', ['$event.target'])
_handleInputEvent( _handleInputEvent(el: HTMLIonInputElement | HTMLIonTextareaElement | HTMLIonSearchbarElement): void {
el: HTMLIonInputElement | HTMLIonTextareaElement | HTMLIonSearchbarElement | HTMLIonRangeElement
): void {
this.handleValueChange(el, el.value); this.handleValueChange(el, el.value);
} }
} }

View File

@ -10,6 +10,7 @@ describe('Inputs', () => {
cy.get('ion-input').should('have.prop', 'value').and('equal', 'some text'); cy.get('ion-input').should('have.prop', 'value').and('equal', 'some text');
cy.get('ion-datetime').should('have.prop', 'value').and('equal', '1994-03-15'); cy.get('ion-datetime').should('have.prop', 'value').and('equal', '1994-03-15');
cy.get('ion-select').should('have.prop', 'value').and('equal', 'nes'); cy.get('ion-select').should('have.prop', 'value').and('equal', 'nes');
cy.get('ion-range').should('have.prop', 'value').and('equal', 50);
}); });
it('should have reset value', () => { it('should have reset value', () => {
@ -27,6 +28,7 @@ describe('Inputs', () => {
cy.get('ion-input').should('not.have.prop', 'value'); cy.get('ion-input').should('not.have.prop', 'value');
cy.get('ion-datetime').should('not.have.prop', 'value'); cy.get('ion-datetime').should('not.have.prop', 'value');
cy.get('ion-select').should('not.have.prop', 'value'); cy.get('ion-select').should('not.have.prop', 'value');
cy.get('ion-range').should('not.have.prop', 'value');
}); });
it('should get some value', () => { it('should get some value', () => {
@ -39,6 +41,7 @@ describe('Inputs', () => {
cy.get('ion-input').should('have.prop', 'value').and('equal', 'some text'); cy.get('ion-input').should('have.prop', 'value').and('equal', 'some text');
cy.get('ion-datetime').should('have.prop', 'value').and('equal', '1994-03-15'); cy.get('ion-datetime').should('have.prop', 'value').and('equal', '1994-03-15');
cy.get('ion-select').should('have.prop', 'value').and('equal', 'nes'); cy.get('ion-select').should('have.prop', 'value').and('equal', 'nes');
cy.get('ion-range').should('have.prop', 'value').and('equal', 50);
}); });
it('change values should update angular', () => { it('change values should update angular', () => {

View File

@ -99,6 +99,12 @@
<ion-note slot="end">{{radio}}</ion-note> <ion-note slot="end">{{radio}}</ion-note>
</ion-item> </ion-item>
<ion-item>
<ion-range [(ngModel)]="range" min="0" max="100" id="first-range">
<ion-label slot="start">Range</ion-label>
</ion-range>
</ion-item>
</ion-list> </ion-list>
<p> <p>
<ion-button (click)="setValues()" id="set-button">Set values</ion-button> <ion-button (click)="setValues()" id="set-button">Set values</ion-button>

View File

@ -13,6 +13,7 @@ export class InputsComponent {
toggle = true; toggle = true;
select? = 'nes'; select? = 'nes';
changes = 0; changes = 0;
range? = 50;
setValues() { setValues() {
console.log('set values'); console.log('set values');
@ -22,6 +23,7 @@ export class InputsComponent {
this.radio = 'nes'; this.radio = 'nes';
this.toggle = true; this.toggle = true;
this.select = 'nes'; this.select = 'nes';
this.range = 50;
} }
resetValues() { resetValues() {
@ -32,6 +34,7 @@ export class InputsComponent {
this.radio = undefined; this.radio = undefined;
this.toggle = false; this.toggle = false;
this.select = undefined; this.select = undefined;
this.range = undefined;
} }
counter() { counter() {