Angular Auto-Save Forms | RxJS Operators
A Brief Guide to Creating Reactive and Template-Driven Forms with RxJs and Angular Material
In this article, we will study how to create auto-saving forms in Angular. We’ll be implementing this feature with all its whistles and bells, like turning it on and off, displaying snack-bar notifications, and more.
In this process, we are going to explore several RxJs operators. We will start by implementing the feature for reactive forms. Finally, we’ll see what changes are needed for template-driven forms.
So let’s get started!
Before we dive into the details, let’s take a quick look at the demo application.
The form has two input fields: the user’s first and last name. It has a “Save” button, which is enabled when the form is valid. Finally, if auto-save is enabled, a loading bar will appear at the bottom of the form whenever the user changes a value. Here is the code:
As you may have noticed, we use Angular Material. To install it, you just need to run:
ng add @angular/material,
It is quite easy to toggle the auto-saving feature.
First, we declare a
Then, we do a two-way bind using that variable
mat-slide-toggle Element. Next, we pass the variable as an input
UserProfile The component, which contains the form.
We handle price changes
autoSave By applying the input property
OnChanges Lifecycle hook. we define
ngOnChanges A method to enable or disable auto-saving based on the value of the property (more on that soon).
The question we need to ask is “when”? When do we want the form to be saved automatically?
We want this to happen if all of the following conditions apply:
- Auto-save is enabled
- user changed a value
- form is valid
Condition #1 has already been applied in the previous section. The rest is implemented inside
enableAutoSaving way. So, let’s take a look.
In order to auto-save the form when the user changes a value (condition #2), we need to subscribe to the form
valueChanges Worth seeing
From official document,
valueChanges “A multicasting is an observable that emits an event each time the value of a control is passed in the UI or programmatically.”
- On line 1, we define
changesSubscriptionTo subscribe. We do the assignment on line 6. It will let us unsubscribe later when we want to disable this feature.
- On lines 2 and 3, we define a
BehaviorSubjectand its corresponding observable to display or hide the loading bar.
- On line 7, if the form is invalid, we filter and close any emitted values (condition #3). There is no point in saving invalid values.
- On line 8, given that the user has typed something to make the form valid, we emit
trueSo the loading bar is displayed.
- On line 9, we use
debounceTimeOperator with a time duration of 1000ms (=1 second). The observable will not emit until the user stops typing for more than a second. We give the user time to react, pause or think as they type.
- Along the lines of 10-11, we use
switchMapoperator, which is a higher-order mapping operator. It sends user data from form to
UsersServiceand expects to be seen. The method returns an observable, in which
switchMapAutomatically subscribes and unsubscribes.
It is called “inner observable” because it is nested in another observable,
valueChanges, called the “outer observable”. Finally, if another value is emitted before the HTTP request is completed, then
switchMapcancels that request in favor of the new one.
- On line 12, we use
finalizeoperator on the inner observable. When this observable completes (normally or by an error), we emit
falseTo hide the loading bar.
- on line 15, we end
saveUser Below method, we are using
catchError and return
EMPTY To avoid propagation of any error from within (
switchMap) viewable to external (
valueChanges) Worth seeing.
If we let this happen, the observable will be complete, and we will have to subscribe again to hear the price changes.
Finally, when we need to disable the feature, we call
disableAutoSaving method to unsubscribe from
changeValues Worth seeing
Unfortunately, we haven’t done that yet.
Let’s say the user has filled out the form, and is now valid. Then, they repeatedly press the “Backspace” button until one of the fields becomes empty. The form becomes invalid, but the last change – the one with the last remaining character – is not canceled.
Yes, it’s a minor detail, but these details make a difference!
We need to see the change in the status of the form and act accordingly. To do this, we need to subscribe
statusChanges The form is observable.
- On lines 1 and 4, we define and set
statusSubscriptionvariable, just like we did with
- On line 5, we use
distinctUntilChangedThe operator allows only specific status values to be emitted when the form changes from valid to invalid and vice versa.
- On line 6, we use
pairwiseOperator to sum the previous and current emitted values. On line 7, we use
tapoperator to use this pair.
- On lines 8-10, we call
disableAutoSavingIf the status of the form has changed
INVALID, it cancels
changesSubscription, and thus no request is sent. problem fixed!
- On lines 12-15, we enable auto-saving if the form is
changesSubscriptionOff, that is, if auto-saving was previously disabled.
- On line 14, we call
updateValueAndValidityclearly on the form. The first change that makes the form valid again will not be autosaved if we don’t. This is because this specific change happened before calling
enableAutoSavingHow do we subscribe
- On line 17, we finally subscribe to the observable.
Finally, when we disable the auto-save feature, we should call
disableStatusWatching method to unsubscribe from
statusChanges Worth seeing
OK, now we’re done! I
As always, you can find a working demo at this stackblitz link or with code this github repository,
We saw how it works with reactive forms. Can we auto-save template-driven forms? of course we can! We just have to make some changes.
First, we need to import
FormsModule (if not already imported). by importing
NgForm Instructions active on all
<form> tag. We don’t need to add any special selector.
But how do we listen to price and position changes? Well, we need to hold onto form.
We export the directive to a local template variable
ngForm as the key. We also register child controls using
ngModel And this
Next, we get the reference of the form by using
@ViewChild Decorator in component class. And all! Now we can use this context and do what we have seen so far.
Notice the strange syntax on line 19? No, this is not a typo.
NgForm The instruction creates a top-level
FormGroup instance and binds it to a form to track the total form value and validation status. This
FormGroup can be accessed through example
NgForm does not provide
updateValueAndValidity method or any other way to force the form to be updated. Hence, we are using this solution.
You can find the source code in a separate branch on both GitHub and StackBlitz in the previous link.
In this article, we demonstrated how to create auto-saving forms in Angular. During this we used and explained several RxJs operators. Lastly, we highlighted the differences between the implementation of Reactive and template-driven forms.
I hope you enjoyed this article and learned something new. If you did, follow me and subscribe to my newsletter for more content like this.
Thanks for reading. Stay tuned for more.
#Create #AutoSaving #Forms #Angular