Проекция контента

В этом посте я отвечу на 3 основных вопроса (Что, Почему и Как).

Что такое проекция содержания?

В Angular проекция содержимого используется для проецирования содержимого в компоненте (angular.io).

Почему мы его используем?

  1. Многие компоненты в вашем приложении используют одинаковую структуру и стиль, но их содержимое отличается, другими словами, возможность повторного использования.
  2. Вы создаете компонент только для отображения, а другой компонент - для обработки действий пользователя, другими словами Разделение проблем.

Как мы это используем?

С одним слотом

В основном вы просто добавляете <ng-content></ng-content> в свой html, и он будет заменен содержимым извне компонента.

<!-- inside container component -->
<ng-content></ng-content>
<!-- inside another component -->
<container-component> <p>Content Here</p> </container-component>

Многослотовая (целевая проекция)

ng-content принимает атрибут select, который позволяет нам установить конкретное имя селектора CSS для этого слота.

  • Использование элемента (ов) name
<!-- inside container component -->
<ng-content select="slot-one"></ng-content>
<!-- inside another component using container component -->
<container-component>
  <slot-one>Content For Slot one</slot-one>
</container-component>

Если вы используете его в обычной настройке angular cli, вы столкнетесь с ошибкой, если сейчас используете тег <slot-one>.

Отклонение необработанного обещания: ошибки синтаксического анализа шаблона: элемент "slot-one" неизвестен, Angular не распознает тег slot-one. slot-one не является ни директивой, ни компонентом.

Быстрый способ обойти эту ошибку - добавить свойство метаданных схемы в ваш модуль, установить значение NO_ERRORS_SCHEMA в вашем файле модуля.

// app.module.ts
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; //
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ContainerComponent } from './container-component';
@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent, ContainerComponent],
  bootstrap: [AppComponent],
  schemas: [NO_ERRORS_SCHEMA] // add this line
})
export class AppModule {}
  • Использование атрибутов [name] | [name][another-name]
<!-- inside container component -->
<!-- Using Single Attribute -->
<ng-content select="[slot-one]"></ng-content>
<!-- Using Multiple Attributes -->
<ng-content select="[slot][two]"></ng-content>
<!-- inside another component using container component -->
<container-component>
  <p slot-one>Content For Slot one</p>
  <p slot two>Content For Slot two</p>
</container-component>
  • Использование атрибута со значением [name="vlue"]
<!-- inside container component -->
<ng-content select="[slot='one']"></ng-content>
<ng-content select="[slot='two']"></ng-content>
<!-- inside another component using container component -->
<container-component>
  <p slot="one">Content For Slot one</p>
  <p slot="two">Content For Slot two</p>
</container-component>
  • Использование класса (ов) .name | .name.another-name
<!-- inside container component -->
<!-- Using Single Class -->
<ng-content select=".slot-one"></ng-content>
<!-- Using Multi Class -->
<ng-content select=".slot.two"></ng-content>
<!-- inside another component using container component -->
<container-component>
  <p class="slot-one">Content For Slot one</p>
</container-component>
  • Без использования обертывания div

как вы можете видеть в предыдущем примере, вы можете использовать целевой слот, обернув свой контент с помощью div или element и прикрепив к нему селектор, но в некоторых кассах вы просто хотите поместить его туда.

Использование атрибута ngProjectAs angular в теге ng-container или желаемом теге and.

<!-- inside container component -->
<!-- Element -->
<ng-content select="slot-one"></ng-content>
<!-- Attributes -->
<ng-content select="[slot-one]"></ng-content>
<ng-content select="[slot][two]"></ng-content>
<!-- Attributes with Value -->
<ng-content select="[slot='one']"></ng-content>
<ng-content select="[slot='two']"></ng-content>
<!-- Inside ngProjectAs use projected name-->
<ng-container ngProjectAs="slot-one">
  Very <strong>important</strong> text with tags.
</ng-container>
<ng-container ngProjectAs="[slot][two]">
  Very <strong>important</strong> text with tags.
</ng-container>

Внутри *ngFor

// Container Component
@Component({
  ...
  template: `
    <li *ngFor="let item of items">
      <ng-template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{$implicit: item}"></ng-template>
    </li>
  `
})
class TabsComponent {
  @ContentChild(TemplateRef) templateRef:TemplateRef;
  @Input() items;
}
<!-- data here is the array  input for container-component -->
<container-component [items]="data">
  <ng-template let-item>
    <!-- here we can use item -->
    {{ item }}
  </ng-template>
</container-component>