Intro
After deciding what approach to use in my previous article it's finally time to code. In this article I'll go through necessary changes to Angular AbstractControl
in order to add visibility functionality.
The todos
There are a couple of things we need to do:
- Add the
visible
state - Add the onChangeEventEmitter
- Add methods to control the state
- Enable adding visible as a constructor parameter
- Remove the validity check when the control is invisible
In order to achieve all of these, we will be altering some of the internal methods of the Angular framework. The best way to do this is to copy the code form their Github repo and apply the changes.
The fifth point form our todo list is not going to be implemented in this article since it is heavily interfering with the internals of the framework and as such, deserves more attention.
The implementation
In order to be able to use prototype
we will be using module augmentation:
declare module "@angular/forms" {
interface AbstractControl {
visibilityChanges: EventEmitter<boolean>;
readonly visible: boolean;
show();
hide();
/**
* This methods is marked as internal inside the AbstractControl.
* Declaring it here allows us to easily override it
* */
_isBoxedValue(formState: any): boolean
}
}
This is pretty straightforward. We 're-declare' the @angular/forms
module and it's AbstractControl
class (this must be declared as interface here, abstract class does not work) and then we declare new members of the class and private method we want to override.
Adding new functionality is also rather simple using the good old prototype
:
(AbstractControl.prototype as { visible: boolean }).visible = true;
AbstractControl.prototype.visibilityChanges = new EventEmitter<boolean>();
AbstractControl.prototype.hide = function () {
if (this.visible) {
(this as { visible: boolean }).visible = false;
this.visibilityChanges.emit(this.visible);
this.reset();
this.updateValueAndValidity();
}
};
AbstractControl.prototype.show = function () {
if (!this.visible) {
(this as { visible: boolean }).visible = true;
this.visibilityChanges.emit(this.visible);
}
};
There are 2 things I want to point out:
- Casting control as
{ visible: boolean}
- this one is from Angular framework itself, it allows us to modify thereadonly
value inside our class, while keeping it non-modifiable outside of it. - Additional calls in the
hide()
- when hiding the control, we have to remember to clear it's content and update validity. Right now we do not have the full functionality to do this correctly - it will come in the following articles - so these two methods have to suffice.
So that's points 1-3 done, now let's take a look at number 4.
Angular allows us to pass either value or value and disable state into the FormControl
constructor. Wouldn't it be nice to also be able to pass initial visibility ? In order to achieve this, we need to override two more methods:
-
AbstractControl.prototype._isBoxedValue
and FormControl.prototype._applyFormState
They check if the passed state is value or object, and assign the object values to form state respectively.
In the first one we just need to extend if statement a bit:
AbstractControl.prototype._isBoxedValue = function(formState: any): boolean {
return typeof formState === 'object' && formState !== null &&
Object.keys(formState).length >= 2 && 'value' in formState &&
('disabled' in formState || 'visible' in formState);
};
But the second one is a bit more tricky. We need to remember that we do not require passing both disabled
and visible
params so if the visible
was undefined
it would be interpreted as a false
in the if statement, hence hiding our control when we didn't want to. That's why we will specifically extend the second method to only allow true
or false
values and nothing else:
(FormControl.prototype as { _applyFormState: () => void })._applyFormState = function(formState: any) {
if (this._isBoxedValue(formState)) {
// ...
if (formState.visible === true || formState.visible === false) {
(this as {visible: any}).visible = formState.visible;
}
} else {
// ...
}
};
Full code for extending the Angular Forms is available here.
Summary
That's all the functionality for this article, as mentioned before, handling the validity of hidden control will be the topic of the next one.
Full code along with the example is available in my Github repo.
Thank you for reading. Hope you enjoyed this article and will find it useful ! See you in the next article.
Top comments (0)