Search code examples
angularjsangularjs-directiveangularjs-scopeangularjs-ng-repeatangularjs-ng-transclude

AngularJS : Directive shell that allows user defined content with directive scope


Here's a plunker example you can see: http://plnkr.co/edit/NQT8oUv9iunz2hD2pf8H

I have a directive that I would like to turn into a web component. I've thought of several ways as to how I can achieve that with AngularJS but am having difficulty with a piece of it. I'm hoping someone can explain my misstep rather than tell me a different way to do it.

Imagine you have a directive component that sets up some shell with css classes maybe some sub components, etc.. but lets the user define the main content of the component. Something like the following:

<my-list items="ctrl.stuff">
    <div>List Item: {{ item.name }}</div>
</my-list>

The HTML for the list directive could be something like the following (with OOCSS):

<ul class="mas pam bas border--color-2">
    <li ng-repeat="items in item track by item.id" ng-transclude></li>
</ul>

Normally this can be solved in the link function by linking the directives scope to the new content. And it does work for other components. However introducing the ng-repeat seems to break that portion of the control. From what I can tell, the appropriate place might be the compile function but the documentation says the transcludeFn parameter will be deprecated so I'm not sure how to proceed.

I should also note that when using the beta AngularJS, there is either a bug or a new paradigm coming, because this is no longer a problem. It seems like the transcluded content always gets access to the directives scope as well as the outer controllers scope.

I really appreciate any enlightenment on this.


Solution

  • It's by design that content added via ng-transclude will bind with an outer controller scope, not a scope of the current element that ng-transclude is on.

    You could solve the problem by copy the ng-transclude's code and modify it a bit to give a correct scope:

    .directive('myTransclude', function () {
      return {
        restrict: 'EAC',
        link: function(scope, element, attrs, controllers, transcludeFn) {
          transcludeFn(scope, function(nodes) {
            element.empty();
            element.append(nodes);
          });
        }
      };
    });
    

    And replace the ng-transclude with my-transclude in your directive template.

    Example Plunker: http://plnkr.co/edit/i7ohGeRiO3m5kfxOehC1?p=preview