ДокументацияAngularКомпоненты Angular
Начальный 9 мин чтения

Компоненты Angular

Компоненты — строительные блоки Angular-приложений. Каждый компонент управляет частью экрана и состоит из шаблона, стилей и класса TypeScript.

AngularcomponentsdecoratorsTypeScript

Анатомия компонента

Angular-компонент состоит из трёх частей:

// user-card.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core'
import { CommonModule } from '@angular/common'

@Component({
  selector: 'app-user-card',     // HTML-тег для использования компонента
  standalone: true,              // Standalone компонент (Angular 15+)
  imports: [CommonModule],       // Импортируемые зависимости
  templateUrl: './user-card.component.html',
  styleUrl: './user-card.component.css',
})
export class UserCardComponent {
  @Input() name = ''
  @Input() age = 0
  @Output() clicked = new EventEmitter<string>()

  onClick() {
    this.clicked.emit(this.name)
  }
}
<!-- user-card.component.html -->
<div class="card" (click)="onClick()">
  <h3>{{ name }}</h3>
  <p>{{ age }} лет</p>
</div>

Шаблонный синтаксис

Интерполяция и привязка

<!-- Вывод данных -->
<h1>{{ title }}</h1>
<p>{{ user.name | uppercase }}</p>

<!-- Привязка свойств [property] -->
<img [src]="user.avatar" [alt]="user.name">
<button [disabled]="isLoading">Загрузить</button>

<!-- Привязка событий (event) -->
<button (click)="handleClick()">Нажми</button>
<input (input)="onInput($event)">

<!-- Двусторонняя привязка [(ngModel)] -->
<input [(ngModel)]="searchQuery" placeholder="Поиск">

Директивы управления потоком (Angular 17+)

<!-- @if -->
@if (user) {
  <div>{{ user.name }}</div>
} @else if (loading) {
  <div>Загрузка...</div>
} @else {
  <div>Пользователь не найден</div>
}

<!-- @for -->
@for (item of items; track item.id) {
  <li>{{ item.name }}</li>
} @empty {
  <li>Список пуст</li>
}

<!-- @switch -->
@switch (status) {
  @case ('active') { <span class="green">Активен</span> }
  @case ('inactive') { <span class="red">Неактивен</span> }
  @default { <span>Неизвестно</span> }
}

Старый синтаксис директив (ngIf, ngFor)

<!-- *ngIf -->
<div *ngIf="isVisible; else hidden">Видимый блок</div>
<ng-template #hidden>Скрытый блок</ng-template>

<!-- *ngFor -->
<li *ngFor="let item of items; let i = index; trackBy: trackById">
  {{ i + 1 }}. {{ item.name }}
</li>

Input и Output

// Дочерний компонент
@Component({ selector: 'app-counter', standalone: true, ... })
export class CounterComponent {
  @Input({ required: true }) initialValue!: number  // Angular 16+
  @Output() valueChange = new EventEmitter<number>()

  count = 0

  ngOnInit() {
    this.count = this.initialValue
  }

  increment() {
    this.count++
    this.valueChange.emit(this.count)
  }
}
<!-- Родитель -->
<app-counter
  [initialValue]="5"
  (valueChange)="onCountChange($event)"
/>

Lifecycle Hooks

import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges } from '@angular/core'

@Component({ ... })
export class MyComponent implements OnInit, OnDestroy, OnChanges {

  ngOnChanges(changes: SimpleChanges) {
    // При изменении Input-свойств
    if (changes['userId']) {
      this.loadUser(changes['userId'].currentValue)
    }
  }

  ngOnInit() {
    // Компонент инициализирован, Input-свойства доступны
    this.loadData()
  }

  ngOnDestroy() {
    // Очистка — отписки от Observable
    this.subscription?.unsubscribe()
  }
}

ViewChild и ContentChild

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core'

@Component({
  template: `<input #nameInput placeholder="Имя">`,
})
export class FormComponent implements AfterViewInit {
  @ViewChild('nameInput') nameInput!: ElementRef<HTMLInputElement>

  ngAfterViewInit() {
    this.nameInput.nativeElement.focus()
  }
}

Pipes (Каналы)

<!-- Встроенные pipes -->
{{ name | uppercase }}
{{ price | currency:'RUB':'symbol':'1.0-0' }}
{{ date | date:'dd.MM.yyyy' }}
{{ data | json }}
{{ text | slice:0:100 }}
{{ items | async }}  <!-- Для Observable/Promise -->
// Кастомный pipe
import { Pipe, PipeTransform } from '@angular/core'

@Pipe({ name: 'pluralize', standalone: true })
export class PluralizePipe implements PipeTransform {
  transform(value: number, words: [string, string, string]): string {
    const cases = [2, 0, 1, 1, 1, 2]
    return `${value} ${words[
      value % 100 > 4 && value % 100 < 20
        ? 2
        : cases[Math.min(value % 10, 5)]
    ]}`
  }
}

// {{ count | pluralize:['задача','задачи','задач'] }}