Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular multiple transclude dynamic number of elements

Angular 1.5 enables the ability to multi transclude.

Notably, it'd be useful to be able to transclude a dynamic number of items into a directive and declare the names and locations of those transcludes at a later time (e.g. in the link/compile).

To further illustrate, I want the ability to do something like:

//Example usage of directive
<multi-slot-transclude-example>
<transclude1>TEST1</div>
<transclude2>TEST2</div>
<transclude3>TEST3</div>
<transclude4>TEST4</div>
.... dynamic number of items ...
</multi-slot-transclude-example>

//Example of directive that dynamically transcludes multiple items
angular.module("multiSlotTranscludeExample", [])
    .directive("directiveName", function(){
    return {
        restrict: 'E',
        transclude: {
            't1': '?transclude1',
            't2': '?transclude2',
            //I'd like this list to be able to be defined non-statically (e.g. in link)
        },
        template: '<div ng-repeat="n in transcludedElementList">'
        + '<div ng-transclude="t{{n}"></div>'
        + '</div>'
        };
})

Note that in the directive declaration which implements a multi-transclude, I have to have prior knowledge about the number of items that will be transcluded when declaring it.

Is there a way to do something like this in either a link (or using a workaround), which would keep the same functionality that transclusion current offers?

like image 905
kclem06 Avatar asked Sep 26 '22 11:09

kclem06


1 Answers

Unfortunately, Angular does not seem to provide any non-intrusive way to do this.

However, it can be achieved with a little tweak.

We need to intervene in angular.js's transclusion slot logic:

...

var slots = createMap();

$template = jqLite(jqLiteClone(compileNode)).contents();

// Small tweak to allow dynamic transclusion
if (directiveValue === 'dynamic') {
  directiveValue = $parse(templateAttrs.transcludeDynamic)();
}

if (isObject(directiveValue)) {

  // We have transclusion slots,
  // collect them up, compile them and store their transclusion functions
  $template = [];

...

This allows us to specify the transclusion options in the component's consumer, like so:

<my-custom-component transclude-dynamic="{testSlot: 'test', testSlot2: 'test2'}">
  <test>something to transclude</test>
  <test2>then another slot content to transclude</test2>
</my-custom-component>

In the component we enable dynamic transclusion:

...
selector: 'myCustomComponent',
template: template,
transclude: 'dynamic',
bindings: {
  transcludeDynamic: '<'
}

Note that we also bind the transclusion info, this enables us to ng-repeat, ng-if and any other logic on it.

For example:

<div ng-repeat="(slot, name) in $ctrl.transcludeDynamic">
  <div ng-transclude="{{slot}}"></div>
  <hr>
  <div>something else</div>
</div>

PR: https://github.com/angular/angular.js/pull/14227

like image 123
Ovidiu Dolha Avatar answered Oct 12 '22 10:10

Ovidiu Dolha