Reputation: 1699
I made a custom form control for input a number :
@Component({
selector: 'number-input',
templateUrl: './number-input.component.html',
styleUrls: ['./number-input.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NumberInputComponent),
multi: true
}
]
})
export class NumberInputComponent implements ControlValueAccessor {
... }
This one works great. And i used it for making a TimePicker with reactive forms:
<number-input [max]="23" formControlName="startTimeHours"></number-input>
<number-input [max]="45" [step]="15" formControlName="startTimeMinutes"></number-input>
I would like to encapsulate this timepicker in a single custom form control. Is it posible? How?
Upvotes: 0
Views: 2631
Reputation:
It's been a while since I've written Angular code, but this worked pretty well last I used it. It's using an old version of Angular 2, so treat it more like pseudo-code.
You're also going to want to think about error propagation. Have a look at a similar question I had a while ago.
@Component({
...
providers: [{
provide: NG_VALIDATORS,
useExisting: CustomDateTimeControl,
multi: true
}, {
provide: NG_VALUE_ACCESSOR,
useExisting: CustomDateTimeControl,
multi: true
}]
})
export class CustomDateTimeControl implements OnInit, ControlValueAccessor, Validator {
private propagateChange = function (change) { };
private propagateTouched = function () { };
// Inner controls (you can also use an internal FormGroup for this)
public date = new FormControl();
public time = new FormControl();
constructor() {}
ngOnInit() {
this.date.valueChanges
.subscribe(value => {
this.propagateChange(value + ' ' + this.time.value);
this.propagateTouched();
}
this.time.valueChanges
.subscribe(value => {
this.propagateChange(this.date.value + ' ' + value);
this.propagateTouched();
}
}
writeValue(value) {
// Need to update the inner controls, but don't use setValue / patchValue,
// as that will trigger valueChanges in the above subscriptions,
// incorrectly calling touched
}
registerOnChange(fn) {
this.propagateChange = fn;
}
registerOnTouched(fn) {
this.propagateTouched = fn;
}
validate(control) {
// Custom logic to validate the parent control. In this case,
// we may choose to union all childrens' errors.
let errors = Object.assign(this.localControl.errors || {}, this.remoteControl.errors || {});
return Object.keys(errors).length ? errors : null;
}
}
Upvotes: 3