JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Вложенные компоненты в Angular

Вложенные компоненты в Angular

Модуль 4: Node.js, Next.js и Angular
14 уровень , 5 лекция
Открыта

1. Зачем нужны вложенные компоненты?

Angular — это не просто набор файлов и шаблонов, а целая архитектура, построенная вокруг компонентов. Каждый компонент — как деталь конструктора LEGO: сам по себе он что-то умеет, но настоящая магия начинается, когда вы соединяете детали вместе.

В реальном приложении редко бывает ситуация, когда на странице один-единственный компонент. Обычно есть "главная" страница (root component), внутри которой есть шапка, меню, контент, подвал, карточки, списки, формы и так далее. Всё это — компоненты, которые вложены друг в друга.

Аналогия: Представьте себе сайт как дом. Дом состоит из комнат (компонентов), комнаты — из мебели (подкомпонентов), мебель — из деталей… и так далее. В Angular вы строите такой дом с помощью иерархии компонентов.

Как устроена иерархия компонентов

В Angular всё начинается с "корневого" компонента — обычно это AppComponent. Он объявляется в файле main.ts как точка входа, а дальше строится дерево компонентов.


AppComponent
├── HeaderComponent
├── MenuComponent
├── ContentComponent
│   ├── ArticleListComponent
│   │   ├── ArticleCardComponent
│   │   ├── ArticleCardComponent
│   │   └── ArticleCardComponent
│   └── ArticleDetailsComponent
└── FooterComponent
Пример дерева вложенных компонентов

В жизни:
AppComponent — весь сайт.
— Внутри — header, меню, контент, подвал.
— Внутри контента — список статей, каждая статья — отдельный компонент.

2. Создание вложенных компонентов с помощью CLI

Angular CLI умеет создавать компоненты в подпапках, чтобы поддерживать порядок в проекте.

Пример

ng generate component shared/header
ng generate component shared/footer
ng generate component articles/article-list
ng generate component articles/article-card

Это создаст структуру:


src/app/
  shared/
    header/
      header.component.ts
      header.component.html
      ...
    footer/
      footer.component.ts
      ...
  articles/
    article-list/
    article-card/

Совет: Чем больше приложение, тем важнее логически группировать компоненты по папкам.

3. Как вставить компонент в шаблон другого компонента

Каждый компонент имеет свой "селектор" (selector) — строку, по которой Angular узнаёт, где разместить компонент в DOM.

Пример:
В компоненте HeaderComponent селектор будет, например, 'app-header'.

Чтобы вставить этот компонент в шаблон другого компонента, просто используйте селектор как HTML-тег:


<!-- app.component.html -->
<app-header></app-header>
<main>
  <app-article-list></app-article-list>
</main>
<app-footer></app-footer>

Важно:
— Имя селектора пишется в kebab-case (app-header, не AppHeader).
— Angular автоматически "узнает" тег и вставит туда содержимое компонента.

4. Standalone-компоненты и вложенность

В Angular 15+ можно создавать standalone-компоненты (без модулей). Но чтобы вложить один standalone-компонент в другой, нужно явно указать зависимость через массив imports.

Пример


// article-list.component.ts
import { Component } from '@angular/core';
import { ArticleCardComponent } from '../article-card/article-card.component';

@Component({
  selector: 'app-article-list',
  standalone: true,
  imports: [ArticleCardComponent], // <-- импортируем вложенный компонент
  template: `
    <h2>Список статей</h2>
    <app-article-card></app-article-card>
    <app-article-card></app-article-card>
  `
})
export class ArticleListComponent {}

Если забыть про imports:
Angular не узнает, что <app-article-card> — это компонент, и покажет ошибку в консоли.

5. Пример: строим мини-приложение «Список статей»

Давайте соберём простой пример с вложенными компонентами.
У нас будет:

  • AppComponent — корневой.
  • HeaderComponent — шапка.
  • ArticleListComponent — список статей.
  • ArticleCardComponent — карточка статьи.

Создаём компоненты

ng generate component shared/header --standalone
ng generate component articles/article-list --standalone
ng generate component articles/article-card --standalone

Реализуем компонент карточки статьи


// article-card.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-article-card',
  standalone: true,
  template: `
    <div class="card">
      <h3>{{ title }}</h3>
      <p>{{ summary }}</p>
    </div>
  `
})
export class ArticleCardComponent {
  @Input() title = '';
  @Input() summary = '';
}

Список статей — вставляем карточки


// article-list.component.ts
import { Component } from '@angular/core';
import { ArticleCardComponent } from '../article-card/article-card.component';

@Component({
  selector: 'app-article-list',
  standalone: true,
  imports: [ArticleCardComponent],
  template: `
    <h2>Статьи</h2>
    <app-article-card
      *ngFor="let article of articles"
      [title]="article.title"
      [summary]="article.summary">
    </app-article-card>
  `
})
export class ArticleListComponent {
  articles = [
    { title: 'Первый пост', summary: 'Краткое описание первого поста' },
    { title: 'Второй пост', summary: 'Что-то интересное про Angular' }
  ];
}

Корневой компонент — собираем всё вместе


// app.component.ts
import { Component } from '@angular/core';
import { HeaderComponent } from './shared/header/header.component';
import { ArticleListComponent } from './articles/article-list/article-list.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [HeaderComponent, ArticleListComponent],
  template: `
    <app-header></app-header>
    <app-article-list></app-article-list>
  `
})
export class AppComponent {}

Итоговая структура


AppComponent
├── HeaderComponent
└── ArticleListComponent
    └── ArticleCardComponent (много раз)

6. Полезные нюансы

Как Angular связывает компоненты между собой

Angular строит дерево компонентов (component tree) во время рендеринга. Каждый компонент — это отдельный класс + шаблон. Angular "понимает", что <app-article-card> — это не просто тег, а отдельный компонент, потому что вы его импортировали.

Важный момент:
Если один компонент вложен в другой, но не указан в imports, Angular не сможет его найти и покажет ошибку компиляции.

Как работает передача данных между компонентами

Вложенные компоненты часто получают данные от родительских через свойства с декоратором @Input().

Пример:
Родитель (ArticleListComponent) передаёт данные карточке (ArticleCardComponent):


<app-article-card
  [title]="article.title"
  [summary]="article.summary">
</app-article-card>

Внутри ArticleCardComponent есть свойства @Input() title и @Input() summary, которые получают значения.

Глубина вложенности и best practices

  • Не стоит делать слишком глубокую иерархию (10+ уровней) — это усложняет понимание кода.
  • Каждый компонент должен отвечать за свою "зону ответственности" (Single Responsibility Principle).
  • Вложенные компоненты помогают переиспользовать код и упрощают тестирование.

Схема: дерево компонентов

graph TD;
    AppComponent --> HeaderComponent
    AppComponent --> ArticleListComponent
    ArticleListComponent --> ArticleCardComponent
    ArticleCardComponent --> AuthorInfoComponent
  
Пример дерева компонентов

7. Типичные ошибки при работе с вложенными компонентами

Ошибка №1: забыли добавить компонент в imports.
Angular не поймёт, что <app-child> — это компонент, и покажет ошибку:

NG0903: Component 'AppChild' is not included in the module's imports array!

Ошибка №2: опечатка в селекторе.
Селектор должен совпадать с тем, что указан в компоненте. Если в шаблоне написано <app-articlecard>, а селектор — 'app-article-card', компонент не найдётся.

Ошибка №3: передача данных не через @Input().
Если попытаться передать данные просто как свойство (без декоратора @Input()), Angular проигнорирует это свойство.

Ошибка №4: циклическая вложенность.
Если компонент A вставляет компонент B, а B — снова A, возникает бесконечная рекурсия и приложение падает.

Ошибка №5: слишком большая ответственность компонента.
Если один компонент делает всё: и отображает список, и отдельные элементы, и логику фильтрации, и формы, — код становится нечитаемым. Разбивайте на вложенные компоненты!

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ