Angularjs: TypeError: Cannot call method 'insertBefore' of null

Please find the fiddle here http://jsfiddle.net/UxYLa/6/

This is a simplified form of what I am trying to do. There are two directive, and the nested one, subDirective, which dynamically creates html form based on the selection(random). If you click repeatedly on the button, it throws below error

TypeError: Cannot call method 'insertBefore' of null
    at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:138:283
    at q (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:7:332)
    at q.after (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:138:258)
    at Object.O.(anonymous function) [as after] (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:139:414)
    at Object.enter (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:141:226)
    at Object.move (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:141:360)
    at https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:185:282
    at Object.fn (https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js:99:371)

I found a reference about this https://groups.google.com/forum/#!msg/angular/dNra_7P2hwU/09-UBn1XxyUJ https://github.com/angular/angular.js/issues/2151 but I am using the latest stable version.

What is the cause of this error.

Full code below:

script:

var myApp = angular.module('myApp', [])
    .controller('TestController', ['$scope', function ($scope) {
        $scope.data = [{ type: "a", values: [{name:"aa"}, {name: "bb"}]},
          { type: "b", values: [{name:"aa"}, {name: "bb"}]}]
}]);   
myApp.directive('directive', function () {
    return {
        scope: {
            data: "=",
        },
        restrict: 'E',
        controller: function ($scope, $element, $attrs) {
            $scope.random = function(){
                $scope.model = $scope.data[Math.floor(Math.random()*$scope.data.length)];
                console.log($scope.model)
            };
        },
         template: '<div><button ng-click="random()">Random</button><sub-directive data="model"></sub-directive></div>'
    };
});


myApp.directive('subDirective', function ($templateCache, $compile) {
    return {    
         scope: {
            data: "=",
        },
        restrict: 'E', 
        link: function (scope, element, attrs) {
             scope.$watch('data', function (newVal, oldVal) {
                if (newVal) {
                     element.html($templateCache.get(newVal.type + '.html'));
                     $compile(element.contents())(scope);
                 }
             });
        }
    };
});

html

  <script type="text/ng-template" id="a.html">
     a.html
  </script>


<script type="text/ng-template" id="b.html">
      <div ng-repeat="itm in data">
          <span ng-if='"string" == itm.type'>
              <input name='{{itm.Name}}'id='{{itm.Name}}' ng-model='model[itm.Name]' type='text'></input>
          </span>
      </div>
  </script>

<div ng-controller="TestController">
    <directive data="data"></directive>
</div>

It has something to do with the compilation and how data is bound. I made a working version here: http://jsfiddle.net/UxYLa/9/

The main differences are in b.html

  <div ng-if="data.values">
      <div ng-repeat="itm in data.values">
          <input name='{{itm.name}}' id='{{itm.name}}' ng-model="itm.name" type='text'></input>
      </div>
  </div>

Have to interate over arrays and refer to itm instead of model since it's in the scope.

Edit: after some digging here are some more clues as to why the error happens, but not when you wrap it in a div. It is calling this: parent.insertBefore(node, index.nextSibling); where parent is element.parent of your ng-repeat. If you don't have the wrapper, the parent is null.

This means the error occurs whenever the html you are compiling has a watch that is on the outside of the template when directly changing the element.

I also made a solution that doesn't attempt to change element directly, but rather appends the compiled element to it. So that everything, when it's checked in the digest cycle will have a proper structure. http://jsfiddle.net/UxYLa/12/

Hope this helped.

i had the same problem. i could fix this by referencing JQuery before Angular in my html script tags.

I had the same problem when using UI-Router and the A tag had the wrong URL in the href attribute which was not a route.

I had the exact same problem, and what seemed to solve it was removing a class of "ng-view" that I had on one of my divs. It's like it was causing a conflict.

I had:

<div class="ng-view"> 
  <div ng-view>
    ...

Then changed to:

<div class="ngview">
  <div ng-view>
    ...

I'm guessing ng-view is a reserved word, but I didn't think it would be affected when used as a class.