Intro
I've been developing in Angular since before it even released officially (from version 2.0.0-rc.5 I think) and it sped up development quite a bit. Although it gives developer quite a powerful and fast tool for developing web applications, there are a few shortcomings to it, one of which lies within the FormGroup.
Note that I'll be talking about use cases in really big and complex projects and therefore problems I discuss may seem a little exaggerated if You're not familiar with what convoluted business requirements may look like.
The Shortcomings
No differentiation between user and programmer input
Angular Forms have single method for setting value: setValue()
which takes value and options. After setting the value, valueChanges
event is dispatched to notify about the change.
Normally it's enough, but sometimes we want to know whether the change came from the user (and maybe trigger some rest call) or was applied programmatically (and do NOT trigger the rest call).
Single state for Invalid/Disabled
The FormGroup
, FormControl
and FormArray
objects store their status in single field named, well, status, the only problem being, it doesn't account for some business cases such as:
- Changing control value programmaticaly when it's disabled
- Checking validity of disabled control
Let's imagine that we have two fields dependent on each other. When field #1 changes value, #2 should be disabled or enabled based on some logic. When disabled, #2 should have it's value either cleared or set to some predefined value. In this case we can first setValue and then disable. But what about the case when #1 changes value again and #2 should have whole another value ? we need to call
enable()
setValue()
disable()
This is tedious and surely, there should be easier to do this.
No default value
In order to clear the control value we have to call reset()
method and pass some value into it. I'd argue that this is not good design. In the Angular repo You can read that this method:
Resets the form control, marking it `pristine` and `untouched`, and setting the value to null.
Well, this all well and nice BUT we have the ability to set control value when we create it and that changes a lot. When we pass value at creation, shouldn't reset()
restore that value ? Of course it should, that's what reset means. Current behaviour of this method does not match the name, there should be another method called clear()
which will work as reset()
works right now, and reset()
should restore the initial value.
Separation from view
Angular forms, although tight to input, have no connection to the view itself which normally is a very good thing. But as usual, there is one case where I think it should be and that's the visibility of the input.
Let's imagine that we have input #1 which decides whether to show input #2 or #3. Right now we can only listen for changes in #1 and then set some variable (ideally only 1) which tells us which other input to show: #2 or #3. Not only that but we also need to clear control value when hiding it and resetting it to some default value when it's shown.
It would be much easier if the Forms
contained state called visibility
and methods get visible()
, show()
and hide()
. Those methods could also handle resetting and clearing value of the controller when shown or hidden respectively.
The Case
And so, we've listed shortcomings - what now ? Do we expect Angular devs to fix these for us ? Do we say to 'no' to business requirements ?
The answer to both questions is no. We cannot expect anyone to fix this, because, in their case it may not be broken. They are writing framework for everyone to use, we cannot expect everyone to have the same problems. As to second question - let's be real. Unless it really is undoable in any way, we cannot say no to business requirement. We can point stupid ones, but we cannot say no just because we want to.
The Solution
There are only two solutions left:
- to extend or rewrite the Angular Forms, which is easier than you may think,
- or use what we have and write bunch of if statements.
Obviously, I'm going with the first solution and in the near future I'll follow through with article about how I extended Angular Forms and some tips on what I would do differently next time.
Summary
Hope You enjoyed my thoughts on Angular Forms 😊 If you have another use cases or know the reasons behind Angular devs decisions to write things that way, let me know in comments below 😊
We continue this topic in part 2
Top comments (4)
I am trying to extend AbstractControl to add a metaData structure to keep information about the controls (FormControl, FormArray ...) that could be used to manage not only the controls, but build the necessary forms for input.
Many of the 'dynamic' Reactive Form examples use an array of structures that is then used to build the FormGroup. The examples then pass this array of data along with the FormGroup structure to then build the input forms. This works if everything is static, but add a FormArray or generate a FormGroup on the fly and things quickly become interesting when trying to keep the data structure in sync with the FormGroup structured.
What I would prefer is a metadata element built into AbstractControl with the necessary getter() and setter() (or added to the constructor) that could then carry information on how to build that specific form control. This would be much like the 'data' element available in @angular/router.
This general concept has had quite a bit of traffic on githuib/angular/angular it does not seem to me, any forward progress.
Does your example given here, have a home on github or some other repo to use an example?
Hi, first of all, sorry for the delayed answer. Yes, there is a github repo here, it's also linked in the following articles in which I provide an example of how to actually add your own functionality.
My conclusions on this problem were different. I've abandoned all FormControl and related "manual" implementations for the ngModel which automatically creates the controls and support two way bindings if one knows how to do it. My quest started with the very problems you mentioned above, in particular; two way bindings. I believe Angular can do a much better job than they have so far, but it is what it is. A lot of these "changes and gaps" were due to the immutable craze that has swept the web world. For Angular it swept them into craziness and ill documented abilities.
Thanks for sharing. Help me a lot.