2

I'm trying to add directives to a page using ng-repeat. Basically I want to do something like:

<{{ currentDirective.directiveName }} ng-repeat="currentDirective in directiveList" />

Here's an example of what I'm trying to do in context.

http://jsfiddle.net/s9hf758w/

Cody Jacques
  • 506
  • 5
  • 9
  • There isn't a way (that I know of) to dynamically generate directives in this fashion; you have a `$compile` and a `$digest` which would both be reliant on something from the other. – Claies Feb 26 '15 at 00:09
  • could use an outer directive and return templates according to the ng-repeat value – charlietfl Feb 26 '15 at 02:21

2 Answers2

1

Why are you trying to add directives this way? Based on your example, the directives that you have return something different? Not sure what exactly you are trying to accomplish but the proper way to do it would be to have

<div my-directive ng-repeat="i in item" />

and the directive will have logic in it's controller or link functions

  • Hey, so I'm trying to manage dynamically changing the directives that are on the page. I know I can do it by recompiling the page after adding or removing directives from it, but since ng-repeat handles changes to the collection it's rendering, I thought maybe I could do that with a collection of directives. It may not be possible though. – Cody Jacques Feb 26 '15 at 00:47
1

I forked the fiddle and worked out a solution using a directive I called bind-compiled-html:

http://jsfiddle.net/hiebj/Lbusqf26/

Directive code:

function bindCompiledHtml($compile) {
    return {
        restrict: 'A',
        link: function($scope, $element, $attrs) {
            var html = $scope.$eval($attrs.bindCompiledHtml),
                toCompile = angular.element(html);
            $element.append($compile(toCompile)($scope));
        }
    };
}

Usage:

<div ng-repeat="directive in directiveHtmlList"
    bind-compiled-html="directive.directiveHtml">
</div>

This solution doesn't require $sanitize or $sce.trustAsHtml and keeps the messy DOM details out of your controller. I also included a version called bind-directive in case you really need to iterate over a list of directive names, but bind-compiled-html is more flexible.

You could successfully insert the HTML by using $sce.trustAsHtml along with ng-bind-html, but if you do that, Angular won't notice that you've added directives to your DOM. You have to tell it to $compile the new DOM elements after ng-repeat runs.

This will get you close to what you are looking for, but unfortunately you will not be able to use ng-repeat directly on your custom directive. You will have to use it on a wrapping element - ng-repeat is an attribute-only directive. The replace property of directives is deprecated, and besides it would cause major problems by destroying your original ng-repeat. Using the bind-compiled-html solution, you will end up with:

<div class="ng-scope ng-binding"
    ng-repeat="directive in directiveHtmlList"
    bind-compiled-html="directive.directiveHtml">
    <first-directive></first-directive>
</div>
<div class="ng-scope ng-binding"
    ng-repeat="directive in directiveHtmlList"
    bind-compiled-html="directive.directiveHtml">
    <second-directive></second-directive>
</div>

For more, check out the Angular docs on ng-bind-html (the inspiration for bind-compiled-html) and $compile.

Jon Hieb
  • 760
  • 8
  • 9
  • Hey, the jsfiddle you posted isn't working. Thanks for the info though, I'll look into this and play around with it tomorrow. – Cody Jacques Feb 26 '15 at 01:33
  • Cody, I edited my answer. You're right - the fiddle wasn't working. See the updates in the answer. Basically, the HTML was being correctly added to the DOM, but Angular didn't know about it; since the HTML was added dynamically, Angular wasn't running $compile on it. You need to run $compile manually or via a new custom directive (maybe `bind-and-compile-html`) in this case. The new fiddle works... But I still don't recommend this approach. – Jon Hieb Feb 26 '15 at 03:57
  • Hey, so I actually had a version using compile, and was trying to see if there was a way to do this avoiding that while still using directives. I guess it looks like I can't though. Thanks for your help, your answer def helped me understand this stuff better :) – Cody Jacques Feb 26 '15 at 04:38
  • Cody - check it out. I have been playing with the fiddle and came up with a cleaner solution. It still uses $compile, but all the ugliness is wrapped up in a directive like I suggested. I'm going to post it at the bottom of the answer as an edit. – Jon Hieb Feb 26 '15 at 04:53
  • Awesome, thank you so much, I really appreciate it. I think the bind-compiled-html will work for what I'm trying to do. This is way cleaner than how I was trying to do it using $compile. – Cody Jacques Feb 26 '15 at 15:35