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.
I’ve checked out. Everything works fine!