DEV Community

loading...
Cover image for A case for extending Angular Forms - part 5

A case for extending Angular Forms - part 5

digitalcrafting profile image DigitalCrafting ・2 min read

Intro

In my previous article we changed the reset() methods of angular forms and added clear(). This time, we are going to differentiate the user input from the programmer (or code) input.

The how

We have actually two options to do this, both of them require adding at least one new method which we will call: setControlValue() since we will be changing the FormControl value:

  1. Add another method setInputValue() which will signify that the input has changed
  2. Use existing setValue() as the one for the user input

Since the first option is only viable if you, for some reason, separate input from the forms, we are going the second path which is also easier to implement.

In the setControlValue() we will enable setting the value even if the control is disabled, because sometimes it's nice to have. We will also use the setValue() method to change the internal value but we will not trigger the valueChanges event unless programmer wants it. Hope this becomes clearer with the implementation.

Implementation

Similarly to previous functionality, we first need to declare extra method. We will do that in the AbstractControl for compatibility with formGroup.get() method, and in the FormControl for actual functionality:

declare module "@angular/forms" {
  interface AbstractControl {
    ...
    controlValueChanges: EventEmitter<any>;
    setControlValue(value: any, options?: {emitEvent?: boolean, emitValueEvent?: boolean});      
  }
  interface FormControl extends AbstractControl {
    setControlValue(value: any, options?: {emitEvent?: boolean, emitValueEvent?: boolean});
  }
}
Enter fullscreen mode Exit fullscreen mode

In the AbstractControl we initialize the EventEmitter and just proxy the setControlValue arguments to setValue for compatibility:

AbstractControl.prototype.controlValueChanges = new EventEmitter<any>();

AbstractControl.prototype.setControlValue = function (value: any, options: { emitEvent?: boolean, emitValueEvent?: boolean }): void {
    let opt = options ? {emitEvent: options.emitValueEvent} : null;
    this.setValue(value, opt);
};
Enter fullscreen mode Exit fullscreen mode

And in the FormControl we actually implement the desired functionality:

FormControl.prototype.setControlValue = function (value: any, options?: { emitEvent?: boolean, emitValueEvent?: boolean }): void {
    if (this.disabled) {
        this.enable();
        this.setValue(value, {emitEvent: options && options.emitValueEvent});
        this.disable();
    } else {
        this.setValue(value, {emitEvent: options && options.emitValueEvent});
    }
    if (options && options.emitEvent) {
        this.controlValueChanges.emit(value);
    }
};
Enter fullscreen mode Exit fullscreen mode

And that's it. We now have an option to override user input even for the disabled control.

Conclusion

This concludes this series. During just few articles, we have extended the Angular Forms quite a bit. We can show and hide controls from a single place (and also do not consider hidden controls for validation), we can reset the control to default value or clear it completely, we can determine whether the value change came from user input or from our internal logic. And it wasn't even that hard when using prototype.

Obviously there might be some corner cases which I forgot about and it needs a lot more testing, especially automated, but otherwise, it works.

Hope this series will be of use to you and that you learned something new along the way :)

Discussion

pic
Editor guide