Search code examples
javascriptangularjsangularjs-directiveangularjs-ng-transclude

Nested transclude directive renders inner transcluded content in wrong place


I have a myList directive, which transcludes it's content. The problem occurs when I try and nest a <my-list> element inside another <my-list>.

JS Fiddle: http://jsfiddle.net/fqj5svhn/

The directive:

var MyListDirective = (function () {
    function MyListDirective() {
        this.restrict = 'E';
        this.replace = true;
        this.transclude = true;
        this.scope = {
            myListType: '='
        };
        this.controller = function () {
            this.classes = 'my-class';
        };
        this.controllerAs = 'myList';
        this.bindToController = true;
        this.template = '<ul ng-class="myList.classes" ng-transclude></ul>';
    }
    return MyListDirective;
})();
angular.module('myApp', []).directive('myList', function () {
    return new MyListDirective();
});

Example usage of the directive:

<div ng-app="myApp">
    <my-list my-list-type="'someType'">
        <li>foo</li>
        <li>bar</li>
        <li>
            <my-list my-list-type="'anotherType'">
                <li>cats</li>
                <li>dogs</li>
            </my-list>
        </li>
    </my-list>
</div>

What gets rendered:

<div ng-app="myApp" class="ng-scope">
    <ul ng-class="myList.classes" ng-transclude="" my-list-type="'someType'" class="ng-isolate-scope my-class">
        <li class="ng-scope">foo</li>
        <li class="ng-scope">bar</li>
        <li class="ng-scope">
            <ul ng-class="myList.classes" ng-transclude="" my-list-type="'anotherType'" class="ng-isolate-scope my-class">
            </ul>
        </li>
        <li class="ng-scope">cats</li>
        <li class="ng-scope">dogs</li>
    </ul>
</div>

As you can see the list items from the inner myList appear to be transcluded by the outer myList. What I want to happen:

<div ng-app="myApp" class="ng-scope">
    <ul ng-class="myList.classes" ng-transclude="" my-list-type="'someType'" class="ng-isolate-scope my-class">
        <li class="ng-scope">foo</li>
        <li class="ng-scope">bar</li>
        <li class="ng-scope">
            <ul ng-class="myList.classes" ng-transclude="" my-list-type="'anotherType'" class="ng-isolate-scope my-class">
                <li class="ng-scope">cats</li>
                <li class="ng-scope">dogs</li>
            </ul>
        </li>
    </ul>
</div>

Suggestions?


Solution

  • It is happening because when browser renders the page it expects <li> to be inside <ul>, but it your case it is inside <my-list> which is an invalid markup. All these things happen before Angular bootstraps and runs directives. There is no way you can predict how the browser would interpret your markup when it's invalid, in this particular case it pushes <li>'s out to be together.

    In this fiddle I've replaced <ul> and <li> with <div>, which does not have any requirements for nesting, and transclusion works just fine.