6

I'm trying to add a body directive which will have a left panel and a right panel. But in between these panels I will have some content that is private to the body directive. I'm currently using transclude=true option to load the content. But, I'm looking for a way to use two ng-transclude. I investigated a lot how to solve that, but I was not able to find an elegant solution. I had to manually add the transcluded objects in the compile step of the body directive. Following is the way I solved that:

Body Directive

var myApp = angular.module('myApp', []);

myApp.directive('body', function () {
    return {
        restrict: 'A',
        transclude: true,
        scope: {
            title: '@'
        },
        templateUrl: 'body.html',
        compile: function compile(element, attrs, transclude) {
            return function (scope) {
                transclude(scope.$parent, function (clone) {
                    for (var i = 0; i < clone.length; i++){ 
                       var el = $(clone[i]);
                       if(el.attr('panel-left') !== undefined) {
                            element.find('#firstPanel').html(el);
                       } else if(el.attr('panel-right') !== undefined) {
                            element.find('#secondPanel').html(el);
                       }
                    }
                });
            }
        }
    };
});

myApp.directive('panelLeft', function () {
    return {
        require: "^body",
        restrict: 'A',
        transclude: true,
        replace: true,
        template: '<div ng-transclude></div>'
    };
});

myApp.directive('panelRight', function () {
    return {
        require: "^body",
        restrict: 'A',
        transclude: true,
        replace: true,
        template: '<div ng-transclude></div>'
    };
});

Template

<script type="text/ng-template" id="body.html">
    <h1> {{title}} </h1>
    <hr/>

    <div id="firstPanel" class="inner-panel"></div>
    <div id="innerMiddleContent" class="inner-panel">middle private content here</div>
    <div id="secondPanel" class="inner-panel"></div>
</script>

<div body title="Sample Body Directive">
    <div panel-left>
        Public content that goes on the left
    </div>   

    <div panel-right>
        Public content that goes on the right
    </div>    
</div>

Here is the JSFiddle for this example. I'm looking for something like this:

Good-to-have Template

 <script type="text/ng-template" id="body.html">
     <h1> {{title}} </h1>
     <hr/>

     <div id="firstPanel" class="inner-panel" ng-transclude="panel-left"></div>
     <div id="innerMiddleContent" class="inner-panel">middle private content here</div>
     <div id="secondPanel" class="inner-panel" ng-transclude="panel-right"></div>
 </script>

Question: Am I doing something wrong? Is there a recommended way to solve this issue?

Alan Souza
  • 7,475
  • 10
  • 46
  • 68
  • 1
    I think you are on the right way, the `ng-transclude` doesn't support splitting transcluded elements to multiple places. BTW, the `transclude` function in `compile:` has been deprecated. You should use the `transclude` function in `link:` instead. – runTarm Aug 13 '14 at 18:55
  • Thanks for your answer and for letting my know that transclude in compile is deprecated. But my question is more if it makes sense to have this multiple ng-transclude integrated in next versions of Angular. Sorry, I should have made myself more clear. – Alan Souza Aug 13 '14 at 19:27
  • 2
    Sound like a good idea! May be you could write an enhanced version of `ng-transclude` to use in your project first. – runTarm Aug 13 '14 at 19:34
  • That is a great suggestion. I will try to do it. – Alan Souza Aug 13 '14 at 20:19

2 Answers2

4

Have you seen this directive?

https://github.com/zachsnow/ng-multi-transclude

I think this is exactly what you are looking for.

So with slight modifications to your template

<div ng-multi-transclude="panel-left" class="inner-panel"></div>
<div ng-multi-transclude="panel-right" class="inner-panel">middle private content here</div>
<div id="secondPanel" class="inner-panel"></div>

and then you can use it like this

<div body title="Sample Body Directive">
  <div name="panel-left">
    Public content that goes on the left
  </div>   

  <div name="panel-right">
    Public content that goes on the right
  </div>    
</div>
Chris Woolum
  • 2,854
  • 20
  • 20
2

As of Angular 1.5 there is support for multiple transclusions without a third party library.

This is referred to as "slot" transclusion and you specify slots by providing an object to the transclude property of your directive.

directive('someDirective', {
  template: '<div ng-transclude="first"></div><div ng-transclude="second"></div>'
  transclude: {
    first: '?elementOne', // Question mark makes it optional
    second: 'elementTwo'
  },
...
}

And then when you use someDirective:

<some-directive>
  <element-one><!-- content to tranclude into first slot --></element-one>
  <element-two><!-- content to tranclude into second slot --></element-two>
</some-directive>

Additional Reading & Examples

Documentation for ngTransclude

Using Angular 1.5's Multiple Transclusion Slots

vpiTriumph
  • 3,116
  • 2
  • 27
  • 39