Перейти к основному содержимому

200 вопросов по Angular

200 вопросов по Angular

Основы Angular

Вопрос

Что такое Angular?

Ответ

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


Вопрос

В чём разница между AngularJS и Angular?

Ответ

AngularJS — это первая версия фреймворка, написанная на JavaScript и использующая концепцию двухстороннего связывания данных. Angular (начиная с версии 2) — это полностью переписанная платформа на TypeScript с компонентной архитектурой, однонаправленным потоком данных и современными возможностями, такими как Ahead-of-Time компиляция и модульность.


Вопрос

Какие основные строительные блоки приложения на Angular?

Ответ

Основные строительные блоки Angular-приложения:

  • Компоненты
  • Модули
  • Шаблоны
  • Метаданные
  • Сервисы
  • Директивы
  • Пайпы
  • Роутинг
  • Dependency Injection

Вопрос

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

Ответ

Компонент — это базовая единица пользовательского интерфейса в Angular. Он состоит из класса TypeScript с декоратором @Component, шаблона HTML и стилей. Компонент управляет отображением части экрана и взаимодействует с пользователем через свойства и методы.

Пример:

@Component({
selector: 'app-greeting',
template: '<h1>Hello, {{ name }}!</h1>',
styles: ['h1 { color: blue; }']
})
export class GreetingComponent {
name = 'World';
}

Вопрос

Что такое NgModule?

Ответ

NgModule — это декоратор, который помечает класс как Angular-модуль. Модуль организует компоненты, директивы, пайпы и сервисы в связанные функциональные блоки. Каждое Angular-приложение имеет как минимум один корневой модуль — обычно AppModule.

Пример:

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Вопрос

Что такое шаблон в Angular?

Ответ

Шаблон — это HTML-файл или строка, которая определяет внешний вид компонента. В шаблоне используются расширения Angular, такие как интерполяция ({{ }}), привязка свойств ([ ]), обработка событий (( )) и структурные директивы (*ngIf, *ngFor).


Вопрос

Что такое интерполяция в Angular?

Ответ

Интерполяция — это способ отображения значений свойств компонента в шаблоне с помощью двойных фигурных скобок: {{ выражение }}. Angular вычисляет выражение и вставляет результат в DOM как текст.

Пример:

<p>Текущее время: {{ currentTime }}</p>

Вопрос

Что такое привязка свойств (property binding)?

Ответ

Привязка свойств позволяет передавать данные из компонента в элемент DOM или директиву. Синтаксис: [свойство]="выражение". Значение выражения вычисляется в компоненте и присваивается указанному свойству элемента.

Пример:

<img [src]="imageUrl" [alt]="imageAlt">

Вопрос

Что такое привязка событий (event binding)?

Ответ

Привязка событий позволяет реагировать на действия пользователя, такие как клик или ввод текста. Синтаксис: (событие)="обработчик()". Когда событие происходит, вызывается указанный метод компонента.

Пример:

<button (click)="onClick()">Нажми меня</button>

Вопрос

Что такое двухсторонняя привязка (two-way binding)?

Ответ

Двухсторонняя привязка объединяет привязку свойств и привязку событий, обеспечивая синхронизацию между моделью и представлением. Используется с помощью синтаксиса [(ngModel)], который требует импорта FormsModule.

Пример:

<input [(ngModel)]="username">
<p>Привет, {{ username }}!</p>

Вопрос

Что такое директивы в Angular?

Ответ

Директивы — это инструкции в DOM, которые изменяют поведение или внешний вид элементов. Angular предоставляет три типа директив:

  • Компоненты (директивы с шаблоном)
  • Структурные директивы (изменяют структуру DOM, например *ngIf, *ngFor)
  • Атрибутивные директивы (изменяют внешний вид или поведение элемента, например NgStyle, NgClass)

Вопрос

Что делает директива *ngIf?

Ответ

Директива *ngIf условно добавляет или удаляет элемент из DOM в зависимости от значения выражения. Если выражение истинно, элемент отображается; если ложно — удаляется.

Пример:

<div *ngIf="isVisible">Этот блок виден</div>

Вопрос

Что делает директива *ngFor?

Ответ

Директива *ngFor повторяет элемент DOM для каждого элемента в массиве. Она предоставляет доступ к текущему элементу, индексу и другим свойствам через переменные контекста.

Пример:

<li *ngFor="let item of items; index as i">{{ i + 1 }}. {{ item }}</li>

Вопрос

Что такое пайпы (pipes) в Angular?

Ответ

Пайпы — это функции, которые преобразуют данные непосредственно в шаблоне. Они применяются с помощью символа |. Angular предоставляет встроенные пайпы, такие как date, uppercase, json, и поддерживает создание собственных.

Пример:

<p>Сегодня: {{ today | date:'fullDate' }}</p>

Вопрос

Как создать собственный пайп?

Ответ

Собственный пайп создаётся как класс с декоратором @Pipe, реализующий интерфейс PipeTransform и метод transform. Затем пайп регистрируется в модуле.

Пример:

@Pipe({ name: 'exponentialStrength' })
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent = 1): number {
return Math.pow(value, exponent);
}
}

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

<p>{{ 2 | exponentialStrength:10 }}</p>

Вопрос

Что такое сервис в Angular?

Ответ

Сервис — это класс, предназначенный для выполнения определённой задачи, такой как получение данных, логирование или обработка бизнес-логики. Сервисы создаются с помощью механизма Dependency Injection и могут быть внедрены в компоненты или другие сервисы.

Пример:

@Injectable({ providedIn: 'root' })
export class LoggerService {
log(message: string) {
console.log(message);
}
}

Вопрос

Что такое Dependency Injection (DI) в Angular?

Ответ

Dependency Injection — это механизм, позволяющий Angular автоматически предоставлять экземпляры зависимостей (например, сервисов) в конструкторы классов. Это упрощает тестирование и повторное использование кода.


Вопрос

Как внедрить сервис в компонент?

Ответ

Сервис внедряется через параметр конструктора компонента. Angular автоматически создаёт или использует существующий экземпляр сервиса.

Пример:

constructor(private logger: LoggerService) { }

ngOnInit() {
this.logger.log('Компонент инициализирован');
}

Вопрос

Что означает providedIn: 'root' в декораторе @Injectable?

Ответ

providedIn: 'root' указывает Angular зарегистрировать сервис на уровне всего приложения, создав один общий экземпляр (singleton). Это включает механизм tree-shakable providers, при котором сервис включается в сборку только при его фактическом использовании.


Вопрос

Что такое жизненный цикл компонента в Angular?

Ответ

Жизненный цикл компонента — это последовательность событий от создания до уничтожения экземпляра компонента. Angular предоставляет хуки жизненного цикла, такие как ngOnInit, ngOnChanges, ngAfterViewInit, ngOnDestroy, которые позволяют выполнять код на определённых этапах.


Работа с компонентами и шаблонами

Вопрос

Что такое входные свойства (input properties) в Angular?

Ответ

Входные свойства позволяют передавать данные из родительского компонента в дочерний. Они объявляются с помощью декоратора @Input() над свойством класса компонента.

Пример:

@Component({ selector: 'app-child', template: '<p>{{ message }}</p>' })
export class ChildComponent {
@Input() message: string = '';
}

Использование в родителе:

<app-child [message]="'Привет из родителя!'"></app-child>

Вопрос

Что такое выходные свойства (output properties) в Angular?

Ответ

Выходные свойства позволяют дочернему компоненту отправлять события родительскому. Они объявляются с помощью декоратора @Output() и обычно используют экземпляр EventEmitter.

Пример:

@Component({ selector: 'app-child', template: '<button (click)="notify()">Нажми</button>' })
export class ChildComponent {
@Output() notifyParent = new EventEmitter<string>();

notify() {
this.notifyParent.emit('Сообщение от ребёнка');
}
}

Использование в родителе:

<app-child (notifyParent)="onMessage($event)"></app-child>

Вопрос

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

Ответ

Данные передаются через выходное свойство с использованием @Output() и EventEmitter. Родительский компонент подписывается на событие с помощью привязки (событие)="обработчик($event)".


Вопрос

Что такое ViewChild и ViewChildren?

Ответ

@ViewChild предоставляет доступ к одному элементу DOM, директиве или компоненту внутри шаблона текущего компонента. @ViewChildren предоставляет доступ к списку таких элементов. Оба декоратора работают только после завершения инициализации представления (ngAfterViewInit).

Пример:

@ViewChild('myInput') inputElement!: ElementRef;
@ViewChildren(ChildComponent) children!: QueryList<ChildComponent>;

Вопрос

Как получить доступ к элементу DOM в компоненте?

Ответ

Доступ к элементу DOM осуществляется через ElementRef, полученный с помощью @ViewChild. Для безопасности рекомендуется использовать Renderer2 вместо прямого изменения свойств ElementRef.nativeElement.

Пример:

@ViewChild('highlight') highlightEl!: ElementRef;

ngAfterViewInit() {
this.highlightEl.nativeElement.style.backgroundColor = 'yellow';
}

Вопрос

Что такое ContentChild и ContentChildren?

Ответ

@ContentChild и @ContentChildren предоставляют доступ к элементам, проецируемым в компонент через <ng-content>. Они работают с контентом, переданным извне, а не с внутренним шаблоном компонента.

Пример:

@Component({
selector: 'app-wrapper',
template: '<ng-content></ng-content>'
})
export class WrapperComponent {
@ContentChild(HeaderDirective) header?: HeaderDirective;
}

Вопрос

Что такое проекция содержимого (content projection)?

Ответ

Проекция содержимого — это механизм, позволяющий внедрять HTML-контент из родительского компонента внутрь дочернего через тег <ng-content>. Это аналог слотов в других фреймворках.

Пример:

<!-- app-card.component.html -->
<div class="card">
<ng-content></ng-content>
</div>

<!-- Использование -->
<app-card>
<h2>Заголовок извне</h2>
<p>Контент тоже извне</p>
</app-card>

Вопрос

Можно ли использовать несколько <ng-content> в одном компоненте?

Ответ

Да, можно использовать несколько <ng-content> с атрибутом select, чтобы разделять проецируемый контент по CSS-селекторам, например, по классам или директивам.

Пример:

<header><ng-content select=".card-header"></ng-content></header>
<main><ng-content select=".card-body"></ng-content></main>
<footer><ng-content select=".card-footer"></ng-content></footer>

Вопрос

Что такое шаблонные переменные (template reference variables)?

Ответ

Шаблонные переменные — это локальные переменные в шаблоне, обозначаемые через #имя. Они ссылаются на элемент DOM, директиву, компонент или даже веб-API (например, NgForm).

Пример:

<input #myInput>
<button (click)="logValue(myInput.value)">Показать значение</button>

Вопрос

Как использовать шаблонную переменную для доступа к методам компонента?

Ответ

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

Пример:

<app-timer #timer></app-timer>
<button (click)="timer.start()">Старт</button>

Вопрос

Что делает структурная директива *ngIf с элементом DOM?

Ответ

*ngIf полностью добавляет или удаляет элемент из DOM в зависимости от условия. Если условие ложно, элемент не существует в DOM; если истинно — создаётся заново.


Вопрос

Как показать один блок при истинном условии и другой — при ложном?

Ответ

Используется синтаксис *ngIf с блоком else.

Пример:

<div *ngIf="isLoggedIn; else loggedOut">Добро пожаловать!</div>
<ng-template #loggedOut>Пожалуйста, войдите.</ng-template>

Вопрос

Что такое ng-template?

Ответ

<ng-template> — это элемент, который никогда не отображается напрямую. Он служит как шаблон для условного или повторяющегося контента, используемого директивами вроде *ngIf, *ngFor или *ngSwitch.


Вопрос

Как работает директива *ngFor?

Ответ

*ngFor повторяет элемент шаблона для каждого элемента массива. Она предоставляет контекстные переменные: index, first, last, even, odd.

Пример:

<li *ngFor="let user of users; let i = index">
{{ i + 1 }}. {{ user.name }}
</li>

Вопрос

Что такое trackBy в *ngFor и зачем он нужен?

Ответ

trackBy — это функция, которая помогает Angular определять, какие элементы списка изменились, добавились или удалены. Это повышает производительность при обновлении больших списков.

Пример:

trackByUserId(index: number, user: User): number {
return user.id;
}
<li *ngFor="let user of users; trackBy: trackByUserId">{{ user.name }}</li>

Вопрос

Что делает директива *ngSwitch?

Ответ

*ngSwitch позволяет отображать один из нескольких элементов в зависимости от значения выражения. Используется совместно с *ngSwitchCase и *ngSwitchDefault.

Пример:

<div [ngSwitch]="status">
<span *ngSwitchCase="'active'">Активен</span>
<span *ngSwitchCase="'inactive'">Неактивен</span>
<span *ngSwitchDefault>Неизвестный статус</span>
</div>

Вопрос

Как применить CSS-класс динамически в Angular?

Ответ

Для динамического применения классов используется директива ngClass или привязка [class.имя]="условие".

Примеры:

<div [class.active]="isActive">...</div>
<div [ngClass]="{ active: isActive, disabled: isDisabled }">...</div>

Вопрос

Как динамически задать стиль элементу?

Ответ

Стили задаются через привязку [style.свойство]="значение" или директиву ngStyle.

Примеры:

<div [style.color]="textColor">Текст</div>
<div [ngStyle]="{ 'font-size': fontSize + 'px', 'color': textColor }">Текст</div>

Вопрос

Что такое безопасная навигация (safe navigation operator)?

Ответ

Оператор ?. позволяет безопасно обращаться к свойствам объекта, который может быть null или undefined. Он предотвращает ошибки во время выполнения.

Пример:

<p>Имя: {{ user?.profile?.name }}</p>

Вопрос

Что такое неизменяемость (immutability) в контексте Angular?

Ответ

Неизменяемость означает, что объекты не изменяются после создания. В Angular это важно для корректной работы механизма обнаружения изменений: при изменении данных создаётся новый объект, что сигнализирует Angular о необходимости обновления представления.


Вопрос

Как обновить представление при изменении объекта, если используется OnPush?

Ответ

При стратегии ChangeDetectionStrategy.OnPush Angular проверяет изменения только при изменении входных свойств или событиях. Чтобы обновить представление, нужно либо передать новый объект (не мутировать старый), либо вызвать ChangeDetectorRef.markForCheck().


Вопрос

Что такое ChangeDetectorRef?

Ответ

ChangeDetectorRef — это сервис, предоставляющий методы для ручного управления обнаружением изменений в компоненте. Основные методы: markForCheck(), detach(), reattach(), detectChanges().


Вопрос

Как отключить автоматическое обнаружение изменений в компоненте?

Ответ

Устанавливается стратегия ChangeDetectionStrategy.OnPush в декораторе @Component, либо вызывается changeDetectorRef.detach() в конструкторе.


Вопрос

Что происходит при использовании async пайпа?

Ответ

Пайп async автоматически подписывается на Observable или Promise в шаблоне и отписывается при уничтожении компонента. Он также запускает обнаружение изменений при получении нового значения.

Пример:

<p>{{ data$ | async }}</p>

Вопрос

Можно ли использовать несколько структурных директив на одном элементе?

Ответ

Нет, на одном элементе нельзя использовать несколько структурных директив (например, *ngIf и *ngFor). Решение — обернуть элемент в <ng-container> и применить одну директиву на каждый уровень.

Пример:

<ng-container *ngIf="showList">
<li *ngFor="let item of items">{{ item }}</li>
</ng-container>

Сервисы, Dependency Injection и HTTP-клиент

Вопрос

Как зарегистрировать сервис в Angular?

Ответ

Сервис регистрируется либо через декоратор @Injectable({ providedIn: 'root' }), либо добавлением в массив providers модуля или компонента. Регистрация на уровне 'root' создаёт singleton для всего приложения.

Пример:

@Injectable({ providedIn: 'root' })
export class DataService { }

Вопрос

Что такое singleton в контексте Angular?

Ответ

Singleton — это экземпляр сервиса, который создаётся один раз и используется во всём приложении. Angular гарантирует единственный экземпляр сервиса при регистрации через providedIn: 'root'.


Вопрос

Можно ли иметь несколько экземпляров одного сервиса?

Ответ

Да, если сервис зарегистрирован на уровне компонента или модуля, а не как singleton. Каждый компонент или модуль получит свой собственный экземпляр.

Пример:

@Component({
providers: [MyService]
})
export class MyComponent { }

Вопрос

Что такое токен провайдера (provider token)?

Ответ

Токен провайдера — это идентификатор, по которому Angular находит зависимость. Обычно это сам класс сервиса, но может быть строкой или InjectionToken.


Вопрос

Что такое InjectionToken и зачем он нужен?

Ответ

InjectionToken — это специальный объект, используемый для внедрения значений, которые не являются классами (например, строки, числа, конфигурации). Он позволяет избежать коллизий имён и обеспечивает типобезопасность.

Пример:

const API_URL = new InjectionToken<string>('ApiUrl');

@NgModule({
providers: [{ provide: API_URL, useValue: 'https://api.example.com' }]
})
export class AppModule { }

// Использование
constructor(@Inject(API_URL) private apiUrl: string) { }

Вопрос

Как внедрить значение, а не класс, через DI?

Ответ

Используется провайдер с useValue. Это позволяет внедрять примитивы, объекты конфигурации или константы.

Пример:

providers: [
{ provide: 'CONFIG', useValue: { timeout: 5000 } }
]

Вопрос

Что делает провайдер с useClass?

Ответ

Провайдер с useClass указывает Angular использовать другой класс вместо запрошенного. Это полезно для замены реализации (например, моков в тестах).

Пример:

{ provide: LoggerService, useClass: ConsoleLoggerService }

Вопрос

Что такое фабричный провайдер (useFactory)?

Ответ

Фабричный провайдер позволяет создавать экземпляр зависимости с помощью функции. Эта функция может принимать другие зависимости через параметры.

Пример:

export function createDataService(http: HttpClient) {
return new DataService(http, 'https://api.example.com');
}

providers: [
{
provide: DataService,
useFactory: createDataService,
deps: [HttpClient]
}
]

Вопрос

Как работает HttpClient в Angular?

Ответ

HttpClient — это сервис из пакета @angular/common/http, предоставляющий методы для выполнения HTTP-запросов (GET, POST, PUT, DELETE и др.). Он возвращает Observable, который нужно подписывать или использовать с async пайпом.


Вопрос

Как отправить GET-запрос с помощью HttpClient?

Ответ

Используется метод get(). Он принимает URL и опционально объект с параметрами запроса.

Пример:

this.http.get<User[]>('/api/users').subscribe(users => {
this.users = users;
});

Вопрос

Как передать параметры запроса в HttpClient?

Ответ

Параметры передаются через объект params в опциях запроса. Значения автоматически кодируются.

Пример:

this.http.get('/api/search', {
params: { q: 'angular', page: '1' }
}).subscribe(...);

Вопрос

Как добавить заголовки к HTTP-запросу?

Ответ

Заголовки добавляются через объект headers в опциях запроса.

Пример:

this.http.post('/api/data', body, {
headers: { 'Authorization': 'Bearer token123' }
}).subscribe(...);

Вопрос

Что такое interceptor в Angular?

Ответ

Interceptor — это класс, реализующий интерфейс HttpInterceptor, который перехватывает все HTTP-запросы и/или ответы. Он используется для логирования, добавления токенов, обработки ошибок и кэширования.


Вопрос

Как создать HTTP interceptor?

Ответ

Создаётся класс с методом intercept(), помеченный декоратором @Injectable(), и регистрируется в провайдерах модуля через HTTP_INTERCEPTORS.

Пример:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authReq = req.clone({
setHeaders: { Authorization: 'Bearer xyz' }
});
return next.handle(authReq);
}
}

// В модуле
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
]

Вопрос

Что означает multi: true при регистрации interceptor?

Ответ

multi: true указывает Angular, что провайдер может иметь несколько значений. Все interceptors будут применены цепочкой.


Вопрос

Как обрабатывать ошибки HTTP-запросов?

Ответ

Ошибки обрабатываются с помощью оператора catchError из RxJS внутри цепочки Observable.

Пример:

import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

this.http.get('/api/data').pipe(
catchError(error => {
console.error('Ошибка:', error);
return throwError(() => new Error('Что-то пошло не так'));
})
).subscribe(...);

Вопрос

Что такое HttpErrorResponse?

Ответ

HttpErrorResponse — это класс, представляющий ошибку HTTP-запроса. Он содержит информацию о статусе, сообщении, URL и теле ошибки (если есть).


Вопрос

Как отменить HTTP-запрос в Angular?

Ответ

HTTP-запрос отменяется вызовом unsubscribe() на подписке или с помощью takeUntil / switchMap для автоматической отмены при изменении состояния.

Пример:

private destroy$ = new Subject<void>();

ngOnInit() {
this.http.get('/api/data').pipe(
takeUntil(this.destroy$)
).subscribe(...);
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}

Вопрос

Что такое switchMap и зачем он используется с HTTP?

Ответ

switchMap — это оператор RxJS, который отменяет предыдущий Observable при поступлении нового значения. Он часто используется для отмены старых запросов при частых изменениях (например, поиск при вводе текста).

Пример:

searchInput.valueChanges.pipe(
switchMap(term => this.http.get(`/api/search?q=${term}`))
).subscribe(results => ...);

Вопрос

Как сериализовать данные в JSON при отправке POST-запроса?

Ответ

HttpClient автоматически сериализует объекты JavaScript в JSON. Нет необходимости вручную вызывать JSON.stringify().

Пример:

this.http.post('/api/users', { name: 'Alice', age: 30 }).subscribe(...);
// Тело запроса будет: {"name":"Alice","age":30}

Вопрос

Как получить необработанный ответ (raw response) от сервера?

Ответ

Указывается опция observe: 'response', чтобы получить полный объект HttpResponse.

Пример:

this.http.get('/api/data', { observe: 'response' }).subscribe(resp => {
console.log(resp.status, resp.headers, resp.body);
});

Вопрос

Как обработать прогресс загрузки файла?

Ответ

Используется опция reportProgress: true и observe: 'events'. События типа HttpEventType.UploadProgress содержат информацию о прогрессе.

Пример:

this.http.post('/upload', file, {
reportProgress: true,
observe: 'events'
}).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
const percent = Math.round(100 * event.loaded / (event.total || 1));
console.log(`Загружено: ${percent}%`);
}
});

Вопрос

Можно ли использовать async/await с HttpClient?

Ответ

Да, но нужно преобразовать Observable в Promise с помощью метода toPromise() (устаревший) или firstValueFrom() / lastValueFrom() из rxjs.

Пример:

import { firstValueFrom } from 'rxjs';

async loadData() {
try {
const data = await firstValueFrom(this.http.get('/api/data'));
console.log(data);
} catch (error) {
console.error(error);
}
}

Вопрос

Что такое withCredentials в HTTP-запросе?

Ответ

withCredentials: true позволяет отправлять куки и заголовки аутентификации в кросс-доменных запросах. Это необходимо при работе с CORS и сессиями.

Пример:

this.http.get('/api/profile', { withCredentials: true }).subscribe(...);

Вопрос

Как протестировать сервис, использующий HttpClient?

Ответ

Используется HttpClientTestingModule и HttpTestingController для мокирования HTTP-запросов в unit-тестах.

Пример:

TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [MyService]
});

const httpMock = TestBed.inject(HttpTestingController);
service.getData().subscribe(...);

const req = httpMock.expectOne('/api/data');
req.flush({ id: 1, name: 'Test' });
httpMock.verify();

Вопрос

Что происходит, если не подписаться на Observable из HttpClient?

Ответ

Запрос не выполняется. HttpClient использует "холодные" Observables, которые активируются только при подписке.


Роутинг и навигация

Вопрос

Что такое RouterModule в Angular?

Ответ

RouterModule — это модуль Angular, предоставляющий функциональность маршрутизации. Он позволяет определять пути, связывать их с компонентами и управлять навигацией в одностраничном приложении.


Вопрос

Как настроить базовый роутинг в приложении?

Ответ

Создаётся массив маршрутов типа Routes, импортируется RouterModule.forRoot(routes) в корневом модуле, и добавляется <router-outlet> в шаблон.

Пример:

const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

Вопрос

Что делает <router-outlet>?

Ответ

<router-outlet> — это директива, которая служит заполнителем для отображения компонента, соответствующего текущему маршруту. Angular динамически подставляет нужный компонент в это место DOM.


Вопрос

Как выполнить программную навигацию?

Ответ

Программная навигация выполняется через сервис Router с использованием метода navigate() или navigateByUrl().

Пример:

constructor(private router: Router) { }

goToProfile() {
this.router.navigate(['/user', 123]);
}

Вопрос

Как передать параметры в маршрут?

Ответ

Параметры передаются как часть пути (параметры маршрута) или как строка запроса (query parameters).

Пример:

this.router.navigate(['/product', productId]); // /product/5
this.router.navigate(['/search'], { queryParams: { q: 'book' } }); // /search?q=book

Вопрос

Как получить параметры маршрута в компоненте?

Ответ

Параметры маршрута извлекаются через сервис ActivatedRoute. Для статичных параметров используется route.snapshot.paramMap; для реактивного доступа — route.paramMap.

Пример:

constructor(private route: ActivatedRoute) { }

ngOnInit() {
const id = this.route.snapshot.paramMap.get('id');
// или
this.route.paramMap.subscribe(params => {
const id = params.get('id');
});
}

Вопрос

Как получить query-параметры в компоненте?

Ответ

Query-параметры извлекаются через ActivatedRoute.queryParamMap аналогично параметрам маршрута.

Пример:

this.route.queryParamMap.subscribe(params => {
const q = params.get('q');
});

Вопрос

Что такое дочерние маршруты (child routes)?

Ответ

Дочерние маршруты — это маршруты, вложенные в другой маршрут. Они позволяют строить иерархические структуры, например, панель администратора с подразделами.

Пример:

{
path: 'admin',
component: AdminComponent,
children: [
{ path: 'users', component: UserListComponent },
{ path: 'settings', component: SettingsComponent }
]
}

Вопрос

Где должен находиться <router-outlet> для дочерних маршрутов?

Ответ

<router-outlet> должен находиться в шаблоне родительского компонента (например, AdminComponent), чтобы отображать дочерние компоненты.


Вопрос

Что такое маршрут по умолчанию (wildcard route)?

Ответ

Маршрут по умолчанию — это маршрут с путём **, который совпадает с любым неопределённым URL. Обычно используется для отображения страницы 404.

Пример:

{ path: '**', component: PageNotFoundComponent }

Вопрос

Как перенаправить один маршрут на другой?

Ответ

Используется свойство redirectTo в конфигурации маршрута.

Пример:

{ path: '', redirectTo: '/home', pathMatch: 'full' }

Вопрос

Что означает pathMatch: 'full'?

Ответ

pathMatch: 'full' указывает Angular сравнивать весь URL целиком. Без этого параметра при пустом пути может произойти частичное совпадение, приводящее к бесконечному редиректу.


Вопрос

Что такое ленивая загрузка модулей (lazy loading)?

Ответ

Ленивая загрузка — это техника, при которой модуль загружается только при переходе на соответствующий маршрут. Это уменьшает размер начальной сборки и ускоряет запуск приложения.


Вопрос

Как настроить ленивую загрузку модуля?

Ответ

Указывается строка с loadChildren, возвращающая динамический импорт модуля.

Пример:

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

Вопрос

Можно ли использовать ленивую загрузку с standalone-компонентами?

Ответ

Да, начиная с Angular 14+, можно загружать standalone-компоненты напрямую без модуля.

Пример:

{
path: 'profile',
loadComponent: () => import('./profile/profile.component').then(c => c.ProfileComponent)
}

Вопрос

Что такое guards в Angular?

Ответ

Guards — это классы, которые контролируют доступ к маршрутам. Они реализуют интерфейсы: CanActivate, CanDeactivate, CanLoad, Resolve, CanMatch.


Вопрос

Как запретить доступ к маршруту?

Ответ

Используется guard с интерфейсом CanActivate, который возвращает false или Observable<boolean>.

Пример:

@Injectable()
export class AuthGuard implements CanActivate {
canActivate(): boolean {
return isLoggedIn();
}
}

// В маршруте
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }

Вопрос

Как предотвратить уход с маршрута?

Ответ

Используется guard с интерфейсом CanDeactivate, который проверяет, можно ли покинуть компонент (например, при наличии несохранённых изменений).

Пример:

export interface CanComponentDeactivate {
canDeactivate: () => boolean | Observable<boolean>;
}

@Injectable()
export class UnsavedChangesGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate) {
return component.canDeactivate ? component.canDeactivate() : true;
}
}

Вопрос

Что делает Resolve guard?

Ответ

Resolve guard загружает данные до активации маршрута. Это гарантирует, что компонент получит данные сразу при инициализации.

Пример:

@Injectable()
export class DataResolver implements Resolve<User> {
resolve(route: ActivatedRouteSnapshot): Observable<User> {
return this.userService.get(route.paramMap.get('id')!);
}
}

// В маршруте
{ path: 'user/:id', component: UserComponent, resolve: { user: DataResolver } }

Вопрос

Как получить данные из Resolve в компоненте?

Ответ

Данные из Resolve доступны через ActivatedRoute.data.

Пример:

ngOnInit() {
this.route.data.subscribe(data => {
this.user = data['user'];
});
}

Вопрос

Что такое CanMatch guard?

Ответ

CanMatch guard определяет, должен ли маршрут быть сопоставлен вообще. Он работает на этапе сопоставления маршрутов, до CanActivate.


Вопрос

Как передать статические данные в маршрут?

Ответ

Статические данные передаются через свойство data в конфигурации маршрута.

Пример:

{ path: 'about', component: AboutComponent, data: { title: 'О нас' } }

Вопрос

Как получить статические данные маршрута в компоненте?

Ответ

Статические данные извлекаются через ActivatedRoute.snapshot.data или ActivatedRoute.data.

Пример:

const title = this.route.snapshot.data['title'];

Вопрос

Что такое относительная навигация?

Ответ

Относительная навигация выполняется относительно текущего маршрута с помощью relativeTo в опциях navigate().

Пример:

this.router.navigate(['../sibling'], { relativeTo: this.route });

Вопрос

Как сохранить query-параметры при навигации?

Ответ

Используется опция queryParamsHandling: 'preserve' или 'merge'.

Пример:

this.router.navigate(['/new-route'], { queryParamsHandling: 'preserve' });

Формы в Angular

Вопрос

Какие два подхода к работе с формами поддерживает Angular?

Ответ

Angular поддерживает два подхода:

  • Шаблонные формы (Template-driven forms)
  • Реактивные формы (Reactive forms)

Вопрос

Что такое шаблонные формы?

Ответ

Шаблонные формы строятся с использованием директив в шаблоне, таких как ngModel, ngForm и встроенные валидаторы. Логика формы находится преимущественно в HTML.

Пример:

<form #myForm="ngForm">
<input name="email" ngModel required email>
</form>

Вопрос

Что такое реактивные формы?

Ответ

Реактивные формы создаются программно в классе компонента с использованием классов FormControl, FormGroup, FormArray. Они обеспечивают явный, неизменяемый и тестируемый способ управления состоянием формы.

Пример:

profileForm = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email])
});

Вопрос

Какой модуль нужно импортировать для реактивных форм?

Ответ

Для реактивных форм необходимо импортировать ReactiveFormsModule в модуль компонента.


Вопрос

Как привязать реактивную форму к шаблону?

Ответ

Используется директива [formGroup] на элементе <form> и [formControlName] на элементах управления.

Пример:

<form [formGroup]="profileForm">
<input formControlName="name">
<input formControlName="email">
</form>

Вопрос

Как получить значение формы?

Ответ

Значение формы доступно через свойство value экземпляра FormGroup.

Пример:

const values = this.profileForm.value; // { name: '...', email: '...' }

Вопрос

Как проверить валидность всей формы?

Ответ

Свойство valid объекта FormGroup возвращает true, если все контролы валидны.

Пример:

if (this.profileForm.valid) {
// отправить данные
}

Вопрос

Как отслеживать изменения в форме?

Ответ

Изменения отслеживаются через Observable valueChanges или statusChanges.

Пример:

this.profileForm.valueChanges.subscribe(value => {
console.log('Изменено:', value);
});

Вопрос

Что такое FormControl?

Ответ

FormControl представляет отдельное поле формы. Он хранит значение, статус валидации, ошибки и взаимодействует с DOM через привязку.


Вопрос

Что такое FormGroup?

Ответ

FormGroup объединяет несколько FormControl или других FormGroup в одну логическую группу. Он предоставляет общий интерфейс для работы с подмножеством формы.


Вопрос

Что такое FormArray?

Ответ

FormArray — это динамический массив контролов или групп, который позволяет добавлять и удалять элементы формы во время выполнения.

Пример:

addresses = new FormArray([
new FormControl('ул. Ленина, 1'),
new FormControl('пр. Мира, 5')
]);

Вопрос

Как добавить элемент в FormArray?

Ответ

Используется метод push().

Пример:

addAddress() {
this.addresses.push(new FormControl(''));
}

Вопрос

Как удалить элемент из FormArray?

Ответ

Используется метод removeAt(index).

Пример:

removeAddress(index: number) {
this.addresses.removeAt(index);
}

Вопрос

Какие встроенные валидаторы предоставляет Angular?

Ответ

Angular предоставляет встроенные валидаторы:

  • Validators.required
  • Validators.minLength / maxLength
  • Validators.pattern
  • Validators.email
  • Validators.nullValidator

Вопрос

Как применить несколько валидаторов к контролу?

Ответ

Валидаторы передаются в виде массива в конструктор FormControl.

Пример:

new FormControl('', [
Validators.required,
Validators.minLength(3),
Validators.pattern(/^[a-zA-Z]+$/)
]);

Вопрос

Как создать кастомный синхронный валидатор?

Ответ

Кастомный валидатор — это функция, принимающая AbstractControl и возвращающая объект ошибок или null.

Пример:

function forbiddenNameValidator(name: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
return control.value === name ? { forbiddenName: { value: control.value } } : null;
};
}

// Использование
new FormControl('', forbiddenNameValidator('admin'));

Вопрос

Как создать асинхронный валидатор?

Ответ

Асинхронный валидатор возвращает Promise или Observable, который разрешается в объект ошибок или null.

Пример:

uniqueEmailValidator(service: UserService): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
return service.checkEmail(control.value).pipe(
map(isTaken => isTaken ? { emailTaken: true } : null)
);
};
}

Вопрос

Как использовать асинхронный валидатор в FormControl?

Ответ

Асинхронный валидатор передаётся третьим аргументом в конструктор FormControl.

Пример:

new FormControl('', null, this.uniqueEmailValidator(userService));

Вопрос

Что делает метод markAsTouched()?

Ответ

Метод markAsTouched() помечает контроль как «тронутый» (touched), что влияет на отображение ошибок в шаблоне (обычно ошибки показываются после первого взаимодействия).


Вопрос

Как сбросить форму?

Ответ

Используется метод reset(), который устанавливает значения по умолчанию, сбрасывает статусы и помечает контролы как нетронутые.

Пример:

this.profileForm.reset();
// или с начальными значениями
this.profileForm.reset({ name: '', email: '' });

Вопрос

Как установить значение формы программно?

Ответ

Используются методы setValue() (для полного совпадения структуры) или patchValue() (для частичного обновления).

Пример:

this.profileForm.setValue({ name: 'Alice', email: 'alice@example.com' });
this.profileForm.patchValue({ email: 'new@example.com' });

Вопрос

В чём разница между setValue и patchValue?

Ответ

setValue требует передачи значений для всех контролов группы. patchValue обновляет только указанные поля, игнорируя остальные.


Вопрос

Как отключить форму или отдельный контрол?

Ответ

Используется метод disable(). Отключённые контролы исключаются из значения формы и не участвуют в валидации.

Пример:

this.profileForm.disable();
this.profileForm.get('email')?.disable();

Вопрос

Как включить отключённый контрол?

Ответ

Используется метод enable().

Пример:

this.profileForm.get('email')?.enable();

Вопрос

Как привязать форму к кнопке отправки?

Ответ

Кнопка отправки помещается внутрь <form>, и используется обработчик (ngSubmit) на форме.

Пример:

<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<button type="submit" [disabled]="!profileForm.valid">Отправить</button>
</form>

RxJS и реактивное программирование в Angular

Вопрос

Что такое Observable в RxJS?

Ответ

Observable — это поток данных, который может выдавать ноль или более значений со временем. Он является основным типом для асинхронных операций в Angular, включая HTTP-запросы и события.


Вопрос

Что такое Observer?

Ответ

Observer — это объект, который получает уведомления от Observable. Он содержит методы next, error и complete.

Пример:

const observer = {
next: value => console.log(value),
error: err => console.error(err),
complete: () => console.log('Завершено')
};

Вопрос

Как подписаться на Observable?

Ответ

Подписка выполняется с помощью метода subscribe(), которому передаётся Observer или функции обратного вызова.

Пример:

observable.subscribe({
next: value => console.log(value),
error: err => console.error(err),
complete: () => console.log('Готово')
});

Вопрос

Что такое оператор в RxJS?

Ответ

Оператор — это функция, которая преобразует один Observable в другой. Операторы применяются через метод pipe().

Пример:

import { map, filter } from 'rxjs/operators';

source$.pipe(
filter(x => x > 0),
map(x => x * 2)
).subscribe(...);

Вопрос

Какие распространённые операторы используются в Angular?

Ответ

Распространённые операторы:

  • map — преобразует каждое значение
  • filter — пропускает только удовлетворяющие условию значения
  • switchMap — переключается на новый Observable и отменяет предыдущий
  • mergeMap — параллельно обрабатывает все входящие значения
  • catchError — обрабатывает ошибки
  • take, takeUntil — ограничивают поток
  • debounceTime, distinctUntilChanged — используются для поиска и ввода

Вопрос

Что делает оператор switchMap?

Ответ

switchMap преобразует каждое значение в новый Observable и автоматически отменяет предыдущий внутренний Observable при поступлении нового значения. Это полезно для отмены старых HTTP-запросов.


Вопрос

Что делает оператор mergeMap?

Ответ

mergeMap (он же flatMap) преобразует каждое значение в Observable и объединяет все результирующие потоки в один, сохраняя порядок завершения, но не обязательно порядок значений.


Вопрос

Что делает оператор concatMap?

Ответ

concatMap преобразует каждое значение в Observable и обрабатывает их последовательно: следующий запускается только после завершения предыдущего.


Вопрос

Когда использовать switchMap, mergeMap или concatMap?

Ответ

  • switchMap — когда нужна отмена предыдущей операции (например, поиск при вводе)
  • mergeMap — когда все запросы должны быть выполнены независимо (например, отправка нескольких логов)
  • concatMap — когда важен строгий порядок выполнения (например, последовательная обработка очереди)

Вопрос

Что такое Subject?

Ответ

Subject — это особый тип Observable, который позволяет многократно передавать значения множеству подписчиков. Он одновременно является Observable и Observer.


Вопрос

Какие виды Subject существуют?

Ответ

Существуют четыре основных вида:

  • Subject — базовый мультикаст
  • BehaviorSubject — хранит последнее значение и выдаёт его новым подписчикам
  • ReplaySubject — хранит последние N значений и воспроизводит их новым подписчикам
  • AsyncSubject — выдаёт только последнее значение после завершения потока

Вопрос

Что такое BehaviorSubject?

Ответ

BehaviorSubject — это Subject, который требует начальное значение и выдаёт текущее значение каждому новому подписчику немедленно при подписке.

Пример:

const state$ = new BehaviorSubject<string>('initial');
state$.subscribe(value => console.log(value)); // сразу выведет 'initial'
state$.next('updated');

Вопрос

Как избежать утечек памяти при подписке на Observable?

Ответ

Утечки памяти предотвращаются отменой подписок. Способы:

  • Явный вызов unsubscribe()
  • Использование take(1) для однократных значений
  • Использование takeUntil с Subject в ngOnDestroy
  • Использование async пайпа в шаблоне

Вопрос

Почему async пайп безопасен с точки зрения утечек?

Ответ

async пайп автоматически подписывается на Observable при инициализации компонента и отписывается при его уничтожении, предотвращая утечки памяти.


Вопрос

Что такое холодный и горячий Observable?

Ответ

Холодный Observable создаёт независимое выполнение для каждого подписчика (например, HttpClient.get). Горячий Observable делится одним выполнением между всеми подписчиками (например, Subject).


Вопрос

Является ли Observable из HttpClient холодным или горячим?

Ответ

Observable из HttpClient является холодным: каждый вызов subscribe() инициирует новый HTTP-запрос.


Вопрос

Как сделать холодный Observable горячим?

Ответ

Используется оператор share() или shareReplay(), который мультикастит значения всем подписчикам.

Пример:

const shared$ = this.http.get('/api/data').pipe(shareReplay(1));

Вопрос

Что делает shareReplay(1)?

Ответ

shareReplay(1) делает Observable горячим и кэширует последнее значение, чтобы выдать его всем будущим подписчикам немедленно.


Вопрос

Как объединить несколько Observable в один?

Ответ

Используются операторы:

  • combineLatest — генерирует значение при изменении любого источника
  • forkJoin — ждёт завершения всех Observable и возвращает массив последних значений
  • zip — объединяет значения по индексу
  • merge — объединяет все значения в один поток

Вопрос

Что возвращает forkJoin?

Ответ

forkJoin возвращает Observable, который выдаёт массив последних значений всех входных Observable после их завершения.

Пример:

forkJoin([this.http.get('/a'), this.http.get('/b')]).subscribe(results => {
const [a, b] = results;
});

Вопрос

Когда использовать combineLatest?

Ответ

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


Вопрос

Что такое of и from в RxJS?

Ответ

of создаёт Observable из конечного набора значений. from преобразует массив, Promise или итерируемый объект в Observable.

Примеры:

of(1, 2, 3).subscribe(...); // выдаст 1, 2, 3
from([1, 2, 3]).subscribe(...); // то же самое
from(fetch('/api')).subscribe(...); // из Promise

Вопрос

Как создать Observable, который выдаёт значение с задержкой?

Ответ

Используется оператор delay или функция timer.

Пример:

of('hello').pipe(delay(1000)).subscribe(...);
// или
timer(1000).subscribe(() => console.log('Прошла секунда'));

Вопрос

Что такое tap и зачем он нужен?

Ответ

tap — это оператор для побочных эффектов (логирование, отладка, вызов методов), не изменяющий поток данных.

Пример:

this.http.get('/data').pipe(
tap(data => console.log('Получены данные:', data))
).subscribe(...);

Вопрос

Как обработать ошибку и продолжить поток?

Ответ

Используется catchError, возвращающий новый Observable вместо ошибки.

Пример:

source$.pipe(
catchError(error => of(defaultValue))
).subscribe(...);

Производительность, оптимизация и продвинутые темы

Вопрос

Что такое Change Detection в Angular?

Ответ

Change Detection — это механизм Angular, который проверяет изменения в данных компонентов и обновляет DOM соответственно. Он запускается после асинхронных событий, таких как события DOM, таймеры или HTTP-ответы.


Вопрос

Какие стратегии обнаружения изменений поддерживает Angular?

Ответ

Angular поддерживает две стратегии:

  • Default — проверяет все компоненты при каждом событии
  • OnPush — проверяет компонент только при изменении входных свойств или событиях внутри компонента

Вопрос

Как включить стратегию OnPush?

Ответ

Указывается свойство changeDetection в декораторе @Component.

Пример:

@Component({
selector: 'app-heavy-list',
templateUrl: './list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListComponent { }

Вопрос

Как работает стратегия OnPush?

Ответ

Компонент с OnPush проверяется только если:

  • Изменилось значение любого @Input() (по ссылке)
  • Произошло событие внутри шаблона компонента (клик, ввод и т.п.)
  • Вызван ChangeDetectorRef.markForCheck()

Вопрос

Что делает метод markForCheck()?

Ответ

markForCheck() помечает компонент и его предков для проверки в следующем цикле обнаружения изменений, даже если используется OnPush.


Вопрос

Как избежать частых проверок в списке с тысячами элементов?

Ответ

Используется стратегия OnPush для элементов списка, неизменяемые объекты данных и trackBy в *ngFor.


Вопрос

Что такое trackBy и как он повышает производительность?

Ответ

trackBy помогает Angular идентифицировать, какие элементы списка остались прежними при обновлении. Это предотвращает ненужное создание и уничтожение DOM-элементов.


Вопрос

Что такое ленивая загрузка и как она влияет на производительность?

Ответ

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


Вопрос

Что такое Ahead-of-Time (AOT) компиляция?

Ответ

AOT — это компиляция шаблонов и компонентов в JavaScript во время сборки, а не в браузере. Это ускоряет запуск приложения, уменьшает размер и повышает безопасность.


Вопрос

Как включить AOT в Angular?

Ответ

AOT включена по умолчанию при сборке через ng build --prod (или просто ng build в современных версиях CLI).


Вопрос

Что такое tree-shaking и как Angular его поддерживает?

Ответ

Tree-shaking — это удаление неиспользуемого кода из финальной сборки. Angular поддерживает его через использование providedIn: 'root' и модульную архитектуру.


Вопрос

Как уменьшить размер бандла Angular-приложения?

Ответ

Способы уменьшения размера:

  • Использование ленивой загрузки
  • Минимизация сторонних библиотек
  • Аудит зависимостей через source-map-explorer
  • Использование OnPush и чистых компонентов
  • Отказ от полифиллов, если не нужны

Вопрос

Что такое standalone-компоненты?

Ответ

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

Пример:

@Component({
standalone: true,
imports: [CommonModule, RouterLink],
template: `<a routerLink="/">Главная</a>`
})
export class NavComponent { }

Вопрос

Какие преимущества дают standalone-компоненты?

Ответ

Standalone-компоненты упрощают архитектуру, устраняют необходимость в большом количестве модулей и улучшают ленивую загрузку.


Вопрос

Что такое сигналы (Signals) в Angular?

Ответ

Сигналы — это реактивная примитивная система, представленная в Angular 16+, которая обеспечивает более эффективное и простое управление состоянием по сравнению с RxJS в некоторых сценариях.


Вопрос

Как создать сигнал?

Ответ

Используется функция signal() для записи и computed() для вычисляемых значений.

Пример:

count = signal(0);
doubleCount = computed(() => this.count() * 2);

increment() {
this.count.update(v => v + 1);
}

Вопрос

Можно ли использовать сигналы в шаблоне?

Ответ

Да, сигналы вызываются как функции в шаблоне.

Пример:

<p>Счёт: {{ count() }}</p>
<p>Удвоенный: {{ doubleCount() }}</p>

Вопрос

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

Ответ

Используется функция effect(), которая автоматически отслеживает зависимости и выполняется при их изменении.

Пример:

effect(() => {
console.log('Текущий счёт:', this.count());
});

Вопрос

Что такое Zone.js и зачем он нужен в Angular?

Ответ

Zone.js — это библиотека, которая перехватывает асинхронные операции (таймеры, события, промисы) и уведомляет Angular о необходимости запуска обнаружения изменений.


Вопрос

Можно ли отключить Zone.js?

Ответ

Да, можно отключить Zone.js и управлять обнаружением изменений вручную, но это требует явного вызова ApplicationRef.tick() или ChangeDetectorRef.detectChanges().


Вопрос

Что такое NgZone?

Ответ

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

Пример:

constructor(private ngZone: NgZone) { }

runOutside() {
this.ngZone.runOutsideAngular(() => {
// тяжёлые вычисления без CD
});
}

Вопрос

Как оптимизировать тяжёлые вычисления в компоненте?

Ответ

Тяжёлые вычисления выносятся за пределы зоны Angular с помощью NgZone.runOutsideAngular(), а результат передаётся обратно через NgZone.run().


Вопрос

Что такое pure пайп?

Ответ

pure пайп (по умолчанию) выполняется только при изменении входного значения (по ссылке). Это повышает производительность.


Вопрос

Когда использовать impure пайп?

Ответ

impure пайп выполняется при каждом цикле обнаружения изменений. Используется редко, например, для фильтрации массива в реальном времени, но может снижать производительность.


Вопрос

Как измерить производительность компонента?

Ответ

Используются инструменты:

  • Вкладка Performance в DevTools браузера
  • console.time() / console.timeEnd()
  • Angular DevTools для анализа компонентов и изменений

Вопрос

Что такое defer блоки в Angular?

Ответ

defer — это экспериментальная функция (начиная с Angular 17), позволяющая откладывать загрузку и рендеринг частей шаблона до наступления определённого условия (например, видимости, взаимодействия, таймаута).

Пример:

<defer on viewport>
<app-heavy-component />
</defer>

Вопрос

Какие триггеры поддерживает defer?

Ответ

Поддерживаемые триггеры:

  • on idle — после завершения основной задачи
  • on timer(x) — через x миллисекунд
  • on viewport — когда элемент попадает в область просмотра
  • on interaction — при клике или фокусе
  • on hover — при наведении курсора
  • on immediate — немедленно

Тестирование, инструменты и экосистема Angular

Вопрос

Какие виды тестов поддерживает Angular?

Ответ

Angular поддерживает три основных вида тестов:

  • Модульные (unit) тесты
  • Интеграционные тесты компонентов
  • Сквозные (end-to-end, e2e) тесты

Вопрос

Какой фреймворк используется для модульного тестирования в Angular?

Ответ

Для модульного тестирования используется Jasmine в связке с Karma как тест-раннером.


Вопрос

Что такое TestBed?

Ответ

TestBed — это утилита Angular для настройки окружения модульных тестов компонентов и сервисов. Она имитирует работу NgModule и позволяет внедрять зависимости.

Пример:

TestBed.configureTestingModule({
declarations: [MyComponent],
providers: [MyService]
});

Вопрос

Как протестировать компонент с зависимостями?

Ответ

Зависимости заменяются моками через providers в TestBed.configureTestingModule.

Пример:

const mockService = { getData: () => of('test') };
providers: [{ provide: DataService, useValue: mockService }]

Вопрос

Как проверить, что метод сервиса был вызван?

Ответ

Используется шпион (spy) через spyOn() из Jasmine.

Пример:

const spy = spyOn(service, 'getData').and.returnValue(of('mock'));
component.ngOnInit();
expect(spy).toHaveBeenCalled();

Вопрос

Как протестировать шаблон компонента?

Ответ

Компонент создаётся через TestBed.createComponent(), затем вызывается fixture.detectChanges() для запуска обнаружения изменений, после чего проверяется DOM через fixture.nativeElement.

Пример:

const fixture = TestBed.createComponent(MyComponent);
fixture.detectChanges();
const el = fixture.nativeElement;
expect(el.querySelector('h1').textContent).toContain('Привет');

Вопрос

Что такое ComponentFixture?

Ответ

ComponentFixture — это обёртка над экземпляром компонента в тестах. Она предоставляет доступ к компоненту, DOM, методам обнаружения изменений и событиям жизненного цикла.


Вопрос

Как протестировать асинхронный код в компоненте?

Ответ

Используются утилиты:

  • fakeAsync + tick() — для синхронного моделирования времени
  • async (устаревший) или waitForAsync — для настоящих асинхронных операций

Пример:

it('should fetch data', fakeAsync(() => {
component.loadData();
tick(); // прогоняет таймеры
expect(component.data).toBe('loaded');
}));

Вопрос

Как тестировать HTTP-запросы?

Ответ

Используется HttpClientTestingModule и HttpTestingController для перехвата и проверки запросов.

Пример:

const req = httpMock.expectOne('/api/data');
expect(req.request.method).toEqual('GET');
req.flush({ id: 1 });

Вопрос

Какие инструменты используются для e2e-тестов в Angular?

Ответ

Для сквозного тестирования используется Cypress (по умолчанию в новых проектах) или ранее Protractor.


Вопрос

Что такое Angular CLI?

Ответ

Angular CLI — это официальный инструмент командной строки для создания, сборки, тестирования и развёртывания Angular-приложений. Он автоматизирует типичные задачи разработки.


Вопрос

Как создать новый компонент через CLI?

Ответ

Используется команда:

ng generate component my-component

или сокращённо:

ng g c my-component

Вопрос

Как обновить Angular до новой версии?

Ответ

Используется команда:

ng update @angular/core @angular/cli

Вопрос

Что такое Angular Material?

Ответ

Angular Material — это библиотека компонентов, реализующая принципы Material Design. Она предоставляет готовые UI-элементы: кнопки, таблицы, диалоги, формы и т.д.


Вопрос

Как добавить Angular Material в проект?

Ответ

Выполняется команда:

ng add @angular/material

Вопрос

Что такое schematics в Angular?

Ответ

Schematics — это система генерации и модификации кода. CLI использует её для создания компонентов, модулей и других артефактов. Пользователи могут создавать собственные schematics.


Вопрос

Как работает ng build?

Ответ

ng build компилирует приложение в статические файлы (JavaScript, CSS, HTML), оптимизирует их и помещает в папку dist/. По умолчанию включает AOT, минификацию и tree-shaking.


Вопрос

Как запустить приложение в режиме разработки?

Ответ

Используется команда:

ng serve

Она запускает сервер разработки с горячей перезагрузкой.


Вопрос

Что такое environment files в Angular?

Ответ

Файлы окружения (environment.ts, environment.prod.ts) содержат конфигурации, специфичные для режима сборки (например, URL API). Angular автоматически подставляет нужный файл при сборке.


Вопрос

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

Ответ

Импортируется объект environment из @environments/environment.

Пример:

import { environment } from '../environments/environment';

const apiUrl = environment.apiUrl;

Вопрос

Что такое custom webpack в Angular?

Ответ

Custom webpack позволяет расширить или изменить конфигурацию сборки Angular без отказа от CLI. Используется через @angular-builders/custom-webpack.


Вопрос

Как добавить глобальные стили в Angular?

Ответ

Глобальные стили указываются в свойстве styles файла angular.json или импортируются в styles.css.


Вопрос

Как подключить стороннюю библиотеку CSS (например, Bootstrap)?

Ответ

Библиотека устанавливается через npm, затем путь к CSS-файлу добавляется в массив styles в angular.json.

Пример:

"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.css"
]

Вопрос

Что такое Angular Elements?

Ответ

Angular Elements — это пакет, позволяющий превращать Angular-компоненты в нативные веб-компоненты (Custom Elements), которые можно использовать вне Angular-приложений.


Вопрос

Как создать веб-компонент из Angular-компонента?

Ответ

Используется функция createCustomElement() из @angular/elements, затем компонент регистрируется через customElements.define().

Пример:

const el = createCustomElement(MyComponent, { injector });
customElements.define('my-element', el);

Вопрос

Что такое Ivy?

Ответ

Ivy — это новый рендеринговый движок Angular, представленный в версии 9. Он уменьшает размер бандла, ускоряет компиляцию и улучшает отладку.


Вопрос

Как проверить, используется ли Ivy?

Ответ

Ivy включён по умолчанию во всех проектах начиная с Angular 9. Наличие подтверждается отсутствием enableIvy: false в angularCompilerOptions.


Вопрос

Что такое Angular DevTools?

Ответ

Angular DevTools — это расширение для браузера, которое позволяет инспектировать компонентное дерево, просматривать входные/выходные свойства и анализировать производительность.


Вопрос

Как отладить шаблонную ошибку в Angular?

Ответ

Ошибки в шаблонах выводятся в консоль браузера с указанием компонента и строки. Использование строгого режима (strictTemplates) в tsconfig.json помогает находить ошибки на этапе компиляции.


Вопрос

Что делает флаг strict в tsconfig.json?

Ответ

Флаг strict включает строгую типизацию TypeScript, что помогает выявлять потенциальные ошибки на этапе разработки.


Вопрос

Как обеспечить совместимость с Internet Explorer?

Ответ

Необходимо включить полифиллы в polyfills.ts и настроить target в tsconfig.json на es5. Однако начиная с Angular 12 поддержка IE официально прекращена.