Blaise
Blaise

Reputation: 11

angularjs, $compiled templates and ng-repeat

Essentially, I take two templates inside a compile function of a directive. I compile them once and for all (via $compile) in the compile phase of the directive. In the link phase of the directive, I watch a scope variable and applies one or the other compiled template to the scope.

testApp.directive('testField', ['$compile', function ($compile) {
  return {
    restrict: 'E',
    scope: true,
    compile: function(tElement, tAttrs) {
      var viewFn = $compile("<div>View: <span ng-repeat='x in [1,2,3]'>{{x}}</span></div>");
      var editFn = $compile("<div>Edit: <span ng-repeat='x in [4,5,6]'>{{x}}</span></div>");

      return function (scope, element, attrs) {
        var innerScope = null;

        scope.$watch("mode", function (mode) {
          if (innerScope) innerScope.$destroy();
          innerScope = scope.$new();

          if (mode == 'VIEW') {
              element.empty().append(viewFn(innerScope));
          } else {
              element.empty().append(editFn(innerScope));
          }
        });
      };
    }
  };
}]);

It works fine, except when the template includes a Ng-repeat that is not the root element in which case it behave strangely:

To reproduce, go to http://plnkr.co/edit/RCqlNWTVdXVoMQCkFcQn?p=preview And switch a couple of time from Edit to View.

You'll notice the number of iterations of ng-repeat grows over time. First time, it displays 123 and 456, like it should

After the first back and forth between view and edit, it displays 123123 and 456456

And it keeps adding one iteration every time you do a back and forth between view and edit.

Upvotes: 0

Views: 2552

Answers (2)

Ali BARIN
Ali BARIN

Reputation: 1920

Maybe you can do like this.

Firstly, you inject new dom directly in your view. After, catch it and apply $compile.

like this;

var viewFn = "<div>View: <span ng-repeat='x in [1,2,3]'>{{x}}</span></div>";
...
element.html(viewFn);
...
$compile(tElement.contents())(innerScope);

Upvotes: 1

Blaise
Blaise

Reputation: 11

I probably found the solution shortly after posting.

The problem apparently lies in the fact that ng-repeat needs to clone the template.

After stumbling on a comment in the angular source, turns out we can pass a second argument to the function returned by compile.

This second argument is a function that is called with a clone of the template (and the scope being linked to).

so instead of

      if (mode == 'VIEW') {
          element.empty().append(viewFn(innerScope));
      } else {
          element.empty().append(editFn(innerScope));
      }

I can do

      function setElts(elts) {
         element.empty().append(elts);
      }

      if (mode == 'VIEW') {
        viewFn(innerScope, setElts);
      } else {
        editFn(innerScope, setElts);
      }

Upvotes: 1

Related Questions