Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 732
Steve Avatar asked Jul 06 '18 15:07

Steve


People also ask

What is the limitation of multiple slot content projection?

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.

What is Transclusion angular?

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.

What is Ngcontent in angular?

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.


2 Answers

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.

like image 87
Bunyamin Coskuner Avatar answered Sep 20 '22 20:09

Bunyamin Coskuner


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

like image 42
Alex Avatar answered Sep 21 '22 20:09

Alex