Angular Front-end Инженер

Angular Front-end Инженер

Роадмап навыков для прокачки

Базовое понимание standalone компонентов

AngularComponentsStandalone Components

Основная идея

Standalone компоненты — это компоненты Angular, которые не требуют объявления в NgModule. Они самодостаточны и сами управляют своими зависимостями через свойство imports в декораторе.

Ключевые аспекты

  • standalone: true — флаг в декораторе, делающий компонент независимым от модулей
  • Самодостаточность — компонент сам импортирует необходимые зависимости
  • Упрощённая архитектура — не нужно создавать и поддерживать NgModule
  • Angular 14+ — доступны начиная с версии 14, рекомендуемый подход с версии 17
  • Обратная совместимость — работают вместе с модульными компонентами

Преимущества standalone

  • Меньше boilerplate-кода (не нужен отдельный модуль)
  • Явные зависимости — видно, что использует компонент
  • Проще тестирование — не нужно конфигурировать TestBed с модулем
  • Tree-shaking работает эффективнее

Частые ошибки на собеседованиях

  • Путают standalone компоненты с компонентами без зависимостей
  • Не знают, что standalone компоненты нужно импортировать через imports, а не declarations
  • Забывают добавить standalone: true в декоратор
  • Не понимают, как миксовать standalone и модульные компоненты

Введение и проблематика

Традиционно в Angular каждый компонент должен быть объявлен в каком-либо NgModule. Это создаёт дополнительную сложность: нужно создавать модули, следить за declarations, imports, exports.

Standalone компоненты решают эту проблему — они самодостаточны и не требуют NgModule для работы.

Standalone компоненты появились в Angular 14 как developer preview и стали стабильными в Angular 15. С Angular 17 это рекомендуемый подход для новых проектов.

Проблемы модульного подхода

  • Boilerplate-код для каждого модуля
  • Сложность понимания зависимостей компонента
  • Необходимость импортировать модули целиком, даже если нужен один компонент
  • Сложнее tree-shaking неиспользуемого кода

Базовая теория

Что такое standalone компонент

Standalone компонент — это компонент с флагом standalone: true в декораторе. Он сам управляет своими зависимостями.

typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
 
@Component({
  selector: 'app-greeting',
  standalone: true, // Ключевой флаг
  imports: [CommonModule], // Зависимости указываются здесь
  template: `
    <h1 *ngIf="name">Hello, {{ name }}!</h1>
    <p *ngIf="!name">Please enter your name</p>
  `
})
export class GreetingComponent {
  name = '';
}

Сравнение с модульным подходом

typescript
// Standalone компонент — всё в одном файле
@Component({
  selector: 'app-user-card',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="card">
      <h2>{{ user.name }}</h2>
      <p>{{ user.email }}</p>
    </div>
  `
})
export class UserCardComponent {
  @Input() user!: User;
}
 
// Использование: просто импортируем компонент
@Component({
  standalone: true,
  imports: [UserCardComponent], // Импорт напрямую
  template: `<app-user-card [user]="currentUser" />`
})
export class ProfileComponent {}

Ключевые концепции

Флаг standalone: true

Добавление standalone: true делает компонент независимым от NgModule:

typescript
@Component({
  selector: 'app-counter',
  standalone: true, // Компонент самодостаточен
  template: `
    <button (click)="decrement()">-</button>
    <span>{{ count }}</span>
    <button (click)="increment()">+</button>
  `
})
export class CounterComponent {
  count = 0;
 
  increment(): void {
    this.count++;
  }
 
  decrement(): void {
    this.count--;
  }
}

Свойство imports

В imports указываются все зависимости компонента:

typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import { ButtonComponent } from './button.component';
 
@Component({
  selector: 'app-form',
  standalone: true,
  imports: [
    CommonModule,    // Для *ngIf, *ngFor, pipes
    FormsModule,     // Для ngModel
    RouterLink,      // Для routerLink
    ButtonComponent  // Другой standalone компонент
  ],
  template: `
    <form>
      <input [(ngModel)]="name" name="name">
      <app-button (click)="submit()">Submit</app-button>
      <a routerLink="/home">Back</a>
    </form>
  `
})
export class FormComponent {
  name = '';
 
  submit(): void {
    console.log('Submitted:', this.name);
  }
}
⚠️

Если используете директивы вроде *ngIf или *ngFor, не забудьте импортировать CommonModule или отдельные директивы NgIf, NgFor.


Практические примеры

Простой standalone компонент

typescript
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
 
@Component({
  selector: 'app-alert',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div
      class="alert"
      [class.alert-success]="type === 'success'"
      [class.alert-error]="type === 'error'"
      [class.alert-warning]="type === 'warning'"
    >
      <ng-content></ng-content>
    </div>
  `,
  styles: [`
    .alert { padding: 12px; border-radius: 4px; margin: 8px 0; }
    .alert-success { background: #d4edda; color: #155724; }
    .alert-error { background: #f8d7da; color: #721c24; }
    .alert-warning { background: #fff3cd; color: #856404; }
  `]
})
export class AlertComponent {
  @Input() type: 'success' | 'error' | 'warning' = 'success';
}

Использование standalone компонента в другом standalone

typescript
import { Component } from '@angular/core';
import { AlertComponent } from './alert.component';
 
@Component({
  selector: 'app-notifications',
  standalone: true,
  imports: [AlertComponent], // Импорт standalone компонента
  template: `
    <app-alert type="success">Операция выполнена успешно!</app-alert>
    <app-alert type="error">Произошла ошибка</app-alert>
  `
})
export class NotificationsComponent {}

Использование standalone в модульном компоненте

typescript
// Standalone компонент можно использовать в NgModule
@NgModule({
  declarations: [LegacyComponent],
  imports: [
    CommonModule,
    AlertComponent // Standalone компонент в imports, не в declarations!
  ]
})
export class LegacyModule {}
🚫

Standalone компоненты добавляются в imports, а НЕ в declarations. Это частая ошибка!


Преимущества standalone компонентов

АспектStandaloneМодульный
Boilerplate✅ Минимум❌ Нужен NgModule
Зависимости✅ Явные в компоненте⚠️ Разбросаны по модулям
Tree-shaking✅ Эффективнее⚠️ Модуль целиком
Тестирование✅ Проще⚠️ Нужен TestBed с модулем
Миграция✅ Постепенная

Пограничные кейсы

Bootstrap приложения со standalone

typescript
// main.ts — запуск приложения без AppModule
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
 
bootstrapApplication(AppComponent).catch(err => console.error(err));

Standalone директивы и pipes

Не только компоненты, но и директивы/pipes могут быть standalone:

typescript
@Directive({
  selector: '[appHighlight]',
  standalone: true
})
export class HighlightDirective {
  // ...
}
 
@Pipe({
  name: 'truncate',
  standalone: true
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, length: number): string {
    return value.length > length ? value.slice(0, length) + '...' : value;
  }
}
 
// Использование
@Component({
  standalone: true,
  imports: [HighlightDirective, TruncatePipe],
  template: `<p appHighlight>{{ text | truncate:50 }}</p>`
})
export class MyComponent {}

Вопросы интервьюера

Q: Что такое standalone компонент?

Компонент с флагом standalone: true, который не требует объявления в NgModule и сам управляет своими зависимостями через imports.

Q: Как использовать standalone компонент в другом компоненте?

Добавить его в массив imports декоратора, а не в declarations.

Q: Можно ли использовать standalone и модульные компоненты вместе?

Да, они полностью совместимы. Standalone компоненты можно импортировать в NgModule через imports.

Q: С какой версии Angular доступны standalone компоненты?

Developer preview в Angular 14, стабильные в Angular 15, рекомендуемый подход с Angular 17.


Источники