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
ng-content default content #12530
Comments
|
If i remember correctly thats the way multi slot transclusion works in ng 1 |
|
Is there any other way to have a fallback-content for the slot? I found a workaround but its quite ugly |
|
not yet |
|
I don't know if adding something inside |
This comment has been minimized.
This comment has been minimized.
|
Anything new on this feature request? |
|
@webmutation , theres a slight mistake in your solution: you should test if It should be like this: <div #ref><ng-content></ng-content></div>
<span *ngIf="ref.children.length == 0">
Display this if ng-content is empty!
</span> |
|
@acaua Actually that is not my code, but I agree node.children should be used and not node.childNodes, not that it is a mistake but falsely assumes that child nodes are of type Element. Even though it would be reasonable to assume that whatever is projected in ng-content would be of type Element, it can create issues, therefore the proper API should be used, and since node.children is supported for IE9+ cross browser support also is not an issue.
If we look at the polyfill for node.children we see the use of childNodes but it is filtered by node.nodeType === 1 (Where nodeType ===1 represents An Element node such as < p >or < div > ), so node.children should always work even when it needs to be polyfilled. @mbeckenbach You are correct that is the way it works in ng1, therefore it would be nice to keep it consistent, unless there is a good reason to make it different. Still hopping the ng1 behavior as suggested by OP gets added, since it was straightforward to use. |
|
Keep in mind that both
where This will result into
This is not the case when you use or or with as template will all result into nothing that is displayed. EDIT Using This will ignore nodes that are only whitespace and are comments (nodeType 8). |
|
being able to determine wether or not ng-content exists seems very important, and having an option that works with unviersal would be very nice. |
|
I have a similar requirement: Though I need something like this: |
|
@benneq I just succeeded with a small hack: export class ContentComponent implements AfterViewInit {
@ViewChild("contentAll")
contentAll: ElementRef;
constructor(private changeDetector: ChangeDetectorRef) { }
ngAfterViewInit(): void {
console.log(this.contentAll);
this.changeDetector.detectChanges();
}
}<div #contentAll *ngIf="contentAll ? contentAll.nativeElement.children.length : true">
<ng-content></ng-content>
</div>
I'm not sure if a HTML only fix is possible as I didn't get that to work. I did manage to clean up the view a bit if you prefer that: export class ContentComponent implements AfterViewInit {
@ViewChild("contentAll")
contentAll: ElementRef;
private hasContent: boolean = true;
constructor(private changeDetector: ChangeDetectorRef) { }
ngAfterViewInit(): void {
console.log(this.contentAll);
this.hasContent = this.contentAll.nativeElement.children.length;
this.changeDetector.detectChanges();
}
}<div #contentAll *ngIf="hasContent">
<ng-content></ng-content>
</div> |
|
Your cleaned up version only works if you only use this functionality once within a component. My example was a bit stripped down. In reality it's more like this: And each of |
|
@benneq i have the exact same case with a modal componenet, if footer is empty i don't want to render it. Here my warkaround in css (.scss here) html scss |
|
@WizardPC Thanks! I wanted to provide a default component if none was set in the ng-content. As they are siblings in my case, one could extend this with your solution. Which is compatible since IE 9 and partially since IE7/8: https://caniuse.com/#feat=css-sel3HTML <div class="my-custom-component-content-wrapper">
<ng-content select="my-custom-component"></ng-content>
</div>
<my-custom-component>
This shows something default.
</my-custom-component>CSS .my-custom-component-content-wrapper:not(:empty) + my-custom-component {
display: none;
} |
|
You can abstract the logic to show default content to directive: |
|
Does Ivy contribute to this? |
|
@dawidgarus greate solution i like it, but i get a problem if i do this. <li class="list-item" translate="SMC_UI.LIST.HEADER.TOTAL" [translateParams]="{COUNT: total}"></liIt throws an Exception: ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'COUNT: undefined'. Current value: 'COUNT: 0'. It seems like the view has been created after its parent and its children have been dirty checked. Has it been created in a change detection hook ? What helps me now is to wrap this into a window.setTimeout with a delay from 0, but it looks a bit hackish. public ngAfterContentChecked() {
const childNodes = Array.from(this.node.childNodes);
const hasContent = childNodes.some((node) => node.nodeType === 1 || node.nodeType === 3);
if (hasContent !== this.hasContent) {
this.hasContent = hasContent;
if (hasContent) {
this.container.clear();
} else {
window.setTimeout(() => {
this.container.createEmbeddedView(this.defaultTemplate)
}, 0);
}
}
} |
|
I have stupid set zero timeouts, allllllllllllll over our Angular code just to shut that type of error up. |
|
@AckerApple That error is typically due to you as developer not handling change detectin/state changes well. The issue appears when code is executed due to a change detection that changes the state again. This might or might not result in an infinite loop of change detection which is both very bad for performance and means a deeper flaw in how state is handled. |
|
@didii, no it’s not as simple as regurgitating some documentation. I know you may think you know the change detection issue and you’ve read that it’s highlighting a deeper issue but you can stop right there. Often I will bind an ID and a loading counter. When that ID changes, I update the binded loading counter and fetch data. In that scenario, I always setTimeout on my load counter increasing so displayed loading spinners can be displayed without causing the change detection error. A change can cause changes. Simple as that. I’ve been through all the documentation on this and still find the setTimeout the best and most widely used. |
|
Don't use |
|
@jcroll, both links provided by @didii, have references to recommending use of setTimeout. I've been up and down this topic too many times and concluded it's just so much less hassle to actually make use of setTimeout. My typical use case is for loading indicators. I could, do and have, used a shared provider class so nothing has to be binded at all. But I many times found myself not wanting to create a provider, register it, require it, and so on..... You cannot convince me not to use the setTimeout, I'm too confident in my Angular knowledge that I know when I can get away with a two-way bind that has a setTimeout to break the change cycle. I got it guys. I do. I'm going to continue using setTimeout and recommending it |
|
Yo @jcroll , my original comment was intended to give the guy @r-hannuschka a little confidence that the setTimeout is not a sin. I'll tell you what happens if I remove my setTimeouts.... I get dev errors only. |
|
I recently did a lot of homework on this subject thanks to others pushing me to do so. Slowest Faster, depending on what the window is currently working on until next repaint cycle Faster Second to Lastly, I theorize a faster way exists by NOT using Lastly, I got completely around the main issue in this thread by just not even trying to have default ng-content. It's been way long but I totally changed my approach and never needed default ng-content since. |
|
This seemed like the cleanest way to me: I'm making an assumption that the first element will have some form of text, which seems fair for most use cases. Doesn't give me any errors when using ChangeDetectionStrategy.OnPush |
|
Any update on this feature request? I am using this workaround for the meantime, its a bit 'hacky': Class Template: Usage: Neater way but without using ng-content itself: Class: Directive: Usage: |
|
@mhevery looking at this answer at StackOverflow, Can we have a clean solution like that please? this is 4 years old, |
|
It is on our radar, but we have higher priority items in front of it, so we don't have a commit date for it. |
|
Vue's slot implementation is truly elegant - default content just goes inside the slot tag, as the OP here suggested. It's incredibly simple and makes it nice and easy to use higher order components. Because it's handled by the core library there's no need to worry about which workaround you use and the compatibility between versions/situations etc. Angular has some catching up to do here. |
|
Is there any update years later? :) |
|
so many votes, such an elegant solution others are doing...and 5 years later. really ? |
|
@AndrewKushnir @alxhub this got stuck 4 months ago? |
|
I managed to workaround using @Component({
selector: "default-ng-content",
template: `
<ng-content select="[selector]"></ng-content>
<ng-template [ngIf]="useDefaultContent">
Here your default content
</ng-template>
`,
})
export class DefaultNgContentComponent implements AfterContentInit {
useDefaultContent = false;
constructor(private elRef: ElementRef<HTMLElement>) {}
ngAfterContentInit() {
// We check if there is the expected selector deeper in the DOM
const nav = this.elRef.nativeElement.querySelector("[selector]");
// If not, we use the default content.
this.useDefaultContent = nav === null;
}
} |
|
@AndrewKushnir This is something that I bump against time and time again. Feels like it's another basic feature/bug that the community has decided is a priority but Google has declared "NOT A PRIORITY". It has been almost five and a half (5.5) years. Would a community implementation be consider or accepted? I have about 6 issues like this that I track, basic Angular functionality that is missing/incorrect. I have dozens if not hundreds of instances of workarounds in my codebases. It's getting ridiculous. Removing any one class of workaround is going to be a major feat if/when the fix comes. Imagine if That's the position you're putting thousands of developers in by keeping issues like these open for half a decade or more. It's disrespectful. And, not something that most open source projects/communities would consider acceptable behavior. |
|
this exists and work ok in AngularDart. maybe Google could ask how they did it? |
|
@MonsieurMan i think this is a good solution, instead of a specific listener you can use a Directive which has a data selector which can be used as selector. If you expect it is a static value or it is allways existing or not (static) you can use ContentChild for this and skip afterContentInit or timeouts. @Directive(
selector: "[myContentDirective]"
)
class MyContentDirective {}
@Component({
template: `
<div ngIf="!defaultContent"> default content </div>
<ng-content select="[myContentDirective]"></ng-content>
`
})
class MyComponent {
// static true so we expect it exists as soon the component is rendered and not hidden by ngIf, nfSwitchCase, async
@ContentChild(MyContentDirective, { read: MyContentDirective, static: true })
private defaultContent: MyContentDirective;
} |

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

I'm submitting a ... (check one with "x")
Current behavior
When you add something inside a ng-content tag you get an error message: element cannot have content.
Expected behavior
Should render the html inside the ng-content tag as a default if no content was given.
Minimal reproduction of the problem with instructions
Create a component that uses ng-content somewhere. Put some html inside, that should be rendered when nothing else is 'injected' there from outside. Then use the component somewhere else.
What is the motivation / use case for changing the behavior?
In some cases you need a default behavior that can be replaced. In my case its an app header that shows the logo. When you navigate to a sub-page the logo will be replaced by a back button. It feels strange to add something like an input property to hide or show the logo. Some default content for ng content placeholders would feel much more intuitive.
Please tell us about your environment:
Win10, Angular CLI beta 18, VS Code
2.1.0
All
TS
node --version=The text was updated successfully, but these errors were encountered: