Что такое компоненты в Angular?

Компоненты в Angular — это базовые строительные блоки пользовательского интерфейса. Каждый компонент управляет частью экрана, отображает данные, реагирует на действия пользователя и взаимодействует с другими компонентами и сервисами. Компонент в Angular объединяет шаблон (HTML), логику (TypeScript) и стили (CSS/SCSS) в единое целое, позволяя создавать изолированные, переиспользуемые и модульные части интерфейса.

1. Структура компонента

Компонент — это обычный TypeScript-класс, аннотированный декоратором @Component, который сообщает Angular метаинформацию:

import { Component } from '@angular/core';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: \['./user.component.scss'\]
})
export class UserComponent {
name = 'Иван';
}

Основные элементы:

  • selector — имя HTML-тега, с помощью которого компонент вставляется в шаблон.

  • template / templateUrl — HTML-шаблон компонента.

  • styles / styleUrls — стили, применяемые к компоненту.

  • Класс компонента содержит свойства, методы, привязки, декораторы (@Input, @Output и др.).

2. Шаблон компонента

HTML-шаблон компонента может содержать:

  • Интерполяцию ({{ }}) для вывода значений.

  • Привязки данных ([property], {{property}}, [(ngModel)]).

  • Привязки событий ((click), (input)).

  • Структурные директивы (*ngIf, *ngFor).

  • Вложенные компоненты.

<h1>{{ name }}</h1>
<button (click)="changeName()">Изменить</button>

3. Стилизация

Каждый компонент по умолчанию использует View Encapsulation (инкапсуляция стилей), что означает, что его стили применяются только к его шаблону.

Три режима:

  • Emulated (по умолчанию) — Angular эмулирует Shadow DOM с помощью атрибутов (_nghost, _ngcontent).

  • None — глобальные стили.

  • ShadowDom — нативный Shadow DOM.

@Component({
encapsulation: ViewEncapsulation.Emulated,
...
})

4. Вложенные компоненты

Компоненты можно использовать как теги внутри других:

<app-user></app-user>
<app-product-card></app-product-card>

Они связаны через selector, и Angular автоматически их находит при сборке, если они объявлены в NgModule.

5. @Input() — входные параметры

Позволяет передавать данные в дочерний компонент из родительского.

@Component({
selector: 'app-user-card',
template: \`<h3>{{ user.name }}</h3>\`
})
export class UserCardComponent {
@Input() user: { name: string };
}
<app-user-card \[user\]="currentUser"></app-user-card>

6. @Output() — отправка событий наверх

Используется для передачи событий от дочернего компонента родителю через EventEmitter.

@Component({
selector: 'app-like-button',
template: \`<button (click)="like()">Like</button>\`
})
export class LikeButtonComponent {
@Output() liked = new EventEmitter<number>();
private count = 0;
like() {
this.count++;
this.liked.emit(this.count);
}
}
<app-like-button (liked)="onLiked($event)"></app-like-button>

7. Жизненный цикл компонента

Каждый компонент проходит определённые этапы жизненного цикла, которые можно обрабатывать с помощью хуков:

Хук Описание
ngOnInit() Вызывается после инициализации компонента.
--- ---
ngOnChanges(changes) Вызывается при изменении @Input свойств.
--- ---
ngDoCheck() Вызывается при каждом цикле обнаружения изменений.
--- ---
ngAfterViewInit() После инициализации представления.
--- ---
ngOnDestroy() Перед уничтожением компонента.
--- ---

Пример:

export class MyComponent implements OnInit, OnDestroy {
ngOnInit() {
console.log('Component initialized');
}
ngOnDestroy() {
console.log('Component destroyed');
}
}

8. Динамические компоненты

Можно создавать компоненты в рантайме с помощью ViewContainerRef и ComponentFactoryResolver:

constructor(private viewContainer: ViewContainerRef) {}
load() {
this.viewContainer.createComponent(MyDynamicComponent);
}

С Angular 14+ рекомендуется использовать createComponent() без ComponentFactoryResolver.

9. Компоненты и модули

Чтобы использовать компонент в шаблоне, он должен быть объявлен (declarations) в одном из Angular модулей (@NgModule), и если он используется в другом модуле — экспортирован.

@NgModule({
declarations: \[UserComponent\],
exports: \[UserComponent\]
})
export class SharedModule {}

10. Связывание с формами

Компоненты можно использовать с формами через @Input()/@Output() или через ControlValueAccessor (создание кастомных элементов формы):

@Component({
selector: 'app-input',
template: \`<input \[value\]="value" (input)="onChange($event.target.value)">\`
})
export class CustomInputComponent implements ControlValueAccessor {
value: string;
onChange = (val: string) => {};
writeValue(val: string) {
this.value = val;
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {}
}

11. Компоненты и маршрутизация

В маршрутах Angular можно указывать компонент, который будет отображаться при переходе:

const routes: Routes = \[
{ path: 'profile', component: ProfileComponent }
\];

Использование в шаблоне:

<router-outlet></router-outlet>

Компонент, указанный в маршруте, будет отрисован в <router-outlet>.

12. Отложенная (Lazy) загрузка компонентов

Компоненты можно загружать по требованию с использованием маршрутизации и lazy модулей:

{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
}

13. Сервисные компоненты

Некоторые компоненты не отображают UI, а управляют логикой (например, SnackbarComponent, ModalComponent) и создаются через сервисы:

this.dialog.open(MyModalComponent, { data: user });

14. Инкапсуляция и изоляция

Каждый компонент изолирован:

  • шаблон не влияет на родительский,

  • стили не “протекают” (если используется Emulated),

  • состояние управляется локально или через сервисы/инъекции.

15. Тестирование компонентов

Angular предоставляет инструменты для unit и integration тестов компонентов с использованием TestBed:

describe('UserComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: \[UserComponent\]
}).compileComponents();
});
});

16. Примеры использования

&lt;!-- app.component.html --&gt;
&lt;h1&gt;Главная страница&lt;/h1&gt;
&lt;app-user-card \[user\]="user" (selected)="onUserSelect($event)"&gt;&lt;/app-user-card&gt;
export class AppComponent {
user = { name: 'Иван' };
onUserSelect(user) {
console.log('Выбран пользователь:', user);
}
}
@Component({
selector: 'app-user-card',
template: \`
&lt;div (click)="select()"&gt;{{ user.name }}&lt;/div&gt;
\`
})
export class UserCardComponent {
@Input() user: any;
@Output() selected = new EventEmitter();
select() {
this.selected.emit(this.user);
}
}