Dynamically Add Elements to the Page With Angular's ng-repeat and ng-include

By Dave Ceddia

There are some times when you might want to dynamically add elements to the page in response to some user action. Maybe you’re writing a form builder app, and users can add chunks of the form one at a time.

In this case, you want the order of the displayed forms to match the order of the button clicks. So if you have 3 buttons (A, B, C) and the user clicks (B, C, A), then you want form B to be added first, then C, then A.

Here’s an example that works this way. Try clicking the buttons.

The controller has an array of form template names. When a button is clicked, the matching form template name is added to an array of displayed forms. An ng-repeat directive loops array over the list of displayed forms, and uses ng-include to render the templates.

The HTML is simple, and for sake of making it easy to understand, it includes some inline templates. In a real app you’d probably want to put these in files and integrate them into your build process.

<body ng-app="exampleApp" ng-controller="DynamicFormCtrl as ctrl">
  <div>
    <button ng-click="ctrl.addForm(0)">Form One</button>
    <button ng-click="ctrl.addForm(1)">Form Two</button>
    <button ng-click="ctrl.addForm(2)">Form Three</button>
  </div>
  
  <div ng-repeat="form in ctrl.displayedForms track by $index">
    <ng-include src="form"></ng-include>
  </div>
  
  <script type="text/ng-template" id="form1.tpl.html">
    <label>
      Form one is an input: <input type="text" />
    </label>
  </script>

  <script type="text/ng-template" id="form2.tpl.html">
    <label>
      Form two gives you choices: <input type="radio"/> <input type="radio"/>
    </label>
  </script>
  
  <script type="text/ng-template" id="form3.tpl.html">
    <button>Form three is a button</button>
  </script>
</body>

The JS code is very simple. All it needs to do is add items to an array when the user clicks buttons.

angular.module("exampleApp", [])
  .controller("DynamicFormCtrl", function() {
    var ctrl = this;
    
    var forms = [
      "form1.tpl.html",
      "form2.tpl.html",
      "form3.tpl.html",
    ];
    
    ctrl.displayedForms = [];
    
    ctrl.addForm = function(formIndex) {
      ctrl.displayedForms.push(forms[formIndex]);
    }
  });

One important thing to notice with this approach is that you need to use the “track by” syntax (in this case, track by $index) if you want to allow repeat values - otherwise you’ll get an error saying “Duplicates in a repeater are not allowed.”

If you don’t want to allow repeats, just check before adding each form to the displayedForms array.