0

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?

5
  • You could turn it into an Angular Element. This is very early research, but the technique is outlined here: moduscreate.com/blog/angular-elements-ngcomponents-everywhere Commented Jan 31, 2018 at 0:00
  • Actually thats my question. How to make a Custom Form Control (special angular component) using another Form control inside. Commented Jan 31, 2018 at 0:35
  • Did the link then provide the answer? Commented Jan 31, 2018 at 1:18
  • no, it doesnt talk about composing multiple custom form controls Commented Jan 31, 2018 at 14:34
  • @MiguelGalante hey did you ever find an answer to this? I trying to do something similar. Commented Jan 29, 2019 at 21:21

1 Answer 1

3

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;
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Really thanks for the note about writeValue skipping valueChange events (could be achieved with setValue but with options: {emitEvent: false}). Resolved problems with "modified after check" errors. Don't know why official documentation is still so skimpy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.