Angular Multipage Form, or Sharing data in Angular
TL;DR: For a multipage form use a parent state for the controller and nested states for the views. A parent state can have a controller that is shared by all child views, making the data flow much easier to reason about.
If you have been using angular for a while now, you might have had to share data between states. If you did so, you probably ended up creating a service that stores the data and is then imported by several views. Even though that solution works, there are other patterns out there to share data, that sometimes seem more appropriate. One such times is when you want to make a multipage form.
If you were doing a long single page form, you would only need one controller, and no service to share data between inputs, so why should you need it in a multipage form?
In this article I will show you how to refactor a 3 step form using a service to share data, into the same form using a parent state for the controller andnested states for the views.
Even though I am showing it here as a refactoring, I now use this pattern directly for my angular forms.
It’s coding time
First of all, let’s take a look at the multiform page that uses a service:
We have 3 states, each with its own controller, and a service (formRegistry) to share the data between them. We need to always inject the service in each controller and in the second step, to be able to access the data in the service, we need to bind it to the controller. Nasty!
Let’s start refactoring!
1 — Create a parent state
To do so you need to do 2 things, create a parent state and rename the child views:
.state('form', {
abstract: true,
url: '',
template: '<ui-view/>',
Rename step1 into form.step1 in the states and in the $state.go(‘form.step1’);. Repeat for step2 and step3.
2 — Add a controller to the parent state
.state('form', {
...
controller: 'formController as formCtrl',
})....controller('formController', function($state){ });
3 — Move the code from the other controllers into the new controller. Replace all references to old controllers in the HTLM by formCtrl. Delete the old controllers.
By now your code should look like:
4 — Inside the controller, replace references to the service formRegistry by this. In the html for step2 replace formCtrl.formRegistry.animal by formCtrl.animal. Now you can safely remove the service formRegistry.
After you finish refactoring, you should have something like this:
Summary
To sum up, we made a multipage form with a single controller, like we would do for a single page form. If you are worried about the fact that we merged N-controllers into one, remember that there is a single responsability here, that of the form. Since this responsability only concerns the form’s controller, this pattern allowed us to get rid of a service whose sole purpose was to overcome a limitation of angular.