Skip to content

Commit 50a4157

Browse files
committed
Support dynamic change of sync validators
1 parent 3ddaf36 commit 50a4157

File tree

13 files changed

+268
-36
lines changed

13 files changed

+268
-36
lines changed

README.md

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ngx-numeric-range-form-field
22

3-
An Angular Material UI numeric range input form field. It is based on custom form field control and control value accessor which allows inserting minimum number and maximum number of some range.
3+
An Angular Material UI numeric range input form field. It is based on custom form field control and control value accessor which allows inserting range numbers, minimum and maximum.
44

55
![Numeric range form field](https://github.com/dineeek/ngx-numeric-range-form-field/blob/main/ngx-numeric-range-form-field/Numeric%20Range%20Form%20Field.png)
66

@@ -48,7 +48,10 @@ form: FormGroup;
4848

4949
constructor() {
5050
this.form = new FormGroup({
51-
range: new FormControl(null, [
51+
range: new FormControl({
52+
minimum: 10,
53+
maximum: 100,
54+
}, [
5255
Validators.required, //optional
5356
Validators.min(10), //optional
5457
Validators.max(100), //optional
@@ -81,13 +84,15 @@ Customizable input and output decorators:
8184
@Input() floatLabel: 'always' | 'never' | 'auto' = 'always';
8285
@Input() minPlaceholder = 'From'; // Placeholder of the minimum value control
8386
@Input() maxPlaceholder = 'To'; // Placeholder of the maximum value control
84-
@Input() readonly = false; // Indicator wether the both controls are readonly
85-
@Input() resettable = true; // Indicator wether the both controls are resettable
86-
@Input() required: boolean; // Required validation
87+
@Input() readonly = false; // Flag for readonly both controls
88+
@Input() minReadonly = false; // Flag for readonly minimum control
89+
@Input() maxReadonly = false; // Flag for readonly maximum control
90+
@Input() resettable = true; // Flag for resetting controls value
8791
@Input() requiredErrorMessage = 'Field is required!'; // Customizable error message when field is required
8892
@Input() minimumErrorMessage = 'Minimum has been reached!'; // Customizable error message when field has min validation
8993
@Input() maximumErrorMessage = 'Maximum has exceeded!'; // Customizable error message when field has max validation
9094
@Input() invalidRangeErrorMessage = 'Inserted range is not valid!'; // Customizable error message when field has invalid numeric range
95+
@Input() dynamicSyncValidators: ValidatorFn | ValidatorFn[]; // Dynamic change of sync validators
9196

9297
@Output() blurred = new EventEmitter<void>(); // Event which emits where user leaves control (focus out)
9398
@Output() enterPressed = new EventEmitter<void>(); // Event which emits when enter is pressed
@@ -107,4 +112,4 @@ export interface INumericRange {
107112

108113
Apache License
109114

110-
Copyright (c) 2021 Dino Klicek
115+
Copyright (c) 2022 Dino Klicek

ngx-numeric-range-form-field/README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ Customizable input and output decorators:
8484
@Input() floatLabel: 'always' | 'never' | 'auto' = 'always';
8585
@Input() minPlaceholder = 'From'; // Placeholder of the minimum value control
8686
@Input() maxPlaceholder = 'To'; // Placeholder of the maximum value control
87-
@Input() readonly = false; // Indicator wether the both controls are readonly
88-
@Input() minReadonly = false; // Indicator wether the minimum control is readonly
89-
@Input() maxReadonly = false; // Indicator wether the maximum control is readonly
90-
@Input() resettable = true; // Indicator wether the both controls are resettable
91-
@Input() required: boolean; // Required validation
87+
@Input() readonly = false; // Flag for readonly both controls
88+
@Input() minReadonly = false; // Flag for readonly minimum control
89+
@Input() maxReadonly = false; // Flag for readonly maximum control
90+
@Input() resettable = true; // Flag for resetting controls value
9291
@Input() requiredErrorMessage = 'Field is required!'; // Customizable error message when field is required
9392
@Input() minimumErrorMessage = 'Minimum has been reached!'; // Customizable error message when field has min validation
9493
@Input() maximumErrorMessage = 'Maximum has exceeded!'; // Customizable error message when field has max validation
9594
@Input() invalidRangeErrorMessage = 'Inserted range is not valid!'; // Customizable error message when field has invalid numeric range
95+
@Input() dynamicSyncValidators: ValidatorFn | ValidatorFn[]; // Dynamic change of sync validators
9696

9797
@Output() blurred = new EventEmitter<void>(); // Event which emits where user leaves control (focus out)
9898
@Output() enterPressed = new EventEmitter<void>(); // Event which emits when enter is pressed
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,107 @@
1-
<ngx-numeric-range-form-field
2-
[formControl]="rangeControl"
3-
label="Numeric range"
4-
(blurred)="onBlur()"
5-
(enterPressed)="onEnter()"
6-
(numericRangeChanged)="onValueChange($event)"
7-
[minReadonly]="true"
8-
></ngx-numeric-range-form-field>
1+
<form [formGroup]="form">
2+
<ngx-numeric-range-form-field
3+
formControlName="range"
4+
label="Numeric range"
5+
(blurred)="onBlur()"
6+
(enterPressed)="onEnter()"
7+
(numericRangeChanged)="onValueChange($event)"
8+
[readonly]="readonly"
9+
[minReadonly]="minReadonly"
10+
[maxReadonly]="maxReadonly"
11+
[disabled]="disabled"
12+
[resettable]="resettable"
13+
[dynamicSyncValidators]="dynamicSyncValidators | async"
14+
></ngx-numeric-range-form-field>
15+
16+
<h3>Current value:</h3>
17+
<p>{{ rangeControl.value | json }}</p>
18+
19+
<div class="options">
20+
<h3>State options:</h3>
21+
<mat-slide-toggle (change)="onDisabled($event)">Disabled</mat-slide-toggle>
22+
23+
<h3>Validation options:</h3>
24+
<mat-slide-toggle (change)="onRequiredChange($event)"
25+
>Required</mat-slide-toggle
26+
>
27+
28+
<div class="options__group">
29+
<mat-form-field appearance="legacy">
30+
<mat-label>Minimum</mat-label>
31+
<input formControlName="minimumOption" matInput placeholder="Minimum" />
32+
</mat-form-field>
33+
34+
<mat-form-field appearance="legacy">
35+
<mat-label>Maximum</mat-label>
36+
<input formControlName="maximumOption" matInput placeholder="Maximum" />
37+
</mat-form-field>
38+
</div>
39+
40+
<h3>Readonly options:</h3>
41+
<div class="options__group">
42+
<mat-slide-toggle (change)="readonly = $event.checked"
43+
>Readonly</mat-slide-toggle
44+
>
45+
46+
<mat-slide-toggle (change)="minReadonly = $event.checked"
47+
>Min</mat-slide-toggle
48+
>
49+
<mat-slide-toggle (change)="maxReadonly = $event.checked"
50+
>Max</mat-slide-toggle
51+
>
52+
</div>
53+
54+
<h3>Reset option:</h3>
55+
<mat-slide-toggle (change)="resettable = $event.checked"
56+
>Resettable</mat-slide-toggle
57+
>
58+
</div>
59+
60+
<h3>Legacy appearance:</h3>
61+
<ngx-numeric-range-form-field
62+
appearance="legacy"
63+
formControlName="range"
64+
label="Numeric range"
65+
(blurred)="onBlur()"
66+
(enterPressed)="onEnter()"
67+
(numericRangeChanged)="onValueChange($event)"
68+
[readonly]="readonly"
69+
[minReadonly]="minReadonly"
70+
[maxReadonly]="maxReadonly"
71+
[disabled]="disabled"
72+
[resettable]="resettable"
73+
[dynamicSyncValidators]="dynamicSyncValidators | async"
74+
></ngx-numeric-range-form-field>
75+
76+
<h3>Standard appearance:</h3>
77+
<ngx-numeric-range-form-field
78+
appearance="standard"
79+
formControlName="range"
80+
label="Numeric range"
81+
(blurred)="onBlur()"
82+
(enterPressed)="onEnter()"
83+
(numericRangeChanged)="onValueChange($event)"
84+
[readonly]="readonly"
85+
[minReadonly]="minReadonly"
86+
[maxReadonly]="maxReadonly"
87+
[disabled]="disabled"
88+
[resettable]="resettable"
89+
[dynamicSyncValidators]="dynamicSyncValidators | async"
90+
></ngx-numeric-range-form-field>
91+
92+
<h3>Fill appearance:</h3>
93+
<ngx-numeric-range-form-field
94+
appearance="fill"
95+
formControlName="range"
96+
label="Numeric range"
97+
(blurred)="onBlur()"
98+
(enterPressed)="onEnter()"
99+
(numericRangeChanged)="onValueChange($event)"
100+
[readonly]="readonly"
101+
[minReadonly]="minReadonly"
102+
[maxReadonly]="maxReadonly"
103+
[disabled]="disabled"
104+
[resettable]="resettable"
105+
[dynamicSyncValidators]="dynamicSyncValidators | async"
106+
></ngx-numeric-range-form-field>
107+
</form>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.options {
2+
display: flex;
3+
flex-direction: column;
4+
width: fit-content;
5+
row-gap: 10px;
6+
7+
&__group {
8+
display: flex;
9+
flex-direction: row;
10+
column-gap: 20px;
11+
}
12+
13+
h3 {
14+
margin-bottom: 0;
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,71 @@
1-
import { Component } from '@angular/core';
2-
import { FormControl, FormGroup, Validators } from '@angular/forms';
1+
import { Component, OnInit } from '@angular/core';
2+
import {
3+
FormControl,
4+
FormGroup,
5+
ValidatorFn,
6+
Validators,
7+
} from '@angular/forms';
8+
import {
9+
FloatLabelType,
10+
MatFormFieldAppearance,
11+
} from '@angular/material/form-field';
12+
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
313
import { INumericRange } from 'projects/ngx-numeric-range-form-field/src/lib/form/model/numeric-range-field.model';
14+
import { combineLatest, merge, Subject, zip } from 'rxjs';
15+
import { startWith, withLatestFrom } from 'rxjs/operators';
416

517
@Component({
618
selector: 'app-root',
719
templateUrl: './app.component.html',
820
styleUrls: ['./app.component.scss'],
921
})
10-
export class AppComponent {
22+
export class AppComponent implements OnInit {
1123
form: FormGroup;
1224

25+
dynamicSyncValidators = new Subject<ValidatorFn | ValidatorFn[]>();
26+
27+
readonly = false;
28+
minReadonly = false;
29+
maxReadonly = false;
30+
disabled = false;
31+
resettable = false;
32+
1333
constructor() {
1434
this.form = new FormGroup({
1535
range: new FormControl(
1636
{
1737
minimum: 10,
1838
maximum: 100,
1939
} as INumericRange,
20-
[Validators.required, Validators.min(10), Validators.max(100)]
40+
[Validators.min(10), Validators.max(100)]
2141
),
42+
minimumOption: new FormControl(10, { updateOn: 'blur' }),
43+
maximumOption: new FormControl(100, { updateOn: 'blur' }),
2244
});
2345
}
2446

2547
get rangeControl(): FormControl {
2648
return this.form.get('range') as FormControl;
2749
}
2850

51+
get minimumOptionControl(): FormControl {
52+
return this.form.get('minimumOption') as FormControl;
53+
}
54+
55+
get maximumOptionControl(): FormControl {
56+
return this.form.get('maximumOption') as FormControl;
57+
}
58+
59+
ngOnInit(): void {
60+
this.listenValidationOptionsChange();
61+
}
62+
2963
onBlur(): void {
3064
console.log('Value', this.rangeControl.value);
3165
}
3266

3367
onEnter(): void {
3468
console.log('Enter pressed!');
35-
this.rangeControl.disable();
3669
}
3770

3871
onValueChange(value: INumericRange): void {
@@ -41,8 +74,41 @@ export class AppComponent {
4174
value,
4275
this.rangeControl.hasError('notValidRange')
4376
);
44-
45-
//this.rangeControl.updateValueAndValidity();
4677
console.log('RANGE CONTROL', this.rangeControl);
4778
}
79+
80+
onRequiredChange(e: MatSlideToggleChange): void {
81+
if (e.checked) {
82+
this.dynamicSyncValidators.next([
83+
Validators.required,
84+
Validators.min(this.minimumOptionControl.value),
85+
Validators.max(this.maximumOptionControl.value),
86+
]);
87+
} else {
88+
this.dynamicSyncValidators.next([
89+
Validators.min(this.minimumOptionControl.value),
90+
Validators.max(this.maximumOptionControl.value),
91+
]);
92+
}
93+
94+
this.form.updateValueAndValidity();
95+
}
96+
97+
onDisabled(e: MatSlideToggleChange): void {
98+
e.checked ? this.rangeControl.disable() : this.rangeControl.enable();
99+
}
100+
101+
private listenValidationOptionsChange(): void {
102+
merge(
103+
this.minimumOptionControl.valueChanges,
104+
this.maximumOptionControl.valueChanges
105+
).subscribe(() => {
106+
this.dynamicSyncValidators.next([
107+
Validators.min(this.minimumOptionControl.value),
108+
Validators.max(this.maximumOptionControl.value),
109+
]);
110+
111+
this.form.updateValueAndValidity();
112+
});
113+
}
48114
}

ngx-numeric-range-form-field/projects/ngx-numeric-range-form-field-demo/src/app/app.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
77
import { MatIconModule } from '@angular/material/icon';
88
import { MatInputModule } from '@angular/material/input';
99
import { NgxNumericRangeFormFieldModule } from 'projects/ngx-numeric-range-form-field/src/lib/ngx-numeric-range-form-field.module';
10+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
1011

1112
@NgModule({
1213
declarations: [AppComponent],
@@ -18,6 +19,7 @@ import { NgxNumericRangeFormFieldModule } from 'projects/ngx-numeric-range-form-
1819
MatFormFieldModule,
1920
MatInputModule,
2021
MatIconModule,
22+
MatSlideToggleModule,
2123
],
2224
providers: [],
2325
bootstrap: [AppComponent],

ngx-numeric-range-form-field/projects/ngx-numeric-range-form-field/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### 2.3.0
2+
3+
Added support for dynamic change of sync validators
4+
15
### 2.2.0
26

37
Added readonly for every input.

ngx-numeric-range-form-field/projects/ngx-numeric-range-form-field/README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ Customizable input and output decorators:
8484
@Input() floatLabel: 'always' | 'never' | 'auto' = 'always';
8585
@Input() minPlaceholder = 'From'; // Placeholder of the minimum value control
8686
@Input() maxPlaceholder = 'To'; // Placeholder of the maximum value control
87-
@Input() readonly = false; // Indicator wether the both controls are readonly
88-
@Input() minReadonly = false; // Indicator wether the minimum control is readonly
89-
@Input() maxReadonly = false; // Indicator wether the maximum control is readonly
90-
@Input() resettable = true; // Indicator wether the both controls are resettable
91-
@Input() required: boolean; // Required validation
87+
@Input() readonly = false; // Flag for readonly both controls
88+
@Input() minReadonly = false; // Flag for readonly minimum control
89+
@Input() maxReadonly = false; // Flag for readonly maximum control
90+
@Input() resettable = true; // Flag for resetting controls value
9291
@Input() requiredErrorMessage = 'Field is required!'; // Customizable error message when field is required
9392
@Input() minimumErrorMessage = 'Minimum has been reached!'; // Customizable error message when field has min validation
9493
@Input() maximumErrorMessage = 'Maximum has exceeded!'; // Customizable error message when field has max validation
9594
@Input() invalidRangeErrorMessage = 'Inserted range is not valid!'; // Customizable error message when field has invalid numeric range
95+
@Input() dynamicSyncValidators: ValidatorFn | ValidatorFn[]; // Dynamic change of sync validators
9696

9797
@Output() blurred = new EventEmitter<void>(); // Event which emits where user leaves control (focus out)
9898
@Output() enterPressed = new EventEmitter<void>(); // Event which emits when enter is pressed

ngx-numeric-range-form-field/projects/ngx-numeric-range-form-field/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngx-numeric-range-form-field",
3-
"version": "2.2.0",
3+
"version": "2.3.0",
44
"peerDependencies": {
55
"@angular/common": "^13.0.0",
66
"@angular/core": "^13.0.0"

ngx-numeric-range-form-field/projects/ngx-numeric-range-form-field/src/lib/container/numeric-range-form-field-container.component.html

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
(blurred)="onBlur()"
1717
(enterPressed)="onEnterPressed()"
1818
(numericRangeChanged)="onRangeValuesChanged($event)"
19+
[dynamicSyncValidators]="dynamicSyncValidators"
1920
></ngx-numeric-range-form-field-control>
2021

2122
<mat-icon

0 commit comments

Comments
 (0)