How to create nested form using AngularJS

In a Rails application, when we need nested forms, we frequently use the ‘nested_form’ gem. However, we cannot use that when we are making an AngularJS + Rails app. To make nested forms using AngularJS, we need to write some specific code and in this blog I do just that. I have some sample code base with a working demo and hope it helps you out.

In this example, we have a User model and an Address model. A User has many addresses. Naturally, when we are adding or updating a user, we want to manage multiple addresses for that user in the same form.

The sample code is hosted on my Github repos and I also have a demo hosted on Heroku.

Adding the UserAddCtr Action


myApp.controller("UserAddCtr", ['$scope', '$resource', 'Users', '$location', function($scope, $resource, Users, $location) {
  $scope.user = {addresses: [{street1: '', street2: '', city: '', state: '', country: '', zipcode: '' }]}
  $scope.save = function () {
    if ($scope.userForm.$valid){
       Users.create({user: $scope.user}, function(){
       $location.path('/');
    }, function(error){
       console.log(error)
    });
  }
 }

 $scope.addAddress = function(){
   $scope.user.addresses.push({street1: '', street2: '', city: '', state: '', country: '', zipcode: '' })
 }

 $scope.removeAddress = function(index, user){
   var address = user.addresses[index];
   if(address.id){
     address._destroy = true;
   }else{
     user.addresses.splice(index, 1);
   }
 };

}]);

In the above code:

  • “$scope.user” has been initialized with an addresses array to ensure that the addresses are nested inside the scope of a user.
  • “$scope.addAddress” function is used to add a new address in the form. This is done simply by push a blank address $scope.user.addresses.
  • “$scope.removeAddress” function is used to remove an address from user form. Here we need to decide whether to mark for deletion from the database or simply remove from the form. If the address.id is present, we need to set “address._destroy = true” to mark it for deletion. (This is similar to what we do when using the nested_form gem for deleting nested attributes). If the address.id is not present, this was newly added in the form, so we simply remove it from the array using “user.addresses.splice(index, 1)”.

Now, lets see the html we have for the user form. For brevity, I am showing only the address template code here. You can check the complete user form html code here.


<div ng-repeat="address in user.addresses">
 <div ng-hide="address._destroy">
 <div class="form-group">
 <label class="control-label col-md-2">Address 1 </label>
 <div class="col-md-3">
 <input type="text" class="form-control" ng-model="address.street1" placeholder="Address 1"/>
 </div>
 <label class="control-label col-md-2">Address 2 </label>
 <div class="col-md-3">
 <input type="text" class="form-control" ng-model="address.street2" placeholder="Address 2"/>
 </div>
 <div class="col-md-2">
 <a ng-click="removeAddress($index, user)" class="btn btn-xs btn-danger">X</a>
 </div>
 </div>
 <div class="form-group">
 <label class="control-label col-md-2">City </label>
 <div class="col-md-3">
 <input type="text" class="form-control" ng-model="address.city" placeholder="Pune"/>
 </div>
 <label class="control-label col-md-2">State </label>
 <div class="col-md-3">
 <input type="text" class="form-control" ng-model="address.state" placeholder="MH"/>
 </div>
 </div>
 <div class="form-group">
 <label class="control-label col-md-2">Country</label>
 <div class="col-md-3">
 <input type="text" class="form-control" ng-model="address.country" placeholder="India"/>
 </div>
 <label class="control-label col-md-2">Zipcode </label>
 <div class="col-md-3">
 <input type="text" class="form-control" ng-model="address.zipcode" placeholder="12345"/>
 </div>
 </div>
<span style="line-height: 1.5;"><hr/>
</span><span style="line-height: 1.5;"></div></span>
</div>
<div class="form-group">
 <div class="col-md-offset-12">
 <a ng-click="addAddress()" class="btn btn-success btn-xs">+ Add address </a>
 </div>
</div>

In this html view, we can seen “ng-repeat” to iterate over user.addresses.

‘<div ng-hide=”address._destroy”>’ hides the deleted address in the from if  _destroy=true.

‘<a ng-click=”addAddress()”>+ Add address </a>’ adds a new address.

‘<a ng-click=”removeAddress($index, user)”>X</a>’ removes the address from user form.

Hope this helps. Please send me feedback if you have a complicated case in a nested form. I will add it to my example.

Advertisements
This entry was posted in Javascript, Ruby on Rails and tagged , . Bookmark the permalink.

One Response to How to create nested form using AngularJS

  1. Valen says:

    I’ve checked out. Everything works fine!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s