I am trying to create a component with multi-slot transclusion in Angular 6, following this blog post (which is for Angular 2).
I created a component:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-criteria',
template: `
<div class="adoption-grid-column adopter">
<div class="adoption-grid-icon ">
<ng-content select="level-icon"></ng-content>
</div>
<div class="adoption-grid-body">
<ng-content select="level-description"></ng-content>
</div>
</div>
`,
styles: []
})
export class CriteriaComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
and then I am trying to use it like this
<app-criteria>
<level-icon>
foo
</level-icon>
<level-description>
bar
</level-description>
</app-criteria>
But it throws a compile error:
ERROR in : 'level-icon' is not a known element
What am I missing here?
I realize I could create sub-components here, but I'm looking for a solution where I can pass blocks of html into slots in my component (such as bulleted lists, images, etc.)
Known limitations of our implementation are as follows: You cannot data-bind the slot's name attribute. You cannot data-bind the slot attribute. You cannot dynamically generate slot elements inside a component's view.
Transclusion is a concept that let's you create a content-slot / ng-content inside your component. So if you have a child component with a content-slot / ng-content, the parent component that uses the child component can put whatever it wants inside the content-slot of the child component.
The ng-content is used when we want to insert the content dynamically inside the component that helps to increase component reusability. Using ng-content we can pass content inside the component selector and when angular parses that content that appears at the place of ng-content.
The easiest solution (the one I prefer) is to create subcomponents with ng-content
as you mentioned. If you don't want to create such components, there are two things you can do.
CUSTOM_ELEMENTS_SCHEMA
You can tell angular to skip over the components it does not recognize by adding CUSTOM_ELEMENTS_SCHEMA
to schema
array of your feature module.
E.g.
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@NgModule({
imports: [...]
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class CustomModule { }
With this way, angular will not complain about level-icon
or level-description
and transclusion will work just fine. However, setting this option may hide other problems you could have. For example, you can make a typo when using a component and since you told angular you would have CUSTOM_ELEMENTS
, it will not give you an error. You end up debugging your code and wondering why the brand new component you just developed is not working.
When you write <ng-content select="level-icon"></ng-content>
angular will actually look for html elements called level-icon
. You can have it search for classes, attributes etc. So what you can do is to change this
<ng-content select="level-icon"></ng-content>
to
<ng-content select="[level-icon]"></ng-content>
or
<ng-content select=".level-icon"></ng-content>
And use your component as follows
<app-criteria>
<div level-icon>
foo
</div>
</app-criteria>
or
<app-criteria>
<div class="level-icon">
foo
</div>
</app-criteria>
With this way, you can select
for ul
, or img
. Basically anything you want.
If you still want to use <level-icon>
as element, you either have to create a subcomponent or use CUSTOM_ELEMENTS_SCHEMA
.
You can also use ngProjectAs
<div ngProjectAs="level-icon">...</div>
<div ngProjectAs="level-description">...</div>
In this case you don't need to disable tag validation
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With