Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Nested transclude issue 2 #8914

Open
IgorMinar opened this issue Sep 3, 2014 · 9 comments
Open

Nested transclude issue 2 #8914

IgorMinar opened this issue Sep 3, 2014 · 9 comments

Comments

@IgorMinar
Copy link
Contributor

(reposting from (comment)[https://github.com//pull/7387#issuecomment-54363122])
JSFiddle example here – in the second test, the nested transclude is ignored.

It seems to lose the transcluded content when the ng-transclude is applied to the same element as the nested directive within the outer directive's template.

It works if the ng-transclude is applied to a wrapper element inside the nested directive (but then you're left with an irritating wrapper element).

@IgorMinar
Copy link
Contributor Author

@vojtajina could you take a look at this one please?

@tbosch
Copy link
Contributor

tbosch commented Sep 15, 2014

Reopening as the commit was reverted.

@tbosch tbosch reopened this Sep 15, 2014
@tbosch
Copy link
Contributor

tbosch commented Sep 15, 2014

See 06fa286

@tbosch tbosch assigned petebacondarwin and unassigned vojtajina Sep 15, 2014
@tbosch
Copy link
Contributor

tbosch commented Sep 15, 2014

@petebacondarwin could you have another look on this?
Please talk to @vojtajina for the reasoning why your fix was not correct...

@tbosch tbosch modified the milestones: 1.3.0-rc.2, 1.3.0-rc.3 Sep 15, 2014
@jeffbcross jeffbcross modified the milestones: 1.3.0-rc.3, 1.3.0 Sep 22, 2014
@fenduru
Copy link

fenduru commented Feb 5, 2015

👍 ran into this today

@teropa
Copy link
Contributor

teropa commented Jun 12, 2015

I think there's a conceptual mismatch between how the fiddle assumes transclusion to work and how it actually works.

In short, transclusion determines the content that will be transcluded during compilation, whereas the transclusion itself is actually done during linking. Whether it’s done with ng-transclude or by calling the bound transclusion function some other way, doesn’t really matter.

So with these directives:

app.directive('inner', function() {
    return {
        transclude: true,
        template: '<u ng-transclude></u>'
    };
});

app.directive('outer', function() {
    return {
        transclude: true,
        template: '<a href="#"><inner ng-transclude></inner></a>'
    };
});

app.directive('workaround', function() {
    return {
        transclude: true,
        template: '<a href="#"><inner><foo ng-transclude></foo></inner></a>'
    };
});

The fiddle’s assumption seems to be that this would happen with the when applied to <outer>✔</outer>:

  1. Outer’s content "✔" is stored for later transclusion

  2. Outer’s content is replaced with its template.

     <outer><a href="#"><inner ng-transclude></inner></a></outer>
    
  3. Inner’s content is replaced with "✔" from outer’s transclusion in step 1

    <outer><a href="#"><inner ng-transclude>✔</inner></a></outer>
    
  4. Inner’s content, now "✔" is stored for later transclusion

  5. Inner’s content is replaced with its template.

    <outer><a href="#"><inner ng-transclude><u ng-transclude></u></inner></a></outer>
    
  6. u’s content is replaced with "✔" from the inner transclusion in step 4

    <outer><a href="#"><inner ng-transclude><u ng-transclude>✔</u></inner></a></outer>
    

However, what actually happens is:

At compilation time:

  1. Outer’s content "✔" is stored for later transclusion

  2. Outer’s content is replaced with the template.

    <outer><a href="#"><inner ng-transclude></inner></a></outer>
    
  3. Inner’s content, "" is stored for later transclusion

  4. Inner’s content is replaced with its template

    <outer><a href="#"><inner ng-transclude><u ng-transclude></u></inner></a></outer>
    

At linking time

  1. The ng-transclude on inner is applied. It is "" from step 3.

    <outer><a href="#"><inner ng-transclude></inner></a></outer>
    

The ng-transclude on u is never done because the element is removed before that.

For the “workaround” case in the fiddle the mental model holds, because transclude: true and ng-transclude are never used on the same element, which is where the confusion begins.

There’s probably a way to fix this. To me it seems like it would mean a way for $compile to know not only where transclude: true is enabled, but also where transclusion is actually being applied. I don’t see away to do this without adding complexity and I feel like added complexity is the last thing $compile needs. :)

@fenduru
Copy link

fenduru commented Jun 12, 2015

@teropa thanks for the break down of why this is happening. However, while the jsbin's mental model might not match the current implementation, it does match the expectation of what the correct behavior would be -- this is kind of the definition of a "bug".

If you have a directive like outer with this template:

<a href="#"><inner ng-transclude></inner></a>
  1. It will work fine if <inner> does not use transclusion
  2. It will be broken if <inner> uses transclusion

This forces <outer> to have intimate implementation knowledge of <inner>, which is unreasonable. <inner> should be able to refactor their code to use transclude without breaking <outer>.

@teropa
Copy link
Contributor

teropa commented Jun 12, 2015

@fenduru Agreed, this is a leaky abstraction at the moment.

@petebacondarwin
Copy link
Member

@teropa - do you have any ideas of how to fix this? I have no capacity and this is a fairly uncommon corner case. Assigning to the Ice Box for now.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants