Skip to content
Advertisement

Multi-slot transclusion in Angular 6

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.)

Advertisement

Answer

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.

  1. 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.

  1. Selecting classes or attributes other than elements.

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.

Advertisement