7

I have a generic <item> directive, and a <listing> directive with filters and pagination tools for listing that <item>:

enter image description here

Example: https://plnkr.co/edit/r6byzhFX5m674ONhH1JS?p=preview

The <listing> template is something like this:

<div ng-repeat="item in items">
  <item date="item">
        <ng-transclude ng-transclude-slot="itemContent"></ng-transclude>
  </item>
</div>

The <item> directive uses the new Angular 1.5 multi-slot transclusion to customize footer and header easily:

<item data="itemData">
  <header>My header</header>
  <footer>My custom footer</footer>
</item>

My problem arises when I try to customize that items when using <listing>. If I use something like this:

<listing items="myItems">
  <item-content>
        <header>{{ item.name }}</header>
        <footer>My custom footer for {{ item.name }}</footer>
  </item-content>
</listing>

It doesn't work, because the <item-content> gets inserted into <item>, but <header> and <footer> don't get transcluded into their proper places, and they cannot read the item scope variable. Is there any way to achieve this?

Javier Marín
  • 2,166
  • 3
  • 21
  • 40
  • Can you create a plnker or fiddle for this? Would like to look into this issue a bit more. – Beyers Feb 15 '16 at 10:43
  • 1
    Here it is: https://plnkr.co/edit/r6byzhFX5m674ONhH1JS?p=preview – Javier Marín Feb 16 '16 at 14:25
  • As far as I can see what you are trying to do wont work. By design, transclude changes the way scopes are nested. It makes it so that the contents of a transcluded directive have whatever scope is outside the directive, rather than whatever scope is on the inside. It gives the contents access to the outside scope. So in your example the transcluded content inside `` and `` accesses the controller scope. There might be a way to get around this using the `transcludeFn` parameter of `link`. But I'm unsure how `transcludeFn` works with the multi-slot feature. – Beyers Feb 17 '16 at 18:20
  • I think I've found a solution. Will post back later today. – Beyers Feb 18 '16 at 05:49
  • I will wait your post – Javier Marín Feb 19 '16 at 10:51
  • Can you explain your solution, @Beyers? – Javier Marín Feb 20 '16 at 21:16
  • I'm sorry to report back that I haven't been able to find a solution for you. The closest I got is this: https://plnkr.co/edit/cmedSodmWtTe22bni9oS?p=preview Using the transclude function of link I can actually the correct data binding, but it doesn't work with actual transclusion. – Beyers Feb 23 '16 at 09:30
  • I see... Anyway, thank you for your attempt! – Javier Marín Feb 24 '16 at 13:32

2 Answers2

2

First, in the listing template you should change ng-transclude-slot="itemHeader" by ng-transclude="itemHeader" and ng-transclude-slot="itemFooter" by ng-transclude="itemFooter" to get the transclusion to work.

Then, you have in single and list example a mistake. If you change in the provided plunkr {items[0].name} by the expected {data.name} in the single example you will see that the name is not being displayed anymore. The second thing to do is to fix this shared problem.

atfornes
  • 468
  • 5
  • 21
  • That's true, I was using `ng-transclude-slot` when `ng-transclude` should have been used. I've updated the plunker. Any idea about how to solve the shared problem? – Javier Marín Feb 17 '16 at 00:21
  • please see if following questions can help: http://stackoverflow.com/questions/18794968/angular-directive-transclusion-and-inheritance and http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs – atfornes Feb 17 '16 at 14:12
1

TL;DR; Working example: https://plnkr.co/edit/1ideOiohle8AEzkDJ333?p=preview

The basic problem you're running into is that when you transclude, the scope you have access to is the top level scope. What you want is to reference the item level scope from outside the directive.

So, instead of using transclusion on the listing directive, I bind a string template as an attribute. I have to use a compile function to pick up the raw string value before angular pulls out the {{}} placeholders and then I use $interpolate to create template functions itemHeaderTemplate and itemFooterTemplate which are then used in the template like so ng-bind-html="itemHeaderTemplate({item: item})".

Note: In order to use ng-bind-html you need to include the ngSanitize module or you'll get a security exception.

Isaac
  • 2,173
  • 1
  • 13
  • 15