Skip to content

Commit

Permalink
fix(forms): the min and max validators should work correctly with…
Browse files Browse the repository at this point in the history
… `0` as a value

Prior to this change the `min` and `max` validator directives would not
set the `min` and `max` attributes on the host element. The problem was
caused by the truthy check in host binding expression that was
calculated as `false` when `0` is used as a value. This commit updates
the logic to leverage nullish coalescing operator in these host binding
expressions, so `0` is treated as a valid value, thus the `min` and
`max` attributes are set correctly.

Partially closes #42267
  • Loading branch information
iRealNirmal committed Jun 2, 2021
1 parent 62b5a6c commit 44e54d1
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 2 deletions.
4 changes: 2 additions & 2 deletions packages/forms/src/directives/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export const MAX_VALIDATOR: StaticProvider = {
selector:
'input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]',
providers: [MAX_VALIDATOR],
host: {'[attr.max]': 'max ? max : null'}
host: {'[attr.max]': 'max ?? null'}
})
export class MaxValidator extends AbstractValidatorDirective implements OnChanges {
/**
Expand Down Expand Up @@ -227,7 +227,7 @@ export const MIN_VALIDATOR: StaticProvider = {
selector:
'input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]',
providers: [MIN_VALIDATOR],
host: {'[attr.min]': 'min ? min : null'}
host: {'[attr.min]': 'min ?? null'}
})
export class MinValidator extends AbstractValidatorDirective implements OnChanges {
/**
Expand Down
37 changes: 37 additions & 0 deletions packages/forms/test/reactive_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2558,8 +2558,23 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');
fixture.componentInstance.max = 1;
fixture.detectChanges();

expect(input.getAttribute('max')).toEqual('1');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({max: {max: 1, actual: 2}});

fixture.componentInstance.min = 0;
fixture.componentInstance.max = 0;
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('0');
expect(input.getAttribute('max')).toEqual('0');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({max: {max: 0, actual: 2}});

input.value = 0;
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(form.valid).toBeTruthy();
expect(form.controls.pin.errors).toBeNull();
});

it('should validate max for float number', () => {
Expand All @@ -2573,6 +2588,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');
const input = fixture.debugElement.query(By.css('input')).nativeElement;
const form = fixture.componentInstance.form;

expect(input.getAttribute('max')).toEqual('10.35');
expect(input.value).toEqual('10.25');
expect(form.valid).toBeTruthy();
expect(form.controls.pin.errors).toBeNull();
Expand All @@ -2586,6 +2602,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');
fixture.componentInstance.max = 10.05;
fixture.detectChanges();

expect(input.getAttribute('max')).toEqual('10.05');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({max: {max: 10.05, actual: 10.15}});

Expand Down Expand Up @@ -2618,6 +2635,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');

fixture.componentInstance.max = 1;
fixture.detectChanges();
expect(input.getAttribute('max')).toEqual('1');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({max: {max: 1, actual: 2}});
});
Expand All @@ -2644,8 +2662,23 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');

fixture.componentInstance.min = 5;
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('5');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({min: {min: 5, actual: 2}});

fixture.componentInstance.min = 0;
input.value = -5;
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('0');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({min: {min: 0, actual: -5}});

input.value = 0;
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(form.valid).toBeTruthy();
expect(form.controls.pin.errors).toBeNull();
});

it('should validate min for float number', () => {
Expand All @@ -2660,6 +2693,8 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');
const input = fixture.debugElement.query(By.css('input')).nativeElement;
const form = fixture.componentInstance.form;

expect(input.getAttribute('min')).toEqual('10.25');
expect(input.getAttribute('max')).toEqual('10.5');
expect(input.value).toEqual('10.25');
expect(form.valid).toBeTruthy();
expect(form.controls.pin.errors).toBeNull();
Expand All @@ -2672,6 +2707,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');

fixture.componentInstance.min = 10.40;
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('10.4');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({min: {min: 10.40, actual: 10.35}});

Expand Down Expand Up @@ -2704,6 +2740,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]');

fixture.componentInstance.min = 5;
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('5');
expect(form.valid).toBeFalse();
expect(form.controls.pin.errors).toEqual({min: {min: 5, actual: 2}});
});
Expand Down
37 changes: 37 additions & 0 deletions packages/forms/test/template_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
input.value = '';
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('max')).toEqual('10');
expect(form.valid).toEqual(true);
expect(form.controls.max.errors).toBeNull();

Expand All @@ -1538,6 +1539,21 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
fixture.detectChanges();
expect(form.valid).toEqual(true);
expect(form.controls.max.errors).toBeNull();

fixture.componentInstance.max = 0;
fixture.detectChanges();
tick();
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('max')).toEqual('0');
expect(form.valid).toEqual(false);
expect(form.controls.max.errors).toEqual({max: {max: 0, actual: 9}});

input.value = 0;
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(form.valid).toEqual(true);
expect(form.controls.max.errors).toBeNull();
}));

it('should validate max for float number', fakeAsync(() => {
Expand All @@ -1552,6 +1568,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
input.value = '';
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('max')).toEqual('10.25');
expect(form.valid).toEqual(true);
expect(form.controls.max.errors).toBeNull();

Expand Down Expand Up @@ -1586,6 +1603,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
input.value = '11';
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('max')).toEqual('10');
expect(form.valid).toEqual(false);
expect(form.controls.max.errors).toEqual({max: {max: 10, actual: 11}});

Expand Down Expand Up @@ -1635,6 +1653,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
input.value = '';
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('10');
expect(form.valid).toEqual(true);
expect(form.controls.min.errors).toBeNull();

Expand All @@ -1649,6 +1668,22 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
fixture.detectChanges();
expect(form.valid).toEqual(false);
expect(form.controls.min.errors).toEqual({min: {min: 10, actual: 9}});

fixture.componentInstance.min = 0;
fixture.detectChanges();
tick();
input.value = -5;
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('0');
expect(form.valid).toEqual(false);
expect(form.controls.min.errors).toEqual({min: {min: 0, actual: -5}});

input.value = 0;
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(form.valid).toEqual(true);
expect(form.controls.min.errors).toBeNull();
}));

it('should validate min for float number', fakeAsync(() => {
Expand All @@ -1663,6 +1698,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
input.value = '';
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('10.25');
expect(form.valid).toEqual(true);
expect(form.controls.min.errors).toBeNull();

Expand Down Expand Up @@ -1696,6 +1732,7 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
input.value = '11';
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(input.getAttribute('min')).toEqual('10');
expect(form.valid).toEqual(true);
expect(form.controls.min.errors).toBeNull();

Expand Down

0 comments on commit 44e54d1

Please sign in to comment.