Spec-Zone.ru › Angular

Создание шаблонных фрагментов с помощью ng-template

По аналогии со стандартным элементом <template>, элемент <ng-template> позволяет объявить шаблонный фрагмент — часть содержимого, которую можно динамически или программно отобразить.

Создание шаблонного фрагмента

Шаблонный фрагмент можно создать в шаблоне любого компонента с помощью элемента <ng-template>:

<p>This is a normal element</p>

<ng-template>
  <p>This is a template fragment</p>
</ng-template>

При отображении приведённого выше примера содержимое элемента <ng-template> не появляется на странице. Вместо этого можно получить ссылку на шаблонный фрагмент и написать код для его динамического отображения.

Контекст привязки для фрагментов

Шаблонные фрагменты могут содержать привязки с динамическими выражениями:

@Component({
  /* ... */,
  template: `<ng-template>You've selected {{count}} items.</ng-template>`,
})
export class ItemCounter {
  count: number = 0;
}

Выражения или инструкции в шаблонном фрагменте вычисляются относительно компонента, в котором объявлен фрагмент, независимо от того, где он отображается.

Получение ссылки на шаблонный фрагмент

Получить ссылку на шаблонный фрагмент можно одним из трёх способов:

  • Объявить переменную-ссылку на шаблон для элемента <ng-template>
  • Найти фрагмент с помощью запроса компонента или директивы
  • Внедрить фрагмент в директиву, применённую непосредственно к элементу <ng-template>.

Во всех трёх случаях фрагмент представлен объектом TemplateRef.

Ссылка на шаблонный фрагмент с помощью переменной-ссылки на шаблон

Чтобы ссылаться на шаблонный фрагмент в других частях того же файла шаблона, можно добавить переменную-ссылку на шаблон к элементу <ng-template>:

<p>This is a normal element</p>

<ng-template #myFragment>
  <p>This is a template fragment</p>
</ng-template>

Затем на этот фрагмент можно ссылаться в любом месте шаблона с помощью переменной myFragment.

Ссылка на шаблонный фрагмент с помощью запросов

Получить ссылку на шаблонный фрагмент можно с помощью любого API запросов компонентов или директив.

Можно напрямую запросить объект TemplateRef с помощью запроса viewChild.

@Component({
  /* ... */,
  template: `
    <p>This is a normal element</p>

    <ng-template>
      <p>This is a template fragment</p>
    </ng-template>
  `,
})
export class ComponentWithFragment {
  templateRef = viewChild<TemplateRef<unknown>>(TemplateRef);
}

Затем на этот фрагмент можно ссылаться в коде компонента или его шаблоне, как и на любой другой член класса.

Если шаблон содержит несколько фрагментов, каждому фрагменту можно назначить имя, добавив переменную-ссылку на шаблон к каждому элементу <ng-template> и запросив фрагменты по этим именам:

@Component({
  /* ... */,
  template: `
    <p>This is a normal element</p>

    <ng-template #fragmentOne>
      <p>This is one template fragment</p>
    </ng-template>

    <ng-template #fragmentTwo>
      <p>This is another template fragment</p>
    </ng-template>
  `,
})
export class ComponentWithFragment {
    fragmentOne = viewChild<TemplateRef<unknown>>('fragmentOne');
    fragmentTwo = viewChild<TemplateRef<unknown>>('fragmentTwo');
}

Затем на эти фрагменты можно ссылаться в коде компонента или его шаблоне, как и на любые другие члены класса.

Внедрение шаблонного фрагмента

Директива может внедрить TemplateRef, если она непосредственно применена к элементу <ng-template>:

@Directive({
  selector: '[myDirective]',
})
export class MyDirective {
  private fragment = inject(TemplateRef);
}
<ng-template myDirective>
  <p>This is one template fragment</p>
</ng-template>

Затем на этот фрагмент можно ссылаться в коде директивы, как и на любой другой член класса.

Отображение шаблонного фрагмента

Получив ссылку на объект TemplateRef шаблонного фрагмента, его можно отобразить одним из двух способов: в шаблоне с помощью директивы NgTemplateOutlet или в коде TypeScript с помощью ViewContainerRef.

Использование NgTemplateOutlet

Директива NgTemplateOutlet из @angular/common принимает TemplateRef и отображает фрагмент как соседний элемент элемента с точкой вставки. Как правило, NgTemplateOutlet следует использовать с элементом <ng-container>.

Сначала импортируйте NgTemplateOutlet:

import {NgTemplateOutlet} from '@angular/common';

В следующем примере объявляется шаблонный фрагмент и отображается внутри элемента <ng-container> с помощью NgTemplateOutlet:

<p>This is a normal element</p>

<ng-template #myFragment>
  <p>This is a fragment</p>
</ng-template>

<ng-container *ngTemplateOutlet="myFragment"></ng-container>

В результате этого примера получается следующая отображённая DOM-структура:

<p>This is a normal element</p>
<p>This is a fragment</p>

Использование ViewContainerRef

Контейнер представления — это узел в дереве компонентов Angular, который может содержать содержимое. Любой компонент или директива могут внедрить ViewContainerRef, чтобы получить ссылку на контейнер представления, соответствующий расположению этого компонента или директивы в DOM.

Для динамического отображения шаблонного фрагмента можно использовать метод createEmbeddedView объекта ViewContainerRef. При отображении фрагмента с помощью ViewContainerRef Angular добавляет его в DOM как следующий соседний элемент компонента или директивы, внедривших ViewContainerRef.

В следующем примере показан компонент, который принимает ссылку на шаблонный фрагмент в качестве входного параметра и отображает этот фрагмент в DOM при нажатии кнопки.

@Component({
  /* ... */,
  selector: 'component-with-fragment',
  template: `
    <h2>Component with a fragment</h2>
    <ng-template #myFragment>
      <p>This is the fragment</p>
    </ng-template>
    <my-outlet [fragment]="myFragment" />
  `,
})
export class ComponentWithFragment { }

@Component({
  /* ... */,
  selector: 'my-outlet',
  template: `<button (click)="showFragment()">Show</button>`,
})
export class MyOutlet {
  private viewContainer = inject(ViewContainerRef);
  fragment = input<TemplateRef<unknown> | undefined>();

  showFragment() {
    if (this.fragment()) {
      this.viewContainer.createEmbeddedView(this.fragment());
    }
  }
}

В приведённом выше примере нажатие кнопки «Показать» приводит к следующему результату:

<component-with-fragment>
  <h2>Component with a fragment>
  <my-outlet>
    <button>Show</button>
  </my-outlet>
  <p>This is the fragment</p>
</component-with-fragment>

Передача параметров при отображении шаблонного фрагмента

Объявляя шаблонный фрагмент с помощью <ng-template>, можно дополнительно указать параметры, которые он принимает. При отображении фрагмента можно передать объект context, соответствующий этим параметрам. Данные из этого объекта контекста можно использовать в выражениях привязки и инструкциях наряду с данными компонента, в котором объявлен фрагмент.

Каждый параметр записывается как атрибут с префиксом let- и значением, соответствующим имени свойства в объекте контекста:

<ng-template let-pizzaTopping="topping">
  <p>You selected: {{ pizzaTopping }}</p>
</ng-template>

Использование NgTemplateOutlet с параметрами

Объект контекста можно привязать ко входному параметру ngTemplateOutletContext:

<ng-template #myFragment let-pizzaTopping="topping">
  <p>You selected: {{ pizzaTopping }}</p>
</ng-template>

<ng-container [ngTemplateOutlet]="myFragment" [ngTemplateOutletContext]="{topping: 'onion'}" />

Использование ViewContainerRef с параметрами

Объект контекста можно передать вторым аргументом в createEmbeddedView:

this.viewContainer.createEmbeddedView(this.myFragment, {topping: 'onion'});

Передача инъекторов шаблонным фрагментам

При отображении шаблонного фрагмента его контекст инъектора определяется местом объявления шаблона, а не местом его отображения. Это поведение можно переопределить, передав пользовательский инъектор.

Использование NgTemplateOutlet с инъекторами

Пользовательский Injector можно передать во входной параметр ngTemplateOutletInjector:

export const THEME_DATA = new InjectionToken<string>('THEME_DATA', {
  factory: () => 'light',
});

@Component({
  selector: 'themed-panel',
  template: `<div [class]="theme">...</div>`,
})
export class ThemedPanel {
  theme = inject(THEME_DATA);
}

@Component({
  selector: 'root',
  imports: [NgTemplateOutlet, ThemedPanel],
  template: `
    <ng-template #myFragment>
      <themed-panel />
    </ng-template>
    <ng-container *ngTemplateOutlet="myFragment; injector: customInjector" />
  `,
})
export class Root {
  customInjector = Injector.create({
    providers: [{provide: THEME_DATA, useValue: 'dark'}],
  });
}

Наследование инъектора точки вставки

Можно присвоить ngTemplateOutletInjector строковое значение 'outlet', чтобы внедрённое представление наследовало инъектор из расположения точки вставки в DOM, а не из места объявления шаблона.

<ng-template #node let-items>
  <item-component>
    @for (child of items; track $index) {
      <ng-container
        *ngTemplateOutlet="node; context: {$implicit: child.children}; injector: 'outlet'"
      />
    }
  </item-component>
</ng-template>

<ng-container *ngTemplateOutlet="node; context: {$implicit: topLevelItems}" />

Каждое рекурсивное отображение шаблона node наследует инъектор из окружающего <item-component>, позволяя каждому вложенному уровню обращаться к провайдерам, область видимости которых ограничена родительским компонентом.

ПРИМЕЧАНИЕ: Это полезно при создании рекурсивных структур и в любых ситуациях, когда отображаемому шаблону нужен доступ к провайдерам из дерева компонентов в месте размещения точки вставки.

Использование ViewContainerRef с инъекторами

Пользовательский инъектор можно передать в составе объекта параметров в createEmbeddedView:

this.viewContainer.createEmbeddedView(this.myFragment, context, {
  injector: myCustomInjector,
});

Структурные директивы

Структурная директива — это любая директива, которая:

  • Внедряет TemplateRef
  • Внедряет ViewContainerRef и программно отображает внедрённый TemplateRef

Для структурных директив Angular поддерживает специальный сокращённый синтаксис. Если применить директиву к элементу и поставить перед селектором директивы символ звёздочки (*), Angular интерпретирует весь элемент и всё его содержимое как шаблонный фрагмент:

<section *myDirective>
  <p>This is a fragment</p>
</section>

Это эквивалентно следующему:

<ng-template myDirective>
  <section>
    <p>This is a fragment</p>
  </section>
</ng-template>

Разработчики обычно используют структурные директивы для условного отображения фрагментов или многократного отображения фрагментов.

Дополнительные сведения см. в разделе «Структурные директивы».

Дополнительные ресурсы

Примеры использования ng-template в других библиотеках см. в следующих материалах:

  • Вкладки Angular Material — содержимое не добавляется в DOM, пока не активирована вкладка
  • Таблица Angular Material — позволяет разработчикам определять разные способы отображения данных

Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/templates/ng-template

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API