Building Dynamic Angular Forms with ngRepeat and ngForm

Free Course

Getting Started with Angular 2

Angular 2 is the shiny new framework that comes with a lot of new concepts. Learn all the great new features.

Angular provides us a lot of tools to deal with forms. Just the fundamental way it binds data for us to a lot of the tools it provides like validation, Angular helps make lives easier.

In the past, we have gone over how to validate basic forms using Angular. This was a simple process and just required adding a few directives here and there.

Dynamically Building Forms

Now let’s talk about a different scenario where we won’t always know how many fields or which fields we will have. There could be many scenarios for needing a dynamically built form. Let’s say that our application pulls data from that API for a form that we need to edit.

For this example, we are going to get a list of users and we need to edit their email. The process for this would look something like:

  • Get an array of users from an API call
  • Display this list to our administrator
  • Provide fields to edit the information of our users
  • Validate those fields using Angular

To keep things simple, instead of pulling a list of users from an API, we will be creating the array right in the JavaScript file. Easy cheese. Here is our list of users and we’ll be editing their email addresses.

Here is the example JSON data we would get back from our API for our users.


  // sample data we would get back from an api
  var users = [
      { 
        name: 'Chris',
        email: ''
      },
      {
        name: 'Holly',
        email: ''
      }
  ];  

  // assign this data to an object to store all our form data
  $scope.formData = {};
  $scope.formData.users = users;

We will create a form using these fields and we will use ng-repeat to loop over each user and display their email field. Here is the code for that:



<!-- we are using bootstrap for these styling classes -->

<form name="userForm" novalidate>

  <div class="form-group" ng-repeat="user in formData.users">
    <label>{{ user.name }}'s Email</label>
    <input type="text" class="form-control" name="email" ng-model="user.email" required>
    <p class="help-block" ng-show="userForm.email.$invalid">Valid Email Address Required</p>
  </div>

</form>

We loop over each of these fields but there is a problem when building our form this way. Validation won’t work because of how we named the name attribute statically.

Remember that we are using the ng-class and ng-show directives to show our errors. More info here.

Validation Without ngForm

By default, Angular currently does not allow us to dynamically create the name attribute of an input field. From our last experiments with validation, we can see that validating a field using Angular requires the name attribute.

The Problem: Name attributes are not dynamically generated and we are not able to validate fields individually. Since name isn’t dynamically generated, then all the fields will validate together as one. To check if an input is valid, you would use {{ userForm.email.$valid }}.

See the Pen Dynamic Angular Forms with ngForm (Non-Working) by Chris Sevilleja (@sevilayha) on CodePen.

Typing into the form above, you can see that the fields are only valid when both become valid email addresses.

Validation Using ngForm

So how would we validate each field individually, the way a user would expect our form to validate? Since Angular uses the method of formName.fieldName.$valid to validate, we will need to have each input be part of its own form.

The Solution: ngForm allows us to create forms within our main form that will allow us to validate fields individually. Let’s modify the code from earlier to accommodate ng-form and see how that changes things.



<form name="userForm" novalidate>

  <div class="form-group" ng-repeat="user in formData.users" ng-class="{ 'has-error' : userFieldForm.email.$invalid }">
  <ng-form name="userFieldForm">
    <label>{{ user.name }}'s Email</label>
    <input type="text" class="form-control" name="email" ng-model="user.email" required>
    <p class="help-block" ng-show="userFieldForm.email.$invalid">Valid Email Address Required</p>
  </ng-form>
  </div>

</form>

Just by adding the ng-form directive, now each field will believe it is part of a form within our main form. In this case, userFieldForm. Now we are able to validate each field individually by using userFieldForm.email.$invalid.

See the Pen Dynamic Angular Forms with ngForm (Working) by Chris Sevilleja (@sevilayha) on CodePen.

Now we are able to validate each field individually!

Conclusion

Hopefully this has helped show how the ngForm directive is to be used when building dynamic forms from ngRepeat.

The official ngForm docs aren’t very elaborate so this should be a good starting point for anyone with the need for a form like this.

Go ahead and experiment further and go even crazier by nesting ngRepeats! As always, let us know if you have any questions or comments.

Chris Sevilleja

Co-founder of Scotch.io. Slapping the keyboard until something good happens.