Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request : ng-content reuse (not multi-content projection) #9173

Closed
yjaaidi opened this issue Jun 13, 2016 · 16 comments
Closed

Feature request : ng-content reuse (not multi-content projection) #9173

yjaaidi opened this issue Jun 13, 2016 · 16 comments

Comments

@yjaaidi
Copy link
Contributor

yjaaidi commented Jun 13, 2016

Hi,

I'm trying to make a component where I want to reuse the same projected 'ng-content' but it doesn't seem to be possible. It would be nice if angular could iterate through all the unnamed ng-contents and replace them.

Here is a use case example.

@Component({
  selector: 'wt-link',
  template: `
<a *ngIf="!isRouteActive() [routerLink]="routerLink">
  <ng-content></ng-content>
</a>
<div *ngIf="isRouteActive()">
  <ng-content></ng-content>
</div>
`
})
class LinkComponent {
  ...
}
<wt-link [routerLink]=...>My Link Name</wt-link>

The link is displayed in another way if the route is active and I want more changes than simple CSS.

Is there any workaround?

@zoechi
Copy link
Contributor

zoechi commented Jun 13, 2016

Looks like a dup of #8563

@yjaaidi
Copy link
Contributor Author

yjaaidi commented Jun 13, 2016

Thanks @zoechi but it's not really the same issue as I don't want to handle this programmatically.

I just want to reuse "ng-content" as many times as I want without having to write any code in my controller.

@Component({
  selector: 'wt-punishment',
  template: `
   <ng-content></ng-content>
   <ng-content></ng-content>
   <ng-content></ng-content>
`
})
class PunishmentComponent{
}
<wt-punishment><div>I won't say that ReactJS is cool</div></wt-punishment>

Should output:

<wt-punishment>
    <div>I won't say that ReactJS is cool</div>
    <div>I won't say that ReactJS is cool</div>
    <div>I won't say that ReactJS is cool</div>
</wt-punishment>

@zoechi
Copy link
Contributor

zoechi commented Jun 13, 2016

As explained in the linked issue <ng-content> is for static projection. But lets the Angular team decide.

@mhevery
Copy link
Contributor

mhevery commented Jun 13, 2016

@yjaaidi This is done intentionally for the following reason:

Imagine that you have:

<third-party-component>
  <my-component></my-component>
</third-party-component>

Here a third-party-component is a component which the developer has not written and hence dose not know how it works internally. Additionally for all we know the third-party-component is a web-component and is not even part of angular.

Question: How many times should my-component be instantiated?
Answer: Exactly once! Because, any other number would be a surprise to the author of the template.

A component such as third-party-componentshould not have the ability to influence the instantiation of my-component.

For this reason the rule is that the only way to control the number of instantiations is with a template such as:

<third-party-component>
  <div *someDirective>
    <my-component></my-component>
  </div>
</third-party-component>

Here the * or template is a hint to the developer that we have a instantiation boundary, and that no assumptions about number of times or when the template would be instantiated can be made. We believe that is a good thing which aids readability and understanding of large projects as it avoids surprises.

The other feature of <ng-content> is that we can transplant the content of third-party-component into the third-party-component's , <ng-content> statically (at compile time.) This significantly improves the speed of the projection / your application runtime.

The side-effect of this is that the projection can be done exactly once. Which is why multiple <ng-content>'s are not supported. 1) Can't project twice and 2) it would break the rule about number of times the my-component would be instantiated.

@wardbell I think the action-item here is that we need to better document this behavior so that people are not surprised by it.

/cc: @pkozlowski-opensource

@yjaaidi
Copy link
Contributor Author

yjaaidi commented Jun 14, 2016

Thanks @mhevery for this complete reply!

What about triggering an error when a template has multiple unnamed ng-contents?

So, if I can't use projection? How could I implement this without having to write my own structural directive manually?

It would be nice to have the ability to create structural directives using templates so we can wrap native structural directives and allow multiple ng-content in structural directives.

For example:

@Directive({
    selector: 'wtIfNotNull',
    template: `
<ng-content *ngIf="value !== null"></ng-content>
`
})
class IfNotNullDirective{
    @Input('wtIfNotNull') value;
}

@pkozlowski-opensource
Copy link
Member

@mhevery thnx for the detailed write-up explaining the current reasoning. I got / agree with most of things but if you could elaborate on this:

A component such as third-party-component should not have the ability to influence the instantiation of my-component.

Actually I would love to better understand reasoning behind this statement. I mean, as an author of <my-component> shell I care about how many times I'm instantiated? Shouldn't a component by totally oblivious to when / how it is instantiated?

Not to mention that <third-party-component> could dynamically instantiate / insert any component and this wouldn't be visible from a template.

We believe that is a good thing which aids readability and understanding of large projects as it avoids surprises.

I think that we should clearly document those as honestly I see perf benefits of the current <ng-content> approach but I'm having hard time coming up with a use-case where it would significantly aid understanding / readability. One or two killer, documented examples would help make it obvious.

I can see how having <template> says "hey, this content might be instantiated or not, one or many times, in any point of time" but it doesn't give me much practical info. I mean - I know that I can't assume anything. But then again, if a component chooses to insert components dynamically (ViewContainerRef::createComponent) I won't see it from just looking at a template. So in a sense I can't make much assumptions about any component from just looking at a template...

@pkozlowski-opensource
Copy link
Member

Interestingly, based on @tbosch comment in #7795 this is a bug with a relatively easy fix.

@pkozlowski-opensource
Copy link
Member

OK, going to close this as a duplicate of #7795 and starting work on it.

@intellix
Copy link

intellix commented Jan 22, 2017

Two use-cases where this would have been nice to have:

Infinite horizontal scrolling of a navigation:

Looks like: ...Support ] [ Home, Contact, Support ] [ Home...
You would pass in the anchors you want in there:

<infinite-horizontal-nav>
  <a>Home</a>
  <a>Contact</a>
  <a>Support</a>
</infinite-horizontal-nav>

Which would create something like:

<infinite-horizontal-nav>
  <div class="group">
    <a>Home</a>
    <a>Contact</a>
    <a>Support</a>
  </div>
  <div class="group">
    <a>Home</a>
    <a>Contact</a>
    <a>Support</a>
  </div>
  <div class="group">
    <a>Home</a>
    <a>Contact</a>
    <a>Support</a>
  </div>
</infinite-horizontal-nav>

Slot machine

The only way I know in HTML to create a reel/spin effect is to repeat results and animate from a start until an end over a long set of elements. You would have a reel and pass in the potential results. The reel would handle the repeating and the animation:

<reel>
 <item result="bell"><item>
 <item result="diamond"><item>
 <item result="seven"><item>
</reel>

I suppose they can be achieved by passing in a dataset and then creating the structure from the parent, but then you also need to pass in options to customise the children should you need to.

@mlc-mlapis
Copy link
Contributor

Hmm, both cases can be usable. Back to @mhevery post above. Probably there could be something as default assumption. Then basic syntax and using:

<infinite-horizontal-nav>
  <a>Home</a>
  <a>Contact</a>
  <a>Support</a>
</infinite-horizontal-nav>

should mean that the ng-content is repeated only once by default.
If extended to:

<infinite-horizontal-nav [allow-repetition]="nn">
  <a>Home</a>
  <a>Contact</a>
  <a>Support</a>
</infinite-horizontal-nav>

then [allow-repetition]="0" could also mean "no repetition" and is the same as [allow-repetition]="1". Probably syntax "unlimited" is not necessary because it is same as something like [allow-repetition]="999999".

@intellix
Copy link

intellix commented Apr 17, 2017

Ran into this again today. This time I was trying to create an infinite horizontally scrolling marquee component: http://plnkr.co/edit/SvswTEZ5DhLH9UOTBds1?p=preview

Without being able to repeat over ng-content, I can't see how to achieve a re-useable marquee UI component at all, it's impossible without being able to duplicate the content:

<my-marquee>
  <div>Haribo found to cure the common cold</div>
  <div><b>BREAKING</b> Bin Laden<br>has risen from the dead</div>
</my-marquee>

@zoechi
Copy link
Contributor

zoechi commented Apr 17, 2017

Multiplying content is not the intended purpose of <ng-content>. Use a <template> (or <ng-template> in Angular4) and ngForTempalte or ngTemplateOutlet to stamp multiple instances.
See also

@abdel-ships-it
Copy link

@zoechi Thanks for pointing me in the right direction, for anyone struggling with this you can check this stackblitz example of how you would use ngTemplateOutlet to achieve this functionality.

@michaelbushe
Copy link

Similar to the stackblitz, check out nelisbijil's solution:
#22972 (comment)

@justDo1T
Copy link

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants