Angularjs – Dynamically adding controls

In one of my project, I needed to dynamically add controls. The scenario is as such, I need to add kids name in a class room. There can be ‘n’ numbers of kids in a classroom. On the page, there are two buttons “Add Child” and “Remove Child”.

In CommonService, an Array of n child is defined as follows (Code is modified/snipped/simplified to bare minimum to discuss the concepts):

var ChildRewardsModule = angular.module('ChildRewards', ['ngRoute']).
   .value('CommonService', {
        childList: ["child1", "child2","child3","child4","child5"]
   })

Here is the controller code:


 $scope.childs = CommonService.childList;

 $scope.removeChild = function () {
        delete $scope[this.child];
    }

 $scope.addChild = function () {
      for (i = 0; i < CommonService.childList.length; i++) {
            if ($scope[CommonService.childList[i]] === undefined) {
                $scope[CommonService.childList[i]] = CommonService.childList[i];
                break;
            }
        }
    }

When user clicks “Add Child”, the addchild function is called and a property of childN (Where N is 1,2,3…) is added to $scope. so after first click $scope will have a property “child1” and a value of “child1” and after second click $scope will have “child2” property and the value will be a string “child2”. $scope will look as follows:

$scope.child1 = “child1”
$scope.child2 = “child2”

controller was the easy part. Now the problem remain was to create a new input control and connect the right $scope model to it. Here is the final code, ng-model need to get the right model of $scope.

<tbody ng-repeat="child in childs">
       <input ng-model="child" type="text" /> 
</tbody>

However, the above code does not work. Take a moment, and think about it, why it does not work? It does not work because ng-repeat creates a new scope for every iteration. So why this happens? If this will not happen then the new ‘childN’ variable may overwrite an existing variable on $scope. To save us from this situation, angular cleverly declares a new $scope for every ‘child’ in ‘childs’. Now, if i have 20 elements in the array, then i will have 20 $scopes, and each having the newly declared variable ‘childN’. This creates a slight problem, as you remember that we created ‘childN’ properties on $scope, which is now no more available in ng-repeater.

However, scope’s inheritance follows the same rule of prototypical inheritance in JavaScript, that is if we try to read a property and if it is not available on object then the inheritance tree will be traversed upwards till a property is found.

Fortunately, angular provides a $parent property, which refers to the parent of scope. Using this solves our problem.

<tbody ng-repeat="child in childs">
       <input ng-model="$parent[child]" type="text" /> 
</tbody>

However, we need to be aware that this can be a dangerous pattern. This is making an assumption about the DOM structure. It is saying, $parent is the scope which we want to refer and which has a childN property. If you will insert another scope-creating directive above the input tag then the parent will be pointing to a completely different scope.

It is recommended to avoid $parent property as it strongly links angularJS expressions to the DOM structure. However, knowing the ramification you can be careful to use it to enable your feature.

<table>
<tbody ng-repeat="child in childs">
   <tr ng-show="{{child}} != undefined">
      <td id="ChildName">
          <label> child{{$index+1}}: </label> 
             <input ng-model="$parent[child]" type="text" /> 
              <button class="btn btn-danger" ng-click="removeChild()"> Remove Child </button>
       </td>
  </tr>
</tbody>
  <tfoot>
        <tr>
              <td>
                   <button id="addChild" ng-click="addChild()"> + Add child</button>
              </td>
        </tr>
  </tfoot>
</table>
Advertisements

3 thoughts on “Angularjs – Dynamically adding controls

  1. Thanks, I was searching for this from long time.
    it would be great , if you can place this example in plnker and share the link for the same.

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