1. Що таке Angular та які його ключові можливості?
Angular - це повноцінний frontend-фреймворк від Google для побудови масштабованих веб-додатків із підтримкою SPA, SSR, SSG та hybrid rendering.
Angular надає структурований підхід до розробки великих застосунків, включаючи архітектуру, маршрутизацію, управління станом і продуктивністю "out of the box".
-
Component-based architecture - декларативна побудова UI на основі компонентів.
-
Standalone Components - можливість створювати застосунки без жорсткої привʼязки до NgModules.
-
Reactivity (Signals + RxJS) - сучасна реактивна модель для роботи зі станом і асинхронними потоками даних.
-
Template control flow (@if, @for, @switch) - нативний, оптимізований синтаксис шаблонів.
-
Dependency Injection (DI) - потужна, tree-shakable система керування залежностями.
-
Routing & Lazy Loading - вбудована маршрутизація з підтримкою code splitting.
-
TypeScript-first - строга типізація та безпечна розробка за замовчуванням.
-
Rendering & Performance - оптимізований change detection, підтримка signal-based рендерингу та zoneless архітектури.
Коротко
Angular - це повноцінний framework, який покриває більшість потреб enterprise-рівня з коробки: від рендерингу та реактивності до маршрутизації, DI та оптимізації продуктивності.
2. Поясни, що таке data-binding в Angular та які є його типи?
- Data-binding - це механізм синхронізації даних між компонентом і шаблоном.
- Interpolation - одностороннє відображення даних у HTML:
<p>{{ userName }}</p>- Property binding - передача значень у властивості DOM-елементів/компонентів:
<img [src]="avatarUrl" />- Event binding - реакція на події DOM:
<button (click)="onSave()">Save</button>- Two-way binding - синхронізація стану між шаблоном і компонентом ([(...)]):
<input [(ngModel)]="email" />Коротко
В Angular доступні 4 основні типи зв’язування даних - interpolation, property binding, event binding, two-way binding.
3. Опиши архітектуру Angular-додатку.
- Архітектура Angular базується на компонентному підході з чітким розділенням відповідальностей.
-
Компоненти (Standalone) - будівельні блоки UI, кожен має шаблон, стилі, логіку.
-
Сервіси - бізнес-логіка, робота з API, збереження стану; надаються через DI.
-
Signals - сучасний спосіб керування станом і реактивністю.
-
Control flow (@if, @for, @switch) - керування відображенням у шаблонах.
-
Router - маршрутизація між екранами без NgModules, з підтримкою lazy loading.
-
Dependency Injection - інжекція залежностей з різними scope (root, component, environment).
4. Що таке компонент в Angular та як він використовується?
- Компонент - це основний будівельний блок Angular-додатку, що відповідає за частину UI та пов’язану з нею логіку.
-
класу (логіка, стан),
-
шаблону HTML,
-
стилів,
-
метаданих (selector, imports тощо).
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<h3>{{ name() }}</h3>
<button (click)="changeName()">Change</button>
`
})
export class UserCardComponent {
name = signal('Viktor');
changeName() {
this.name.set('Updated Name');
}
}У шаблоні іншого компонента можна підключити:
<app-user-card></app-user-card>Коротко
Компонент = ізольований блок UI + логіка. В Angular він створюється як standalone, без NgModules.
5. Що таке директиви в Angular та які з них найчастіше використовуються?
- Директива - це інструкція для DOM-елемента або компонента, яка змінює його поведінку чи вигляд.
-
Structural (змінюють DOM):
-
@if(новий синтаксис замість*ngIf)-
@for(новий синтаксис замість*ngFor) -
@switch(альтернатива*ngSwitch)
-
-
Attribute (змінюють властивості/стилі елемента):
-
ngClass -
ngStyle -
ngModel
-
-
Custom directives - можна створювати свої для повторного використання логіки.
Коротко
Директиви в Angular = спосіб керувати DOM. Найчастіше - @if, @for,
ngClass, ngStyle, ngModel.
6. Як створити сервіс в Angular і навіщо його використовують?
Сервіс - це клас із бізнес-логікою або функціоналом, який не пов’язаний напряму з UI.
-
повторного використання коду,
-
роботи з API,
-
керування станом,
-
інкапсуляції логіки поза компонентом.
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UserService {
userName = signal('Guest');
setUser(name: string) {
this.userName.set(name);
}
}import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-header',
standalone: true,
template: `<h2>Welcome, {{ userService.userName() }}</h2>`
})
export class HeaderComponent {
userService = inject(UserService);
}Коротко
Сервіс створюють через @Injectable, а використовують для бізнес-логіки та
спільного стану між компонентами.
7. Поясни, що таке dependency injection (DI) в Angular.
Dependency Injection (DI) - це механізм Angular, який автоматично створює та надає об’єкти (сервіси, токени) компонентам чи іншим сервісам замість ручного створення через new.
-
спрощує тестування (можна підмінити залежності mock-ами),
-
забезпечує повторне використання сервісів,
-
керує життєвим циклом об’єктів (singleton, scoped).
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class ApiService {
getData() {
return ['item1', 'item2'];
}
}import { Component, inject } from '@angular/core';
import { ApiService } from './api.service';
@Component({
selector: 'app-list',
standalone: true,
template: `<li *ngFor="let item of data">{{ item }}</li>`
})
export class ListComponent {
api = inject(ApiService);
data = this.api.getData();
}Коротко
DI в Angular = автоматичне надання залежностей (наприклад, сервісів) компонентам
без new.
8. Що таке модуль в Angular і для чого він використовується?
- У попередніх версіях Angular (до 15) модулі (NgModule) були обов’язковими для структурування застосунку. В Angular 20 модулі більше не потрібні, оскільки з’явилися standalone components.
-
сумісності зі старим кодом,
-
групування функціоналу (напр. Angular Material ще має модулі),
-
поступової міграції на standalone API.
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
bootstrap: [AppComponent]
})
export class AppModule {}bootstrapApplication(AppComponent, {
providers: []
});Коротко
Модулі в Angular зараз - це легасі-інструмент, який замінено на standalone компоненти. Їхня головна роль сьогодні - лише для підтримки старого коду чи бібліотек.
9. Як обробляти події в Angular?
В Angular події обробляються через event binding, тобто підписку на подію DOM або кастомної події компонента.
<button (click)="onClick()">Click me</button>import { Component } from '@angular/core';
@Component({
selector: 'app-button',
standalone: true,
template: `<button (click)="onClick()">Click me</button>`,
})
export class ButtonComponent {
onClick() {
console.log('Button clicked!');
}
}import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-child',
standalone: true,
template: `<button (click)="notifyParent()">Notify</button>`
})
export class ChildComponent {
@Output() notify = new EventEmitter<string>();
notifyParent() {
this.notify.emit('Hello from child');
}
}<app-child (notify)="onNotify($event)"></app-child>Коротко: в Angular події обробляються через (eventName)="handler()" для DOM та
через @Output + EventEmitter для кастомних подій.
10. Що таке двостороннє зв’язування (two-way binding) і як його реалізувати в Angular?
Двостороннє зв’язування (two-way binding) - це синхронізація стану між компонентом і шаблоном, коли:
- зміни в UI оновлюють стан компонента,
- зміни в компоненті автоматично відображаються в UI.
<input [(ngModel)]="name" />
<p>Hello, {{ name }}</p>@Component({
selector: 'app-input',
standalone: true,
template: `<input [(ngModel)]="name" />`
})
export class InputComponent {
name = 'Evan';
}import { Component, signal } from '@angular/core';
@Component({
selector: 'app-input',
standalone: true,
template: `
<input
[value]="name()"
(input)="name.set($any($event.target).value)"
/>
<p>Hello, {{ name() }}</p>
`
})
export class InputComponent {
name = signal('Evan');
}Signals забезпечують реактивність, але two-way binding тут реалізується вручну через event → set().
import { Component, model } from '@angular/core';
@Component({
selector: 'app-input',
standalone: true,
template: `
<input
[value]="name()"
(input)="name.set($any($event.target).value)"
/>
`
})
export class InputComponent {
name = model('Evan');
}<app-input [(name)]="userName"></app-input>Коротко
ngModel- legacy two-way bindingsignal()- сучасна реактивність (локальний стан)model()- рекомендований спосіб two-way binding у сучасному Angular
11. Поясни різницю між компонентом і директивою в Angular?
-
Компонент
-
це спеціальний тип директиви, який має шаблон (HTML) + стилі + логіку;
-
використовується для створення UI-елементів;
-
приклад:
@Component({ selector: 'app-user', template: '<p>User</p>' }).
-
-
Директива
-
не має власного шаблону;
-
змінює поведінку або вигляд існуючих елементів/компонентів;
-
може бути structural (
@if,@for) або attribute (ngClass,ngStyle).
-
import { Directive, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[highlight]',
standalone: true
})
export class HighlightDirective {
constructor(el: ElementRef, r: Renderer2) {
r.setStyle(el.nativeElement, 'background', 'yellow');
}
}Використання у шаблоні:
<p highlight>Text with highlight</p>Коротко
компонент = директива + шаблон, а директива = поведінка без власного UI.
12. Що таке пайпи (Pipes) в Angular та де їх варто використовувати?
Pipe - це клас, який трансформує дані без зміни їхнього оригінального стану. Використовується у шаблонах для форматування значень.
-
date→ форматування дат -
currency→ вивід валют -
uppercase/lowercase→ зміна регістру -
async→ робота з Promise / Observable
<p>{{ today | date:'dd/MM/yyyy' }}</p>
<p>{{ price | currency:'USD' }}</p>import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'exclaim',
standalone: true
})
export class ExclaimPipe implements PipeTransform {
transform(value: string): string {
return value + '!';
}
}У шаблоні:
<p>{{ 'Hello' | exclaim }}</p>
<!-- Hello! -->Коротко
Pipes потрібні для форматування та трансформації даних у шаблоні, щоб не захаращувати логіку компонента.
13. Як обробляти надсилання форм (form submissions) в Angular?
В Angular є два основні підходи:
- Template-driven forms (простий варіант, з
ngModel):
<form #form="ngForm" (ngSubmit)="onSubmit(form.value)">
<input name="email" [(ngModel)]="email" required />
<button type="submit">Send</button>
</form>onSubmit(value: any) {
console.log('Form submitted:', value);
}- Reactive forms (рекомендований для складних кейсів):
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-login',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input formControlName="email" />
<button type="submit">Login</button>
</form>
`
})
export class LoginComponent {
form = new FormGroup({
email: new FormControl('')
});
onSubmit() {
console.log(this.form.value);
}
}Коротко
Форми в Angular обробляються через (ngSubmit) і бувають template-driven та
reactive. Для простих форм можна брати ngModel, для великих і складних -
reactive forms.
14. Що таке Angular CLI і для чого його використовують?
Angular CLI - це офіційний інструмент командного рядка для створення та керування Angular-проєктами.
-
ng new→ створення нового застосунку -
ng serve→ локальний дев-сервер з hot reload -
ng generate (ng g)→ генерація компонентів, сервісів, пайпів, директив -
ng build→ продакшн-білд з оптимізацією -
ng test, ng e2e→ запуск тестів -
ng add→ інтеграція бібліотек (напр. Angular Material) -
ng update→ оновлення Angular до нової версії
Коротко: Angular CLI = швидкий старт, генерація коду, білд і управління життєвим циклом проєкту.
15. Як виконувати HTTP-запити в Angular за допомогою HttpClient ?
В Angular для роботи з HTTP використовується HttpClient, який надає методи get, post, put, delete тощо.
-
Імпортувати HttpClientModule у bootstrapApplication.
-
Інжектити HttpClient у сервіс чи компонент.
-
Виконати запит і підписатися (або використовувати async pipe).
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class ApiService {
constructor(private http: HttpClient) {}
getUsers() {
return this.http.get('https://jsonplaceholder.typicode.com/users');
}
}import { Component, inject } from '@angular/core';
import { AsyncPipe, NgFor } from '@angular/common';
import { ApiService } from './api.service';
@Component({
selector: 'app-users',
standalone: true,
imports: [NgFor, AsyncPipe],
template: `
<ul>
<li *ngFor="let user of users$ | async">{{ user.name }}</li>
</ul>
`
})
export class UsersComponent {
api = inject(ApiService);
users$ = this.api.getUsers();
}Коротко
В Angular 20 HTTP-запити робляться через HttpClient, а результат часто обробляється в шаблоні через async pipe.
16. Як передати дані з батьківського компонента до дочірнього?
Передача даних відбувається через input-зв’язування (@Input() декоратор). Батьківський компонент передає значення дочірньому через атрибут у шаблоні.
child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
standalone: true,
template: `<p>Message: {{ message }}</p>`
})
export class ChildComponent {
@Input() message = '';
}parent.component.ts
import { Component } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
standalone: true,
imports: [ChildComponent],
template: `<app-child [message]="parentMessage"></app-child>`
})
export class ParentComponent {
parentMessage = 'Hello from Parent!';
}Коротко:
- Дані від батька до дитини передаються через @Input() - це property binding [property]="value".
17. Як передати подію або дані від дочірнього компонента до батьківського?
Для передачі подій вгору використовується @Output() разом із EventEmitter. Дочірній компонент «викидає» подію, а батьківський підписується на неї через (eventName) у шаблоні.
child.component.ts
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-child',
standalone: true,
template: `<button (click)="sendMessage()">Send</button>`
})
export class ChildComponent {
@Output() message = new EventEmitter<string>();
sendMessage() {
this.message.emit('Hello from Child!');
}
}parent.component.ts
import { Component } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
standalone: true,
imports: [ChildComponent],
template: `<app-child (message)="onMessage($event)"></app-child>`
})
export class ParentComponent {
onMessage(data: string) {
console.log('Received from child:', data);
}
}Коротко
Передача даних child → parent відбувається через @Output() і
(event) binding. Дитина емітить подію, батько слухає.
18. Які є життєві цикли (lifecycle hooks) компонентів в Angular і що вони означають?
Lifecycle hooks - це методи, які Angular викликає на різних етапах «життя» компонента: створення, оновлення, знищення.
| Хук | Коли викликається | Типове використання |
|---|---|---|
| ngOnChanges(changes) | Коли змінюються @Input властивості | Реакція на зміни вхідних даних від батьківського компонента |
| ngOnInit() | Один раз після ініціалізації компоненту | Ініціалізація даних, запитів до API |
| ngDoCheck() | На кожній зміні (детекції) | Кастомна логіка перевірки змін |
| ngAfterContentInit() | Один раз після вставлення контенту (ng-content) | Робота з проєктованим контентом |
| ngAfterContentChecked() | Після кожної перевірки контенту | Оновлення після змін у проєктованому контенті |
| ngAfterViewInit() | Один раз після ініціалізації view (дочірніх компонентів) | Доступ до елементів через ViewChild/ViewChildren |
| ngAfterViewChecked() | Після кожної перевірки view | Оновлення DOM після перевірки |
| ngOnDestroy() | Перед знищенням компоненту | Очищення підписок, таймерів, ресурсів |
import { Component, OnInit, OnDestroy } from '@angular/core';
@Component({
selector: 'app-demo',
standalone: true,
template: `<p>Lifecycle demo</p>`
})
export class DemoComponent implements OnInit, OnDestroy {
ngOnInit() {
console.log('Component initialized');
}
ngOnDestroy() {
console.log('Component destroyed');
}
}Коротко
Lifecycle hooks - це хуки життєвого циклу компонента, які дають змогу реагувати на створення, оновлення та знищення елемента.
19. Що таке ViewEncapsulation в Angular і для чого воно використовується?
ViewEncapsulation - це механізм інкапсуляції стилів у Angular, який визначає, як CSS компоненту впливає на DOM (чи лише на цей компонент, чи на весь застосунок).
| Тип інкапсуляції | Опис | Особливість |
|---|---|---|
| Emulated (default) | Angular імітує поведінку Shadow DOM, додаючи унікальні атрибути до елементів. | Стилі діють лише всередині цього компонента. |
| ShadowDom | Використовує нативний Shadow DOM браузера. | Повна ізоляція стилів, немає витоку назовні. |
| None | Без інкапсуляції. | Стилі поширюються глобально на весь застосунок. |
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-example',
templateUrl: './example.component.HTML',
styleUrls: ['./example.component.css'],
encapsulation: ViewEncapsulation.ShadowDom
})
export class ExampleComponent {}Коротко:
ViewEncapsulation контролює межі застосування CSS - чи стилі “ізольовані” всередині компонента, чи поширюються глобально. У більшості випадків - використовується Emulated.
20. Як застосовувати умовне (conditional) стилювання в Angular-компонентах?
- В Angular умовне стилювання реалізується через директиви прив’язки стилів та
класів -
ngClassіngStyle.
| Метод | Приклад | Опис |
|---|---|---|
| [ngClass] | <div [ngClass]="{ 'active': isActive, 'disabled': !isActive }"></div> |
Додає або забирає CSS-класи залежно від умови. |
| [ngStyle] | <div [ngStyle]="{ 'color': isActive ? 'green' : 'red' }"></div> |
Застосовує стилі напряму через об’єкт. |
| Класова прив’язка | <div [class.active]="isActive"></div> |
Додає клас, якщо умова true. |
| Стильова прив’язка | <div [style.backgroundColor]="isActive ? 'blue' : 'gray'"></div> |
Змінює конкретний CSS-властивість залежно від умови. |
Коротко:
Використовуй ngClass для керування класами та ngStyle або [style.prop] для
динамічних inline-стилів. Це дає повний контроль над виглядом елементів залежно
від стану компонента.
21. У чому різниця між структурними та атрибутними директивами в Angular?
- Директиви в Angular бувають структурні та атрибутні, і вони впливають на DOM по-різному.
| Тип директиви | Опис | Приклади | Вплив на DOM |
|---|---|---|---|
| Структурна (Structural) | Змінює структуру DOM - додає, видаляє або змінює елементи. | *ngIf, *ngFor, *ngSwitchCase |
Створює або прибирає елементи в дереві DOM. |
| Атрибутна (Attribute) | Змінює вигляд або поведінку наявного елемента. | ngClass, ngStyle, ngModel, кастомні директиви (наприклад, appHighlight) |
Не змінює структуру DOM, лише властивості або стилі елемента. |
Коротко:
Структурні директиви керують тим, що є в DOM, атрибутні директиви - тим, як це виглядає або поводиться.
22. Як створити власну структурну директиву в Angular?
Структурна директива змінює DOM (додає або видаляє елементи). Щоб створити кастомну структурну директиву:
| Крок | Опис |
|---|---|
| 1 | Створити директиву з декоратором @Directive і standalone: true. |
| 2 | Інжектити TemplateRef і ViewContainerRef для доступу до шаблону та контейнера. |
| 3 | Створити метод або сеттер, який вирішує, коли вставляти або видаляти шаблон. |
| 4 | Використовувати директиву через *yourDirective у шаблоні. |
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]',
standalone: true
})
export class UnlessDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
@Input() set appUnless(condition: boolean) {
this.viewContainer.clear();
if (!condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
}
}
}<p *appUnless="isLoggedIn">You are not logged in!</p>Коротко:
Кастомна структурна директива керує DOM через ViewContainerRef і
TemplateRef. Використовується з * синтаксисом у шаблоні.
23. Як зробити сервіс singleton в Angular?
У Angular singleton-сервіс - це сервіс, який створюється лише один раз і використовується у всьому застосунку. Для цього потрібно вказати, де він надається (provided).
| Спосіб | Приклад | Пояснення |
|---|---|---|
1. Через providedIn: 'root' |
@Injectable({ providedIn: 'root' }) |
Найпоширеніший спосіб. Сервіс реєструється в головному інжекторі, створюється один раз для всього застосунку. |
| 2. Через модуль (deprecated підхід) | Додати в providers масив модуля (@NgModule) |
Використовується рідше. Сервіс буде singleton лише в межах цього модуля. |
| 3. Через компонент (локальний інжектор) | Додати в providers масив компонента |
Сервіс не буде singleton - створюється новий екземпляр для кожного компонента. |
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private token = '';
setToken(t: string) { this.token = t; }
getToken() { return this.token; }
}Коротко:
Найкраща практика - @Injectable({ providedIn: 'root' }), бо це гарантує
singleton-поведінку і оптимізує tree-shaking.
24. Як використовувати Observables у сервісах для обміну даними між компонентами?
Observables у сервісах дозволяють реактивно ділитися даними між компонентами -
без прямої передачі через @Input() чи @Output().
| Підхід | Опис | Типовий випадок використання |
|---|---|---|
| Subject | Дає змогу як передавати (next()), так і підписуватись (subscribe()) на дані. |
Динамічне оновлення стану між компонентами. |
| BehaviorSubject | Зберігає останнє значення, яке автоматично отримують нові підписники. | Поточний стан (наприклад, авторизація, вибраний користувач). |
| ReplaySubject | Передає певну кількість останніх значень новим підписникам. | Історія подій або кешування даних. |
data.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class DataService {
private messageSource = new BehaviorSubject<string>('Hello');
message$ = this.messageSource.asObservable();
updateMessage(newMsg: string) {
this.messageSource.next(newMsg);
}
}component-a.ts
@Component({...})
export class ComponentA {
constructor(private dataService: DataService) {}
sendMessage() {
this.dataService.updateMessage('Message from A');
}
}component-b.ts
@Component({...})
export class ComponentB {
message = '';
constructor(private dataService: DataService) {
this.dataService.message$.subscribe(msg => this.message = msg);
}
}Коротко:
Сервіс з Subject або BehaviorSubject діє як “shared data channel” - один
компонент надсилає дані, інші підписуються. Це реактивний і чистий спосіб обміну
станом між компонентами.
25. Які існують способи надання (provide) сервісу в Angular і чим вони відрізняються?
- У Angular є кілька способів оголосити, де і як створюється сервіс. Від цього залежить область його дії (scope) - чи він буде singleton, чи матиме локальний екземпляр.
| Спосіб | Як реалізується | Область дії | Коментар |
|---|---|---|---|
1. providedIn: 'root' |
У декораторі @Injectable({ providedIn: 'root' }) |
Глобальна (один екземпляр у всьому застосунку) | ✅ Найкраща практика. Оптимізується tree-shaking. |
2. providedIn: 'platform' |
Через @Injectable({ providedIn: 'platform' }) |
Спільний сервіс між кількома Angular застосунками на одній сторінці | Рідко використовується. |
3. providedIn: 'any' |
Через @Injectable({ providedIn: 'any' }) |
Новий екземпляр для кожного lazy-loaded модуля | Корисно для ізольованих модулів. |
4. У providers масиві модуля (@NgModule) |
Додавання сервісу в providers |
Тільки в межах цього модуля | Використовується в legacy-проєктах. |
5. У providers масиві компонента |
providers: [MyService] у декораторі @Component |
Новий екземпляр для кожного екземпляра компонента | Для локального стану або ізольованої логіки. |
@Injectable({
providedIn: 'root'
})
export class UserService {}або
@Component({
selector: 'app-profile',
providers: [UserService]
})
export class ProfileComponent {}Коротко:
Найчастіше використовується providedIn: 'root' - це дає один спільний
екземпляр (singleton). Інші способи - для lazy-loading, ізоляції або особливих
випадків.
26. Поясни, що таке providedIn у сервісах Angular і яку роль воно відіграє?
providedIn- це параметр у декораторі@Injectable, який визначає, де Angular має зареєструвати сервіс у DI (Dependency Injection) системі. Від нього залежить область дії (scope) сервісу та кількість створених екземплярів.
Значення providedIn |
Опис | Область дії | Використання |
|---|---|---|---|
'root' |
Сервіс реєструється у головному інжекторі застосунку. | Глобальна (singleton у всьому застосунку). | ✅ Найпоширеніший і рекомендований спосіб. |
'platform' |
Один інжектор для всієї платформи (кілька Angular app на сторінці). | Спільний між застосунками. | Рідкісний випадок використання. |
'any' |
Кожен lazy-loaded модуль отримує власний екземпляр. | Локальна для модуля або компонента. | Для незалежних частин застосунку. |
Клас або модуль (SomeModule) |
Сервіс буде створено лише в межах цього модуля. | Локальна. | Використовується для модульної ізоляції. |
@Injectable({
providedIn: 'root'
})
export class LoggerService {
log(message: string) {
console.log(`[LOG]: ${message}`);
}
}Коротко:
providedIn визначає, де саме Angular створює сервіс і чи буде він спільним
(singleton). У більшості випадків використовують providedIn: 'root' - це
просто, ефективно і підтримує tree-shaking.
27. Як у Angular використовувати HttpClient для обробки JSON-даних?
HttpClient - це сервіс Angular для виконання HTTP-запитів. Він автоматично
перетворює JSON-відповіді в об’єкти JavaScript, тому додаткового парсингу не
потрібно.
| Крок | Опис |
|---|---|
| 1 | Імпортуй HttpClientModule у кореневий або standalone компонент. |
| 2 | Інжектуй HttpClient у сервіс або компонент. |
| 3 | Використовуй методи get(), post(), put(), delete() тощо. |
| 4 | Angular автоматично обробляє JSON через RxJS Observable. |
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface User {
id: number;
name: string;
email: string;
}
@Injectable({ providedIn: 'root' })
export class UserService {
private apiUrl = 'https://jsonplaceholder.typicode.com/users';
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl);
}
addUser(user: User): Observable<User> {
return this.http.post<User>(this.apiUrl, user);
}
}component.ts
@Component({...})
export class AppComponent {
users$ = this.userService.getUsers();
constructor(private userService: UserService) {}
}-
HttpClientавтоматично парсить JSON у JS-об’єкти. -
Можна вказати generic тип (
<User[]>), щоб отримати типізовану відповідь. -
Повертає Observable, тому можна застосовувати оператори RxJS (
map,catchError, тощо).
Коротко:
HttpClient - це зручний API для роботи з JSON у Angular. Він типізований,
реактивний і не потребує ручного JSON.parse().
28. Як обробляти REST API-запити та помилки у сервісах Angular?
- REST-запити в Angular виконуються через
HttpClient, а обробка помилок - через RxJS операторcatchError. Усе це зазвичай інкапсулюється в окремому сервісі, щоб компоненти залишалися “чистими”.
| Крок | Опис |
|---|---|
| 1 | Створи сервіс (@Injectable) і підключи HttpClient. |
| 2 | Використовуй методи get(), post(), put(), delete(). |
| 3 | Обгорни запити у pipe() з catchError() для обробки помилок. |
| 4 | Поверни типізований Observable, щоб компонент міг підписатися. |
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, throwError, Observable } from 'rxjs';
export interface Product {
id: number;
name: string;
price: number;
}
@Injectable({ providedIn: 'root' })
export class ProductService {
private apiUrl = 'https://api.example.com/products';
constructor(private http: HttpClient) {}
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.apiUrl).pipe(
catchError(this.handleError)
);
}
addProduct(product: Product): Observable<Product> {
return this.http.post<Product>(this.apiUrl, product).pipe(
catchError(this.handleError)
);
}
private handleError(error: HttpErrorResponse) {
if (error.status === 0) {
console.error('Network error:', error.error);
} else {
console.error(`API returned code ${error.status}:`, error.error);
}
return throwError(() => new Error('Something went wrong; please try again.'));
}
}-
catchError()- RxJS оператор для перехоплення помилок. -
throwError()- створює новий стрім з помилкою. -
Обробку логіки (
try again,notify user,log error) краще робити всередині сервісу, не в компоненті.
Коротко:
- REST API виклики обробляються у сервісі через
HttpClient. Для помилок використовуйcatchError()у поєднанні з власнимhandleError()методом - це робить код чистим і передбачуваним.
29. Як налаштовується маршрутизація (routing) в Angular-застосунках?
Routing в Angular визначає, який компонент відображається при переході на певний
URL. Він налаштовується через масив маршрутів і RouterModule (або
provideRouter для standalone API).
| Крок | Опис |
|---|---|
| 1 | Створити масив маршрутів (Routes[]), де кожен об’єкт описує шлях і компонент. |
| 2 | Імпортувати RouterModule.forRoot(routes) або використати provideRouter(routes) у main.ts. |
| 3 | Додати <router-outlet> у шаблон, щоб рендерити активний маршрут. |
| 4 | Використовувати директиви [routerLink] для навігації. |
app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: '**', redirectTo: '' } // catch-all
];main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { AppComponent } from './app.component';
import { routes } from './app.routes';
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)]
});app.component.HTML
<nav>
<a routerLink="/">Home</a>
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>-
Route Guards (
canActivate,canDeactivate) - для захисту маршрутів. -
Lazy Loading - динамічне підвантаження модулів або компонентів.
-
Route Parameters (
:id) - для передачі динамічних значень у маршруті.
Коротко:
Маршрутизація в Angular конфігурується через масив Routes і RouterModule або
provideRouter(). Компоненти рендеряться у <router-outlet>, а переходи
виконуються через [routerLink].
30. Як у Angular створити маршрут, який динамічно завантажує модуль лише під час доступу до нього (lazy loading)?
Так, у сучасному Angular (v16–20) це робиться через lazy loading з
використанням динамічного import() у файлі маршрутизації. Це дозволяє не
включати модуль у основний bundle, а завантажувати його лише при навігації.
// app.routes.ts (Angular 17+ standalone API)
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
},
];У випадку standalone-компонентів:
{
path: 'dashboard',
loadComponent: () =>
import('./dashboard/dashboard.component').then(c => c.DashboardComponent),
}Коротко:
loadChildrenабоloadComponentвикористовуються для lazy loading.- Модуль/компонент завантажується лише при першому переході на відповідний маршрут.
- Це оптимізує стартову швидкість застосунку.
31. Що таке RouterOutlet в Angular і як його використовують?
<router-outlet>- це директива, яка визначає місце у шаблоні, куди Angular підставляє компонент, що відповідає активному маршруту. Вона є “контейнером” для відображення контенту згідно з конфігурацією маршрутизатора.
<!-- app.component.HTML -->
<nav>
<a routerLink="/home">Home</a>
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>// app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
export const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
];Коротко:
RouterOutlet- точка вставки для компонентів маршруту.- Підтримує вкладені маршрути (може бути кілька
router-outlet). - Без нього маршрути не відображаються у DOM.
32. Як у Angular застосовуються route guards (захисники маршрутів)?
- Route guards - це сервіси, які контролюють доступ до маршрутів. Вони
реалізують спеціальні інтерфейси (
CanActivate,CanDeactivate,CanLoad,CanMatch,Resolve) і використовуються в конфігурації маршрутизатора.
// auth.guard.ts
import { CanActivateFn } from '@angular/router';
export const authGuard: CanActivateFn = (route, state) => {
const isLoggedIn = !!localStorage.getItem('token');
return isLoggedIn; // або redirectUrl при потребі
};// app.routes.ts
export const routes = [
{
path: 'dashboard',
canActivate: [authGuard],
loadComponent: () =>
import('./dashboard/dashboard.component').then(c => c.DashboardComponent),
},
];Коротко:
- Guards перевіряють, чи можна активувати, завантажити або покинути маршрут.
- Починаючи з Angular 15+, зручно використовувати функціональні guards
(
CanActivateFn) без класів. - Повертають
true/false,UrlTree, абоObservable/Promise.
33. Для чого в Angular використовується ActivatedRoute у маршрутизації?
ActivatedRoute дає доступ до інформації про поточний активний маршрут, включно
з параметрами, query-параметрами, фрагментами URL і даними, переданими через
data. Використовується всередині компонентів для отримання контексту маршруту.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user',
template: `<p>User ID: {{ userId }}</p>`
})
export class UserComponent implements OnInit {
userId!: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// отримати параметр з URL
this.userId = this.route.snapshot.paramMap.get('id')!;
// або підписка на зміни параметрів
this.route.paramMap.subscribe(params => {
this.userId = params.get('id')!;
});
}
}Коротко:
ActivatedRoute- доступ до параметрів маршруту, query-параметрів, fragment і data.- Потрібен для динамічного завантаження даних залежно від маршруту.
- Працює як зі snapshot, так і з Observable для реактивного оновлення.
34. Що таке параметри маршруту в Angular і як до них звертатися?
Параметри маршруту - це змінні частини URL, які визначаються у маршрутах та дозволяють передавати дані у компонент.
// app.routes.ts
import { Routes } from '@angular/router';
import { UserComponent } from './user.component';
export const routes: Routes = [
{ path: 'user/:id', component: UserComponent },
];// user.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user',
template: `<p>User ID: {{ userId }}</p>`
})
export class UserComponent implements OnInit {
userId!: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// Через snapshot (одноразово)
this.userId = this.route.snapshot.paramMap.get('id')!;
// Через Observable (реактивно при зміні маршруту)
this.route.paramMap.subscribe(params => {
this.userId = params.get('id')!;
});
}
}Коротко:
- Route parameters - частина URL (наприклад,
/user/123→id = 123). - Доступ через
ActivatedRoute.snapshot.paramMapабоActivatedRoute.paramMap.subscribe(). - Використовуються для динамічного рендерингу контенту.
35. Як у Angular заздалегідь завантажити дані перед переходом на маршрут (resolve data)?
Для цього використовують Route Resolver - сервіс, який реалізує інтерфейс
Resolve<T>. Angular чекає, поки resolver отримає дані, і передає їх у
компонент через ActivatedRoute.data.
// user.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { UserService } from './user.service';
@Injectable({ providedIn: 'root' })
export class UserResolver implements Resolve<any> {
constructor(private userService: UserService) {}
resolve() {
return this.userService.getUser(); // може повертати Observable або Promise
}
}// app.routes.ts
import { Routes } from '@angular/router';
import { UserComponent } from './user.component';
import { UserResolver } from './user.resolver';
export const routes: Routes = [
{
path: 'user/:id',
component: UserComponent,
resolve: { userData: UserResolver }
}
];// user.component.ts
ngOnInit() {
this.route.data.subscribe(data => {
console.log(data.userData); // доступ до preload-даних
});
}Коротко:
- Resolver завантажує дані перед активацією маршруту.
- Повертає
Observable,Promiseабо просте значення. - Дані доступні через
ActivatedRoute.dataу компоненті.
36. Як реалізувати lazy loading модулів або компонентів у Angular?
Lazy loading дозволяє завантажувати модулі чи компоненти тільки при переході на відповідний маршрут, щоб зменшити початковий розмір bundle.
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.module').then(m => m.AdminModule),
},
];{
path: 'dashboard',
loadComponent: () =>
import('./dashboard/dashboard.component').then(c => c.DashboardComponent),
}Коротко:
loadChildren- для lazy loading модулів.loadComponent- для lazy loading standalone-компонентів (Angular 15+).- Підвищує швидкість старту додатку, завантажуючи код лише за потреби.
37. Поясни різницю між Template-driven та Reactive формами в Angular.
-
Template-driven форми будуються переважно у HTML-шаблоні за допомогою директив (ngModel, ngForm). Вони простіші, підходять для невеликих форм, але менш контрольовані - логіка зосереджена у шаблоні.
-
Reactive форми створюються в TypeScript-коді за допомогою FormGroup, FormControl, FormBuilder. Вони більш предиктивні, масштабовані й краще підходять для складних форм, валідації та тестування.
Template-driven:
<form #form="ngForm">
<input name="email" ngModel required />
</form>Reactive:
form = new FormGroup({
email: new FormControl('', { nonNullable: true, validators: [Validators.required] })
});<form [formGroup]="form">
<input formControlName="email" />
</form>Коротко:
- Template-driven - декларативний підхід у шаблоні.
- Reactive - імперативний підхід у коді, з повним контролем над станом форми.
38. Як виконується валідація користувацького введення у формах Angular?
В Angular є вбудована, кастомна та асинхронна валідація. Валідація визначається
або через HTML-атрибути (у Template-driven формах), або через Validators у
Reactive формах.
Reactive форма з валідацією:
form = new FormGroup({
email: new FormControl('', {
nonNullable: true,
validators: [Validators.required, Validators.email]
}),
password: new FormControl('', {
validators: [Validators.required, Validators.minLength(6)]
})
});HTML:
<form [formGroup]="form">
<input formControlName="email" />
<div *ngIf="form.controls.email.invalid && form.controls.email.touched">
Invalid email
</div>
</form>Кастомний валідатор (приклад):
function forbiddenNameValidator(control: FormControl) {
return control.value === 'admin' ? { forbiddenName: true } : null;
}Асинхронний валідатор (приклад):
function emailExistsValidator(service: UserService): AsyncValidatorFn {
return control => service.checkEmail(control.value).pipe(
map(exists => (exists ? { emailTaken: true } : null))
);
}Коротко:
- Використовуємо Validators (built-in або custom).
- Реактивний підхід дає більше контролю й гнучкості для відображення помилок та асинхронних перевірок.
39. Як динамічно додавати або видаляти елементи управління (form controls) у Reactive Forms в Angular?
- Для динамічної роботи з полями форми використовують
FormArrayабо методиaddControl()/removeControl()уFormGroup. - Це дозволяє створювати або видаляти поля на льоту - наприклад, динамічні списки чи масиви інпутів.
Приклад із FormArray:
form = new FormGroup({
users: new FormArray<FormControl<string>>([])
});
get users() {
return this.form.get('users') as FormArray;
}
addUser() {
this.users.push(new FormControl('', Validators.required));
}
removeUser(index: number) {
this.users.removeAt(index);
}HTML:
<form [formGroup]="form">
<div formArrayName="users">
<div *ngFor="let user of users.controls; let i = index">
<input [formControlName]="i" />
<button type="button" (click)="removeUser(i)">Remove</button>
</div>
</div>
<button type="button" (click)="addUser()">Add User</button>
</form>Коротко:
- Використовуй FormArray для списків контролів.
- Використовуй
addControl()/removeControl()уFormGroupдля динамічних окремих полів.
40. Що таке FormGroup у Angular і як він працює?
FormGroup- це об’єкт, який об’єднує кількаFormControlабо навіть іншихFormGroupу єдину структуру. Він дозволяє керувати станом, значеннями та валідацією всієї групи як одного цілого.
Ключові моменти:
-
FormGroupзберігає набір контролів у вигляді об’єкта. -
Дозволяє отримати стан (
valid,dirty,touched) або значення (value) всієї групи. -
Може мати групову валідацію (на рівні всієї форми).
Приклад:
form = new FormGroup({ user: new FormGroup({ name: new FormControl('',
Validators.required), email: new FormControl('', Validators.email) }) });HTML:
<form [formGroup]="form">
<div formGroupName="user">
<input formControlName="name" />
<input formControlName="email" />
</div>
</form>Коротко:
FormGroup= контейнер для контролів → дає змогу керувати групою полів як єдиним об’єктом (для валідації, оновлення, сабміту).
41. Як створити власні (custom) валідатори у формах Angular?
Кастомний валідатор - це функція, яка приймає FormControl або
AbstractControl і повертає об’єкт помилки { [key: string]: any } або null,
якщо помилок немає. Її можна використовувати в Reactive Forms або
Template-driven.
Синхронний валідатор (приклад):
import { AbstractControl, ValidationErrors } from '@angular/forms';
export function forbiddenWordValidator(control: AbstractControl):
ValidationErrors | null { const forbidden = control.value?.toLowerCase() ===
'admin'; return forbidden ? { forbiddenWord: true } : null; }Використання:
form = new FormGroup({ username: new FormControl('', [forbiddenWordValidator])
});Асинхронний валідатор (приклад):
export function uniqueEmailValidator(service: UserService) {
return (control: AbstractControl) => {
return service
.checkEmail(control.value)
.pipe(map(isTaken => (isTaken ? { emailTaken: true } : null)));
};
}Коротко:
- Кастомний валідатор - це функція, що повертає
{ errorKey: true }абоnull. - Може бути синхронним або асинхронним (через Observable).
- Підходить для складної бізнес-логіки, якої немає серед стандартних
Validators.
42. Поясни, як використовувати formArrayName для роботи з полями форми типу масиву в Angular.
formArrayNameвикористовується в шаблоні для прив’язки доFormArrayусередині Reactive Forms. Це дозволяє відображати та керувати динамічними наборами полів (наприклад, списком телефонів, тегів чи користувачів).
Приклад:
form = new FormGroup({
phones: new FormArray<FormControl<string>>([
new FormControl('', Validators.required)
])
});
get phones() {
return this.form.get('phones') as FormArray;
}
addPhone() {
this.phones.push(new FormControl('', Validators.required));
}
removePhone(index: number) {
this.phones.removeAt(index);
}HTML:
<form [formGroup]="form">
<div formArrayName="phones">
<div *ngFor="let phone of phones.controls; let i = index">
<input [formControlName]="i" placeholder="Phone number" />
<button type="button" (click)="removePhone(i)">Remove</button>
</div>
</div>
<button type="button" (click)="addPhone()">Add Phone</button>
</form>Коротко:
formArrayName- це директива для доступу доFormArrayу шаблоні.- Кожен елемент масиву - окремий FormControl або FormGroup.
- Використовується для динамічних форм, де кількість полів може змінюватися.
43. Як відправити дані форми з Angular-додатку на бекенд-сервіс?
- У Angular форма зазвичай відправляється через сервіс, який використовує
HttpClientдля HTTP-запиту (POST,PUTтощо). Після сабміту зчитуютьform.value, перевіряютьform.validі викликають метод сервісу.
Приклад:
// user.service.ts
@Injectable({ providedIn: 'root' })
export class UserService {
constructor(private http: HttpClient) {}
submitUser(data: any) {
return this.http.post('/api/users', data);
}
}// component.ts
form = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl('', Validators.email)
});
constructor(private userService: UserService) {}
onSubmit() {
if (this.form.valid) {
this.userService.submitUser(this.form.value).subscribe({
next: () => console.log('User saved!'),
error: err => console.error('Error:', err)
});
}
}HTML:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input formControlName="name" />
<input formControlName="email" />
<button type="submit">Save</button>
</form>Коротко:
- Отримуєш
form.value. - Перевіряєш
form.valid. - Відправляєш через
HttpClient(звичайно через сервіс). - Обробляєш відповідь у
subscribe().
44. Що таке виявлення змін (change detection) і як його реалізує Angular?
Виявлення змін (change detection) - це процес, за допомогою якого Angular визначає, що дані в компоненті змінились, і оновлює відповідні частини UI.
Як Angular це реалізує:
-
У сучасному Angular механізм базується на Signals - Angular відслідковує залежності між сигналами та шаблоном і оновлює тільки ті фрагменти DOM, які справді змінились (fine-grained reactivity, без глобального циклу).
-
Без Signals Angular використовує Zone.js, який перехоплює async-події і запускає перевірку всього дерева компонентів.
-
Для ручного контролю можливе використання
ChangeDetectorRef.
Коротко:
Angular 20+ оновлює UI точково через Signals, а старий підхід (Zone.js + глобальна перевірка) використовується лише для зворотної сумісності.
45. Які основні способи оптимізації продуктивності Angular-застосунку?
- Використання Signals
Fine-grained reactivity → оновлюється тільки та частина DOM, яка залежить від сигналу.
- Standalone Components
Менший бандл, швидший старт, немає модульних оверхедів.
- Lazy loading та route-level code splitting
Завантажувати лише той код, який потрібен на даному маршруті.
- OnPush (для legacy компонентів без сигналів)
Зменшує кількість викликів change detection у старих компонентах.
- trackBy у ngFor
Запобігає перерендеру списків:
<li *ngFor="let item of items; trackBy: trackById"></li>- Оптимізація RxJS
takeUntil, shareReplay, уникання непотрібних сабскрипцій.
- Async Pipe замість manual subscribe
Запобігає memory leaks і зайвим CD-циклами.
- Оптимізація шаблону
Мінімізувати важкі обчислення у template (винести в getters або signals).
- Preloading strategies
Оптимізує навігацію між маршрутами (наприклад, PreloadAllModules або custom).
- Build-level оптимізації
ng build --configuration production
minification, treeshaking, локальні i18n-файли
image optimization (WebP/AVIF)
46. Що таке зони (Zones) в Angular і яку роль вони відіграють?
Зони (Zone.js) - це механізм, який перехоплює всі асинхронні операції (події, таймери, проміси) і автоматично запускає change detection після їх виконання.
Щоб не писати вручну, коли саме оновлювати UI.
Будь-який async виклик → Angular знає, що могли змінитися дані → оновлює вʼю.
Zone.js патчить setTimeout, XHR, addEventListener тощо.
Після завершення async-дії зона викликає Angular change detection.
Зони більше не потрібні для реактивного рендерингу (Signals).
Є режим Noop Zone / Zoneless, де Angular оновлює UI точково без глобального CD.
Коротко:
Zones - старий механізм для автозапуску change detection. У нових версіях Angular його замінює сигнал-базована реактивність.
47. Як у Angular налаштувати SSR за допомогою Angular Universal
- Увімкнення SSR
ng add @angular/ssrАвтоматично створюється сервер, SSR bootstrap і hydration.
- Bootstrap
Browser
bootstrapApplication(AppComponent, appConfig);Server
export default () =>
bootstrapApplication(AppComponent, appConfig);- Hydration
provideClientHydration()Angular підхоплює HTML, а не рендерить заново.
- Сервер (Node / Express)
renderApplication(bootstrap, { url, document })- SSR-safe код
isPlatformBrowser(PLATFORM_ID)без window, document напряму
- Дані
-
API викликаємо на сервері
-
Передаємо в браузер через
TransferState -
Signals працюють з SSR без проблем
- Коли використовувати
SEO, швидкий FCP
- Коли не використовувати
Адмінки, real-time dashboards
48. У чому різниця між Ahead-of-Time (AOT) та Just-in-Time (JIT) компіляцією в Angular і коли використовується кожна з них?
-
Компіляція під час білду
-
Angular-шаблони → JS до запуску в браузері
-
Швидший старт
-
Кращий performance
-
Менший бандл
-
Ранні compile-time помилки
-
Безпека (немає runtime compiler)
Default у production
-
Компіляція в браузері під час виконання
-
Потрібен Angular compiler у runtime
-
Повільніший старт
-
Більший бандл
-
Зручно для dev (швидкий rebuild)
Використовується в dev-режимі
49. Опишіть декоратори, доступні в Angular.
Angular використовує TypeScript-декоратори для опису метаданих компонентів, директив, сервісів та DI.
- Класові декоратори
@Component
Описує UI-компонент.
@Component({ selector: 'app-user', standalone: true, template: `{{ name }}` })
export class UserComponent { name = 'Viktor'; }@Directive
Створює кастомну директиву (attribute або structural).
@Directive({ selector: '[appHighlight]' }) export class HighlightDirective {}@Pipe
Створює pipe для трансформації даних у шаблонах.
@Pipe({ name: 'uppercase' }) export class UppercasePipe { transform(value:
string) { return value.toUpperCase(); } }@Injectable
Позначає клас як сервіс для DI.
@Injectable({ providedIn: 'root' }) export class UserService {}- Property decorators (взаємодія з шаблоном)
@Input
Передача даних у компонент.
@Input() title!: string;@Output
Передача подій з компонента.
@Output() saved = new EventEmitter<void>();@HostBinding
Байндінг до властивостей host-елемента.
@HostBinding('class.active') isActive = true;@HostListener
Підписка на події host-елемента.
@HostListener('click') onClick() {}- Dependency Injection decorators
@Inject
Явна інʼєкція залежності.
constructor(@Inject(API_URL) private apiUrl: string) {}@Optional
Залежність може бути відсутня.
constructor(@Optional() private logger?: LoggerService) {}@Self, @SkipSelf
Контроль області пошуку залежностей.
constructor(@Self() private control: NgControl) {}- View / Content decorators
@ViewChild / @ViewChildren
Доступ до елементів власного шаблону.
@ViewChild('input') input!: ElementRef;@ContentChild / @ContentChildren
Доступ до проєктованого контенту (ng-content).
@ContentChild(TemplateRef) tpl!: TemplateRef<any>;- Стан у Angular 20+
-
Декоратори все ще підтримуються
-
Але часто замінюються:
-
inject()замість constructor DI -
Signals замість
@Input+ngOnChanges
-
-
Standalone API не скасовує декоратори, лише спрощує архітектуру
50. Як реалізувати анімаційні переходи в Angular-додатку?
Angular має вбудовану систему анімацій через @angular/animations. У Angular
20+ використовую provideAnimations() у конфігурації:
// app.config.ts
import { provideAnimations } from '@angular/platform-browser/animations';
export const appConfig: ApplicationConfig = {
providers: [provideAnimations()]
};- Анімація станів компонента
typescriptimport { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
template: `<div [@openClose]="isOpen()">Content</div>`,
animations: [
trigger('openClose', [
state('true', style({ height: '200px', opacity: 1 })),
state('false', style({ height: '0px', opacity: 0 })),
transition('false <=> true', animate('300ms ease-in-out'))
])
]
})
export class MyComponent {
isOpen = signal(false);
}- Анімація роутів
// app.component.ts
@Component({
template: `
<div [@routeAnimations]="outlet.activatedRouteData['animation']">
<router-outlet #outlet="outlet"></router-outlet>
</div>
`,
animations: [
trigger('routeAnimations', [
transition('* <=> *', [
query(':enter', [style({ opacity: 0 })], { optional: true }),
query(':leave', [animate('200ms', style({ opacity: 0 }))], { optional: true }),
query(':enter', [animate('300ms', style({ opacity: 1 }))], { optional: true })
])
])
]
})- Анімація списків (stagger)
animations: [
trigger('listAnimation', [
transition('* => *', [
query(':enter', [
style({ opacity: 0, transform: 'translateY(-20px)' }),
stagger(100, [
animate('300ms', style({ opacity: 1, transform: 'translateY(0)' }))
])
], { optional: true })
])
])
]- Використовую
transformтаopacityдля GPU-acceleration :enter/:leaveдля появи/зникнення елементів- Створюю reusable анімації через
animation()таuseAnimation() - Відстежую події:
(@trigger.done)="onAnimationDone($event)"
51. Як створюються власні директиви в Angular?
- Типи директив
-
Attribute directive - змінює поведінку або вигляд елемента приклад: highlight, tooltip
-
Structural directive - змінює структуру DOM приклад: *ngIf, *ngFor
- Attribute directive
Приклад: директива підсвічування
import { Directive, ElementRef, Input, effect, signal } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
color = signal('yellow');
constructor(private el: ElementRef) {
effect(() => {
this.el.nativeElement.style.backgroundColor = this.color();
});
}
@Input()
set appHighlight(value: string) {
this.color.set(value);
}
}Використання
<p appHighlight="lightblue">Highlighted text</p>- Structural directive
Приклад: кастомний *appIf
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appIf]',
standalone: true,
})
export class AppIfDirective {
constructor(
private tpl: TemplateRef<unknown>,
private vcr: ViewContainerRef
) {}
@Input()
set appIf(condition: boolean) {
this.vcr.clear();
if (condition) {
this.vcr.createEmbeddedView(this.tpl);
}
}
}Використання
<div *appIf="isVisible">Visible content</div>- Best practices
-
Використовуйте standalone директиви
-
Для реактивності - signals + effect
-
Уникайте прямої роботи з DOM (краще Renderer2, якщо потрібно)
-
Мінімізуйте side-effects у constructor
-
Structural директиви завжди працюють через TemplateRef + ViewContainerRef
Коротко
Власні директиви створюються через @Directive, бувають attribute та structural
і в Angular 20+ зазвичай є standalone з реактивністю на signals.
52. Чи можете ви пояснити використання директив ngClass та ngStyle?
Використання директив ngClass та ngStyle в Angular
Директива ngClass використовується для динамічного додавання або видалення
CSS-класів на елементі.
- Обʼєкт (найпоширеніше)
<div [ngClass]="{ active: isActive, disabled: isDisabled }"></div>- Масив
<div [ngClass]="['card', isDark ? 'dark' : 'light']"></div>- Рядок
<div [ngClass]="dynamicClass"></div>-
Використовуйте для умовної стилізації
-
Працює ефективно з
ChangeDetectionStrategy.OnPush -
Добре поєднується з signals
isActive = signal(true);Директива ngStyle використовується для динамічного задання inline-стилів.
<div [ngStyle]="{ color: textColor, fontSize: fontSize + 'px' }"></div>textColor = 'red';
fontSize = 16;-
Використовуйте лише коли стилі не можна описати класами
-
Уникайте великої кількості inline-стилів (performance + maintainability)
-
Віддавайте перевагу
ngClass -
Для складної логіки - computed signals
-
Для дизайн-систем - класи + CSS variables
ngClass- для керування CSS-класами,ngStyle- для динамічних inline-стилів.
53. Як ви взаємодієте з DOM безпосередньо за допомогою директив?
Взаємодія з DOM за допомогою директив в Angular
ElementRef(обмежено)
Дає доступ до нативного DOM-елемента.
constructor(private el: ElementRef) {
this.el.nativeElement.focus();
}Недолік: прямий доступ до DOM Не рекомендовано для SSR та безпеки
- Renderer2 (рекомендовано)
Абстракція над DOM - безпечна та SSR-friendly.
constructor(
private el: ElementRef,
private renderer: Renderer2
) {}
ngOnInit() {
this.renderer.setStyle(
this.el.nativeElement,
'background-color',
'yellow'
);
}- Працює з SSR
- Безпечний (XSS)
- Кросплатформений
- @HostBinding Біндінг до властивостей host-елемента.
@HostBinding('class.active') isActive = true;- Чисто
- Без прямого DOM-доступу
- @HostListener
Підписка на події host-елемента.
@HostListener('mouseenter')
onHover() {
this.isActive = true;
}-
Уникати nativeElement напряму
-
Використовувати Renderer2
-
Для стилів і класів -
@HostBinding -
Для подій -
@HostListener -
Для реактивності - signals + effect
-
Перевіряти платформу (isPlatformBrowser) при роботі з DOM API
Коротко
У Angular взаємодія з DOM у директивах має відбуватись через Renderer2 або host-декоратори, а не через прямий доступ до nativeElement.
54. Коли слід використовувати Renderer2 і які його переваги?
Коли слід використовувати Renderer2 і які його переваги
Використовуйте Renderer2, коли потрібно:
- Динамічно змінювати стилі, класи, атрибути
- Додавати або видаляти DOM-елементи
- Працювати з подіями зсередини директив
- Забезпечити SSR-сумісність
- Уникнути XSS-ризиків
- Писати кросплатформений код (browser / server / web workers)
import { Directive, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
constructor(
private el: ElementRef,
private renderer: Renderer2
) {
this.renderer.setStyle(
this.el.nativeElement,
'background-color',
'yellow'
);
}
}- Безпека
- Захищає від XSS
- Не дозволяє небезпечні DOM-операції напряму
- SSR-friendly
- Працює коректно при Server-Side Rendering
- Не ламається через відсутність window / document
- Абстракція над DOM
- Angular вирішує як саме застосувати зміни
- Підтримує різні платформи
- Краща підтримка Angular lifecycle
- Інтегрується з change detection
- Менше побічних ефектів
- Для простого керування класами → краще
ngClass - Для обробки подій → краще
@HostListener - Для стилів →
@HostBinding - Для читання значень (read-only) → допустимо
ElementRef
- Не використовувати nativeElement напряму
- Renderer2 - стандарт для директив
- Поєднувати з signals для реактивності
- Перевіряти платформу при складних DOM-операціях
Коротко
Renderer2 - це безпечний, SSR-сумісний та кросплатформений спосіб взаємодії з DOM, який слід використовувати замість прямого доступу до nativeElement.
55. Як створити власний канал в Angular?
Як створити власний Pipe (канал) в Angular
- Створення pipe
Приклад: простий pipe для форматування імені
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'capitalize',
standalone: true,
})
export class CapitalizePipe implements PipeTransform {
transform(value: string): string {
if (!value) return '';
return value[0].toUpperCase() + value.slice(1);
}
}- Використання в шаблоні
<p>{{ 'angular' | capitalize }}</p>- Pure vs Impure pipes
Pure pipe (за замовчуванням)
-
Викликається лише при зміні input
-
Краща продуктивність
@Pipe({ name: 'myPipe', pure: true })Impure pipe
-
Викликається на кожен change detection
-
Використовувати обережно
@Pipe({ name: 'myPipe', pure: false })- Pipe з параметрами
@Pipe({ name: 'truncate', standalone: true })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 10): string {
return value.length > limit ? value.slice(0, limit) + '…' : value;
}
}<p>{{ text | truncate:20 }}</p>- Best practices (Angular 20+)
-
Використовуйте standalone pipes
-
Тримайте pipes pure
-
Без side-effects
-
Не використовуйте pipes для складної бізнес-логіки
-
Для реактивних сценаріїв - signals або computed values
Коротко
Pipe в Angular створюється через @Pipe, реалізує PipeTransform і використовується для декларативної трансформації даних у шаблоні.
56. Опишіть чисті та нечисті канали.
Чисті (Pure) та нечисті (Impure) канали в Angular
- Викликаються лише тоді, коли змінюється посилання на input
- Значення кешується Angular
- Висока продуктивність
- Повністю детерміновані (без side-effects)
@Pipe({
name: 'uppercase',
standalone: true,
pure: true, // default
})
export class UppercasePipe {}<p>{{ name | uppercase }}</p>-
Форматування рядків
-
Обчислення на основі immutable-даних
-
99% кейсів
- Викликаються на кожен цикл change detection
- Не кешуються
- Значно гірша продуктивність
- Можуть мати side-effects (небажано)
Копіювати код
@Pipe({
name: 'timeAgo',
standalone: true,
pure: false,
})
export class TimeAgoPipe {}<p>{{ timestamp | timeAgo }}</p>-
Завжди починайте з pure pipe
-
Уникайте impure pipes
-
Для реактивних сценаріїв:
-
Signals (
computed) -
RxJS +
async
-
-
Impure pipe - останній варіант
Коротко
Pure pipes - швидкі та безпечні, Impure pipes - повільні й використовуються лише у виняткових випадках.
57. Що таке асинхронний канал і як він використовується?
Асинхронний канал (AsyncPipe) в Angular
async - це вбудований Angular pipe, який:
- підписується на
ObservableабоPromise - автоматично оновлює шаблон при нових значеннях
- сам відписується при знищенні компонента
users$ = this.userService.getUsers();<ul>
<li *ngFor="let user of users$ | async">
{{ user.name }}
</li>
</ul>dataPromise = fetchData();<p>{{ dataPromise | async }}</p>- Підписується на джерело даних
- Тригерить change detection при нових значеннях
- Відписується автоматично при destroy компонента
Усунення memory leaks без ngOnDestroy
- Менше коду
- Немає ручних subscribe / unsubscribe
- Краще читається шаблон
- Ідеально працює з OnPush
- SSR та signals-friendly
import { toSignal } from
'@angular/core/rxjs-interop';
users = toSignal(this.userService.getUsers()); html Копіювати код<li *ngFor="let user of users()">- Для роботи з HTTP-запитами
- Для стрімів даних (RxJS)
- Для реактивних UI-станів
- Якщо значення потрібне лише в TS, а не в шаблоні
- Для складної бізнес-логіки (краще в сервісах)
Коротко
async pipe - стандартний і безпечний спосіб роботи з асинхронними даними в шаблонах Angular без memory leaks.
58. Що таке NgRx і як він допомагає в управлінні станом?
Що таке NgRx і як він допомагає в управлінні станом
NgRx - це бібліотека для state management в Angular, побудована на:
- патерні Redux
- RxJS
- односпрямованому потоці даних (unidirectional data flow)
NgRx забезпечує єдине джерело правди (single source of truth) для стану застосунку.
- Store
Глобальний immutable-стан застосунку.
export interface AppState {
users: User[];
}- Actions
Описують що сталося.
export const loadUsers = createAction('[Users] Load');- Reducers
Описують як змінюється стан.
export const usersReducer = createReducer( [],
on(loadUsersSuccess, (\_, { users }) => users) );- Selectors
Ефективне отримання даних зі store.
export const selectUsers = createSelector( selectUsersState,
users => users );- Effects
Робота з side-effects (HTTP, storage, navigation).
loadUsers$ = createEffect(() => this.actions$.pipe(
ofType(loadUsers), switchMap(() => this.api.getUsers()) ) );- Централізує стан
- Робить змінюваність передбачуваною
- Полегшує debugging (Redux DevTools)
- Спрощує тестування
- Добре масштабується для великих команд
- Великі застосунки
- Складний shared-state
- Багато асинхронних side-effects
- Потрібен time-travel debugging
- Маленькі застосунки
- Простий локальний стан
- Overhead без потреби
NgRx не замінює signals
-
Часто комбінується:
- NgRx Store → глобальний стан
- Signals → локальний UI-стан
-
Для простіших кейсів:
- Signals
- Component Store
- Services + RxJS
Коротко
NgRx - це потужний, але важкий інструмент для управління станом, який варто використовувати лише тоді, коли складність застосунку цього вимагає.
59. Поясніть концепції дій, редукторів та ефектів у NgRx.
Дії, редуктори та ефекти в NgRx
- Actions (Дії)
Actions описують що сталося в застосунку.
Це прості обʼєкти з обовʼязковим полем type.
export const loadUsers = createAction('[Users] Load');
export const loadUsersSuccess = createAction(
'[Users] Load Success',
props<{ users: User[] }>()
);- Тригерять зміну стану
- Сигналізують про події (UI, API, router)
- Reducers (Редуктори)
Reducers - чисті функції, які визначають як змінюється стан у відповідь на action.
export const usersReducer = createReducer( initialState,
on(loadUsersSuccess, (state, { users }) => ({ ...state, users, })) );- Без side-effects
- Без мутацій
- Повертають новий immutable-стан
- Effects (Ефекти)
Effects обробляють side-effects (HTTP, storage, navigation) і працюють поза reducer.
loadUsers$ = createEffect(() => this.actions$.pipe(
ofType(loadUsers), switchMap(() => this.api.getUsers().pipe( map(users =>
loadUsersSuccess({ users })) ) ) ) );- Асинхронні операції
- Взаємодія з зовнішніми API
- Dispatch нових actions
- Reducers - максимально прості
- Вся асинхронність - тільки в Effects
- Signals можна використовувати поверх select
- Для локального стану - ComponentStore
Коротко
Actions описують події, Reducers - змінюють стан, Effects - обробляють побічні ефекти, разом утворюючи передбачуваний односпрямований потік даних.
60. Як би ви зберігали стан програми після оновлення сторінки?
Основний підхід: Persist + Rehydrate
localStorage- довготривалий станsessionStorage- лише на сесіюIndexedDB- великі обʼєми даних
Persist + Rehydrate через ngrx-store-localstorage
import { localStorageSync } from 'ngrx-store-localstorage';
export const metaReducer: MetaReducer = reducer =>
localStorageSync({
keys: ['auth', 'settings'],
rehydrate: true,
})(reducer);- Автоматичне відновлення
- Контроль, які slice зберігати
- Production-ready
const savedState = JSON.parse(localStorage.getItem('appState') || '{}');StoreModule.forRoot(reducers, {
initialState: savedState,
});@Injectable({ providedIn: 'root' })
export class AppStateService {
theme = signal(
localStorage.getItem('theme') ?? 'light'
);
constructor() {
effect(() => {
localStorage.setItem('theme', this.theme());
});
}
}- Мінімальний оверхед
- Підходить для невеликого стану
Зберігати
- Auth / refresh tokens
- User preferences
- Filters, feature state
Не зберігати
- Derived / computed state
- Тимчасовий UI-стан
- Великі API-колекції
- Persist тільки потрібні slices
- Не зберігати sensitive data без шифрування
- Для SSR - використовувати isPlatformBrowser
- Signals - для локального стану
- NgRx - для глобального
Коротко
Я зберігаю стан через persisting у browser storage та rehydration при старті, використовуючи NgRx meta-reducers або signals + services залежно від складності застосунку.
61. Чи можете ви обговорити концепцію незмінності в управлінні станами?
Концепція незмінності (Immutability) в управлінні станами
Незмінність означає, що стан не змінюється напряму.
Кожна зміна створює новий обʼєкт стану, а старий залишається незмінним.
// Мутація
state.count++;
// Незмінно
{ ...state, count: state.count + 1 }- Передбачуваність
- Один action → один новий стан
- Без прихованих побічних ефектів
- Продуктивність
- Angular використовує reference checks
- Ефективна робота з OnPush та signals
- Debugging
- Time-travel debugging (NgRx DevTools)
- Легко відстежувати зміни стану
- Тестування
- Reducers - чисті функції
- Просте unit-тестування
on(updateUser, (state, { user }) => ({
...state,
user: { ...state.user, ...user }
}))Заборонено:
state.user.name = user.name;- Signals реагують на зміну посилань
- computed() перевиконується лише при новому reference
- Immutability = краща продуктивність UI
- Мутація вкладених обʼєктів
- Використання .push() / .splice()
- Зміни state поза reducer / сервісом
Коротко
Незмінність - основа надійного state management: вона забезпечує передбачуваність, продуктивність і зручне тестування.
62. Як тестувати компоненти Angular?
Основні типи тестування
- Unit-тести (основні)
Тестують ізольовану логіку компонента.
Інструменти:
TestBed- Jasmine / Jest
- Karma (або Vite + Vitest)
beforeEach(() => {
TestBed.configureTestingModule({
imports: [MyComponent], // standalone
});
});
it('should create component', () => {
const fixture = TestBed.createComponent(MyComponent);
expect(fixture.componentInstance).toBeTruthy();
});- Тестування шаблону (DOM)
Перевірка рендерингу та binding’ів.
it('should render title', () => {
const fixture = TestBed.createComponent(MyComponent);
fixture.componentInstance.title = 'Hello';
fixture.detectChanges();
const el = fixture.nativeElement.querySelector('h1');
expect(el.textContent).toContain('Hello');
});- Тестування взаємодії (events)
it('should emit event on click', () => {
spyOn(component.saved, 'emit');
const button = fixture.nativeElement.querySelector('button');
button.click();
expect(component.saved.emit).toHaveBeenCalled();
});Mock сервісів
providers: [
{ provide: UserService, useValue: mockUserService }
]HttpClient
import { provideHttpClientTesting } from '@angular/common/http/testing';
providers: [provideHttpClientTesting()]it('should update signal value', () => {
component.count.set(1);
expect(component.count()).toBe(1);
});Тестувати
- Бізнес-логіку компонента
- Взаємодію з сервісами
- Binding’и та events
Не тестувати
- Внутрішню реалізацію Angular
- CSS / стилі
- Простий boilerplate
Best practices
- Використовуйте standalone components у тестах
- Мінімізуйте TestBed конфіг
- Mock тільки зовнішні залежності
- Один тест - одна відповідальність
- Для UI-флоу - e2e (Cypress / Playwright)
Коротко
Компоненти Angular тестуються переважно unit-тестами через TestBed, з моками залежностей та перевіркою DOM і взаємодій; у Angular 20+ тести простіші завдяки standalone та signals.
63. Поясніть, що таке TestBed та його роль у тестуванні Angular.
TestBed - це основний тестовий API Angular, який:
- створює ізольоване тестове середовище
- емулює Angular DI, change detection та lifecycle
- дозволяє конфігурувати залежності так само, як у реальному застосунку
- Конфігурація тестового модуля
TestBed.configureTestingModule({
imports: [MyComponent], // standalone
providers: [MyService],
});- Створення компонента
const fixture = TestBed.createComponent(MyComponent);
const component = fixture.componentInstance;- Керування change detection
fixture.detectChanges();- Доступ до DI
const service = TestBed.inject(MyService);- Підтримує standalone components
- Не потребує NgModule
- Працює з signals
- Сумісний з SSR і zoneless режимом
- Менше boilerplate, ніж у старих версіях
Використовувати
- Для тестування компонентів
- Для інтеграційних unit-тестів
- Коли потрібні DI та lifecycle
Не обовʼязково
- Для простих pure-функцій
- Для логіки без Angular-залежностей
- Мінімізуйте конфігурацію TestBed
- Імпортуйте лише тестований standalone-компонент
- Mock зовнішні залежності
- Не тестуйте Angular internals
Коротко
TestBed - це тестовий інструмент Angular, який створює середовище, максимально наближене до реального застосунку, і є основою тестування компонентів та сервісів.
64. Як ви створюєте імітаційний імітаційний код сервісу Angular для цілей тестування?
- Ізолювати тест від реальних залежностей (HTTP, storage, API)
- Зробити тести швидкими та детермінованими
- Тестувати поведінку, а не реалізацію
- Простий mock-обʼєкт (найчастіше)
const userServiceMock = {
getUsers: () => of([{ id: 1, name: 'Test User' }]),
};TestBed.configureTestingModule({
imports: [MyComponent],
providers: [
{ provide: UserService, useValue: userServiceMock },
],
});- Просто
- Швидко
- Ідеально для unit-тестів
- Mock-клас
class UserServiceMock {
getUsers() {
return of([]);
}
}providers: [
{ provide: UserService, useClass: UserServiceMock }
]- Краще для складної логіки
- Більше boilerplate
- Spy-обʼєкти (Jasmine / Jest)
Jasmine
const userServiceSpy = jasmine.createSpyObj('UserService', ['getUsers']);
userServiceSpy.getUsers.and.returnValue(of([]));Jest
const userServiceMock = {
getUsers: jest.fn().mockReturnValue(of([])),
};- Перевірка викликів
- Контроль поведінки
- HttpClient mock (для API)
import { provideHttpClientTesting } from '@angular/common/http/testing';
TestBed.configureTestingModule({
providers: [provideHttpClientTesting()],
});httpMock.expectOne('/api/users').flush([]);- Mock через useValue - default вибір
- Не використовуйте реальні HTTP-запити
- Mock тільки зовнішні залежності
- Один mock = один тестовий сценарій
- Для signals - просто викликайте .set()
- Мокати приватні методи
- Тестувати реалізацію замість поведінки
- Підміняти весь Store замість slice
Коротко
Mock-сервіси в Angular створюються через useValue, useClass або spy-обʼєкти і дозволяють ізольовано та надійно тестувати компоненти й сервіси.
65. Чи можна виконувати наскрізне тестування в Angular?
Так, Angular повністю підтримує E2E (end-to-end) тестування, але не має вбудованого інструмента - використовується зовнішній тест-раннер.
Playwright (рекомендовано)
- Сучасний стандарт
- Швидкий і стабільний
- Працює з SSR та SPA
- Підтримує multiple browsers
npm init playwright@latesttest('login flow', async ({ page }) => {
await page.goto('/');
await page.fill('#email', 'test@mail.com');
await page.click('button[type=submit]');
await expect(page).toHaveURL('/dashboard');
});- Простий у використанні
- Чудовий dev experience
- Менш стабільний для складних SSR-сценаріїв
cy.visit('/');
cy.get('input').type('test@mail.com');
cy.contains('Submit').click();- Deprecated
- Не рекомендується у сучасних Angular-проєктах
- Реальні користувацькі флоу
- Навігацію
- Інтеграцію з бекендом
- Авторизацію
- SSR + hydration (якщо є)
- Мінімізувати кількість E2E-тестів
- Не тестувати дрібну логіку (для цього unit)
- Використовувати test IDs (data-testid)
- Мокати бекенд або використовувати test environment
- Запускати E2E у CI
- E2E працює з standalone та signals без проблем
- SSR тестується через Playwright
- Zoneless режим не впливає на E2E
Коротко
Angular не має власного E2E-фреймворку, але відмінно працює з Playwright і Cypress для повноцінного наскрізного тестування.
66. Які відмінності між Jasmine та Karma в контексті тестування Angular?
- Jasmine - це фреймворк для написання тестів
- Karma - це тест-раннер, який запускає ці тести в браузерах
Вони доповнюють, а не замінюють одне одного.
Що це
BDD-тестовий фреймворк, який надає:
describe,it,beforeEachexpect, matchers- spies (
spyOn)
describe('Counter', () => {
it('should increment', () => {
expect(1 + 1).toBe(2);
});
});Відповідає за
- Синтаксис тестів
- Assertions
- Mock / Spy логіку
Що це
Test runner, який:
- Запускає тести
- Відкриває браузери (Chrome, Firefox, Headless)
- Відслідковує файли та перезапускає тести
- Репортує результати
ng test- Середовище виконання
- Інтеграцію з браузером
- CI-запуск
- Jasmine + Karma - legacy default
- Все частіше замінюються на:
- Jest
- Vitest (Vite)
- Karma повільніший, але стабільний
- Jasmine простий, але менш гнучкий за Jest
- Для нових проєктів:
- Vitest / Jest
- Для legacy Angular:
- Jasmine + Karma - ок
- Не змішувати відповідальності інструментів
Коротко
Jasmine пише тести, Karma запускає тести, разом вони утворюють класичний стек тестування Angular.
67. Які стратегії ви б використали для зменшення часу завантаження програми Angular?
- Lazy Loading (критично важливо)
Lazy routes
{
path: 'admin',
loadComponent: () =>
import('./admin/admin.component').then(m => m.AdminComponent),
}- Менший initial bundle
- Швидший старт
- Standalone + Tree Shaking
- Використовувати standalone components
- Імпортувати тільки необхідні залежності
@Component({ standalone: true, imports: [CommonModule], })- Change Detection Optimization
- ChangeDetectionStrategy.OnPush
- Signals замість зайвого RxJS
changeDetection: ChangeDetectionStrategy.OnPush- SSR + Hydration (якщо є SEO)
- SSR для швидкого First Contentful Paint
- Hydration для уникнення повторного рендеру
provideClientHydration()- Code Splitting & Dynamic Imports
- Динамічно завантажувати важкі бібліотеки
const chart = await import('chart.js');- Оптимізація Assets
- Lazy loading зображень
<img src="img.png" loading="lazy" />- Стиснення (gzip / brotli)
- Мінімізація шрифтів
- Zoneless Angular (Angular 20+)
provideExperimentalZonelessChangeDetection()- Менше runtime overhead
- Потрібна дисципліна в реактивності
- Preloading стратегія
withPreloading(PreloadAllModules)Завантажує lazy-модулі після старту
- Видалення зайвого
- Не імпортувати CommonModule без потреби
- Не використовувати impure pipes
- Мінімізувати глобальні стилі
Коротко
Найбільший ефект дають lazy loading, standalone + OnPush, SSR з hydration та code splitting; дрібні оптимізації мають сенс лише після цього.
68. Поясніть ліниве завантаження та як воно покращує продуктивність програми.
Lazy loading - це підхід, за якого частини застосунку завантажуються лише тоді, коли вони реально потрібні, а не під час старту.
{
path: 'dashboard',
loadComponent: () =>
import('./dashboard/dashboard.component')
.then(m => m.DashboardComponent),
}- Менший initial bundle
- Менше JS на старті
- Швидший First Load
- Швидший Time to Interactive (TTI)
- Браузер швидше стає інтерактивним
- Краще використання мережі
- Код завантажується on demand
- Менше непотрібних запитів
Preloading
Після старту застосунку Angular може підвантажити lazy-модулі у фоні.
withPreloading(PreloadAllModules)- Lazy load feature areas, а не дрібні компоненти
- Не lazy load critical UI
- Поєднувати з OnPush
- Використовувати standalone components
- Lazy load важкі сторонні бібліотеки
- Lazy loading кожного компонента
- Lazy loading root layout
- Відсутність fallback UI (loading)
Коротко
Lazy loading зменшує розмір стартового бандлу, пришвидшує завантаження та покращує UX, завантажуючи код лише тоді, коли він потрібен.
69. Як би ви реалізували розділення коду в Angular для покращення продуктивності?
Code splitting - це розбиття JavaScript-коду на окремі чанки, які завантажуються за потреби, а не всі одразу при старті.
- Lazy loading маршрутів (основний інструмент)
Standalone components
{
path: 'profile',
loadComponent: () =>
import('./profile/profile.component')
.then(m => m.ProfileComponent),
}- Менший initial bundle
- Швидший старт застосунку
- Lazy loading feature areas
Розділяйте застосунок по фічах, а не по дрібних компонентах.
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES),
}- Динамічні імпорти для важких бібліотек
async loadChart() {
const { Chart } = await import('chart.js');
new Chart(...);
}Бібліотека не потрапляє в initial bundle
- Preloading (баланс між швидкістю і UX)
provideRouter(
routes,
withPreloading(PreloadAllModules)
);- Lazy модулі завантажуються після старту
- Покращує UX без шкоди initial load
- Standalone + Tree Shaking
- Використовуйте standalone components
- Імпортуйте тільки необхідні залежності
@Component({
standalone: true,
imports: [CommonModule],
})- Lazy load pages / feature areas
- Не lazy load critical UI
- Не дробіть код надто дрібно
- Поєднувати з OnPush та signals
- Вимірювати ефект через bundle analyzer
- Lazy loading кожного компонента
- Lazy loading layout/root
- Відсутність loading state
Коротко
Code splitting в Angular реалізується переважно через lazy loading маршрутів і dynamic imports, що суттєво зменшує initial bundle і покращує продуктивність.
70. Обговоріть використання опції trackBy в *ngFor для покращення продуктивності.
trackBy дозволяє Angular ідентифікувати елементи списку за унікальним
ключем, а не за позицією в масиві.
Без trackBy Angular:
- вважає, що всі елементи нові
- перевидаляє та перерендерює весь DOM-список
З trackBy Angular:
- оновлює тільки змінені елементи
- зберігає існуючі DOM-ноди
<li *ngFor="let user of users; trackBy: trackById">
{{ user.name }}
</li>trackById(index: number, user: User): number {
return user.id;
}this.users = [...this.users]; // новий reference- Без trackBy → весь список перерендериться
- З trackBy → DOM залишиться стабільним
- Великі списки
- Часті оновлення масиву
- Реактивні дані (signals / RxJS)
- OnPush change detection
- Анімації в списках
Добре
- id
- унікальний UUID
- стабільний primary key
Погано
- index
- випадкові значення
- значення, що можуть змінюватись
- Signals часто створюють нові references
- trackBy + immutability = максимальна ефективність
- Особливо важливо для zoneless Angular
- Не використовувати trackBy взагалі
- Використовувати index
- Повертати обʼєкт замість примітива
Коротко
trackBy дозволяє Angular оновлювати лише змінені елементи списку, значно зменшуючи кількість DOM-операцій і покращуючи продуктивність.
71. Як додати підтримку кількох мов у застосунок Angular?
Основні підходи
Підходить для SEO, SSR, статичних мов.
Кроки:
- Позначити тексти в шаблоні:
<h1 i18n="@@title">Hello</h1>- Згенерувати файли перекладів:
ng extract-i18n-
Додати
messages.xx.xlf(en, uk, pl, тощо) -
Зібрати для кожної мови:
ng build --localizeПлюси:
- Максимальна продуктивність
- SEO-friendly
- Без runtime overhead
Мінуси:
- Немає runtime-перемикання мови
- Окремий build на кожну мову
Підходить для динамічного перемикання мови.
npm install @ngx-translate/core @ngx-translate/http-loadertranslate.use('uk');<h1>{{ 'TITLE' | translate }}</h1>Плюси:
- Runtime switch
- Один build
- Зручно для SPA
Мінуси:
- Гірше для SEO без SSR
- Runtime overhead
language = signal<'en' | 'uk'>('en');
translations = { en: { title: 'Hello' }, uk: { title: 'Привіт' }, };
title = computed(() => translations[this.language()].title);<h1>{{ title() }}</h1>Плюси:
- Мінімальний оверхед
- Повний контроль
- Ідеально для UI-стану
Мінуси:
- Не підходить для великих словників
- Не змішувати підходи
- Ключі перекладів мають бути стабільні
- Lazy-load translation файлів
- Для SSR - server-side locale
Коротко
У Angular багатомовність реалізується через Angular i18n (compile-time) або ngx-translate / signals (runtime) - вибір залежить від вимог до SEO та перемикання мов.
72. Опишіть процес реалізації локалізації Angular.
- Увімкнення i18n Angular i18n підтримується з коробки - додаткових бібліотек не потрібно.
ng add @angular/localize- Позначення текстів у шаблонах
<h1 i18n="@@title">Hello</h1>
<p i18n>Welcome to our application</p>i18n- маркер для перекладу@@title- стабільний ключ (рекомендовано)
- Експорт текстів для перекладу
ng extract-i18nГенерується файл messages.xlf (або .json, .arb)
- Створення файлів перекладу
messages.en.xlf
messages.uk.xlf
messages.pl.xlf
Кожен файл містить переклад для конкретної мови.
- Налаштування angular.json
"i18n": {
"sourceLocale": "en",
"locales": {
"uk": "src/locale/messages.uk.xlf",
"pl": "src/locale/messages.pl.xlf"
}
}- Збірка для різних мов
ng build --localizeAngular створює окремий build для кожної мови.
- Деплой та routing
- Кожна мова має власний bundle
- Часто використовується префікс у URL: /en, /uk, /pl
- Для SSR - мова визначається на сервері
- i18n працює зі standalone components
- Повністю сумісний з SSR + hydration
- Немає runtime overhead
- Максимальна продуктивність і SEO
- Немає runtime-перемикання мови
- Окремий build для кожної мови
Для runtime-перемикання використовують ngx-translate
Коротко
Локалізація в Angular реалізується через позначення текстів → експорт → переклад → multi-build, забезпечуючи високу продуктивність і SEO-сумісність.
73. Які найкращі практики безпеки для Angular-застосунків?
- Захист від XSS (Cross-Site Scripting)
- Angular автоматично екранує дані в шаблонах
- Не використовуйте
innerHTMLбез потреби - Якщо потрібно - лише через
DomSanitizer(обережно)
this.safeHtml = sanitizer.bypassSecurityTrustHtml(html);- Уникати небезпечних API
<div [innerHTML]="html"></div><div>{{ text }}</div>- HTTP безпека
- Використовуйте HTTPS
- Додавайте HTTP Interceptors для:
- Authorization headers
- CSRF-токенів
- Centralized error handling
provideHttpClient(withInterceptors([authInterceptor]));- Захист від CSRF
- Використовуйте CSRF-токени (на боці бекенду)
- Angular автоматично підтримує XSRF через cookies
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN',
});- Безпечне зберігання даних
Не зберігати sensitive data у localStorage
Краще:
- HttpOnly cookies
- Short-lived tokens
- Мінімальний client-side state
- Route Guards і доступ
Захищайте приватні маршрути
canActivate: [AuthGuard]Не довіряйте лише frontend - бекенд обовʼязковий
- Dependency Security
Регулярно оновлюйте Angular та бібліотеки
Використовуйте:
npm audit- Build та Runtime безпека
Production build:
ng build --configuration production- Увімкнений AOT
- Видалений debug-код
- Без eval, Function, dynamic scripts
- Angular 20+ рекомендації
- Standalone components → менша attack surface
- Signals → менше небезпечних side-effects
- Zoneless → менше глобальних патчів
- SSR + hydration → безпечний initial render
Коротко
Безпека в Angular базується на вбудованому захисті від XSS, правильній роботі з HTTP, обмеженні доступу, безпечному зберіганні даних і регулярному оновленні залежностей.
74. Як запобігти міжсайтовому скриптингу (XSS) у застосунках Angular?
XSS - це атака, за якої зловмисник інʼєктує шкідливий JavaScript у сторінку, який виконується в браузері користувача.
- Вбудований захист Angular (основа)
Angular автоматично екранує всі дані, що рендеряться в шаблонах.
<!-- Безпечно -->
<div>{{ userInput }}</div>HTML і JS не виконуються, а екрануються.
- Уникати небезпечних bindingʼів
Небезпечно
<div [innerHTML]="html"></div>Безпечно
<div>{{ text }}</div>- DomSanitizer - тільки за необхідності
Використовувати лише якщо довіряєте джерелу.
this.safeHtml =
sanitizer.bypassSecurityTrustHtml(trustedHtml);bypassSecurityTrust* відключає захист Angular
- Не виконувати динамічний код
eval(userInput);
new Function(userInput);Погано
<a [href]="userInput">Link</a>Добре
<a [attr.href]="safeUrl">Link</a>- Безпечна робота з URL
Angular автоматично блокує:
- javascript:
- data: (у багатьох контекстах)
<img [src]="imageUrl" />Angular перевіряє контекст (URL, HTML, style)
- HTTP + Backend захист
- Завжди валідувати та очищати дані на бекенді
- Використовувати Content Security Policy (CSP)
- Не довіряти client-side валідації
- Angular 20+ best practices
- Не використовувати innerHTML без потреби
- Не зберігати HTML у state
- Використовувати standalone components
- Мінімізувати прямий DOM-доступ
- Signals + template binding → безпечніше
- Використання bypassSecurityTrustHtml без розуміння
- Рендеринг HTML з API
- Зберігання user-generated HTML
Коротко
Angular за замовчуванням захищає від XSS, але розробник може сам створити вразливість, використовуючи innerHTML, eval або DomSanitizer без потреби.
75. Чи можна виконувати автентифікацію та авторизацію в застосунках Angular?
Так. Angular повністю підтримує обидві концепції, але:
- автентифікація зазвичай реалізується спільно з бекендом (JWT, OAuth)
- авторизація - переважно на фронтенді через guards і policy-логіку
- Автентифікація (Authentication)
Це, перевірка хто користувач (login).
- Login → бекенд
- Отримання JWT / session
- Збереження токена (HttpOnly cookie або memory)
login(credentials) {
return this.http.post('/api/login', credentials);
}Через HTTP Interceptor:
export const authInterceptor = (req, next) => {
const token = authService.token();
return next(
req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
})
);
};- Авторизація (Authorization)
Це, перевірка що користувач може робити.
export const authGuard: CanActivateFn = () => {
return authService.isLoggedIn();
};{
path: 'admin',
canActivate: [authGuard],
loadComponent: () => import('./admin.component'),
}- Ролі та права доступу
export const roleGuard: CanActivateFn = () => {
return authService.hasRole('admin');
};{ path: 'admin', canActivate: [roleGuard] }- UI-рівень авторизації
<button *ngIf="isAdmin()">Delete</button>isAdmin = computed(() => user()?.role === 'admin');- Angular 20+ best practices
- Не довіряти лише frontend-перевіркам
- Backend завжди має фінальне слово
- Guards - для routing
- Interceptors - для токенів
- Signals - для auth state
- Не зберігати токени в localStorage (краще HttpOnly cookies)
Коротко
Angular дозволяє повноцінно реалізувати автентифікацію через бекенд і авторизацію через guards та UI-логіку, дотримуючись чіткої відповідальності між frontend і backend.
76. Чим TypeScript відрізняється від JavaScript і чому він є кращим в Angular?
- Динамічно типізований
- Типи перевіряються під час виконання
- Гнучкий, але схильний до runtime-помилок
function sum(a, b) {
return a + b;
}
sum(1, '2'); // "12" - помилка логікиTypeScript = JavaScript + типи
- Статична типізація (на етапі компіляції)
- Раннє виявлення помилок
- Краща читабельність і підтримуваність
function sum(a: number, b: number): number {
return a + b;
}
// sum(1, '2'); ❌ compile-time error- Архітектура та масштабування
Angular - enterprise-фреймворк, TypeScript:
- робить код передбачуваним
- зручний для великих команд
- Dependency Injection
Типи дозволяють Angular DI працювати надійно.
constructor(private userService: UserService) {}- Декоратори та метадані
Angular активно використовує декоратори, які:
- неможливі в чистому JS у такому вигляді
- добре типізуються в TS
- Tooling та DX
- Autocomplete
- Навігація по коду
- Safe refactoring
- Strict mode (strict: true)
- Angular 20+ контекст
- Signals
- Standalone components
- Typed forms
- Typed HttpClient
Усе це максимально виграє від TypeScript
Коротко
TypeScript зменшує кількість помилок, покращує підтримуваність і масштабування коду, тому є природним і обґрунтованим вибором для Angular.
77. Які переваги використання інтерфейсів TypeScript у застосунках Angular?
Інтерфейси описують форму обʼєктів (structure typing) і використовуються лише на етапі компіляції.
interface User {
id: number;
name: string;
email: string;
}- Статична типізація
- Помилки виявляються до runtime
- Менше багів у production
function printUser(user: User) {
console.log(user.name);
}- Контракти між шарами
Чіткі API між:
- компонентами
- сервісами
- бекендом
getUser(): Observable<User> {}- Краща читабельність і підтримка
- Код самодокументований
- Легше онбордити нових розробників
- IDE та DX
- Autocomplete
- Safe refactoring
- Навігація по властивостях
- Інтеграція з Angular API
HttpClient
this.http.get<User[]>('/api/users');Forms
form: FormGroup<UserForm>;NgRx
interface AppState {
users: User[];
}- Інтерфейси - для data models
- Не використовувати для runtime-логіки
- Тримати окремо (models/, types/)
- Використовувати разом зі strict mode
Коротко
Інтерфейси TypeScript роблять Angular-код типобезпечним, передбачуваним і масштабованим, що критично важливо для великих і довготривалих проєктів.
78. Чи можете ви пояснити використання декораторів у TypeScript, наводячи приклад в Angular?
Декоратори - це спеціальні функції TypeScript, які додають метадані або змінюють поведінку класів, методів, властивостей або параметрів під час компіляції.
Angular активно використовує декоратори для опису структури застосунку.
- Class decorators
- Property decorators
- Method decorators
- Parameter decorators
- Class decorator -
@Component
import { Component } from '@angular/core';
@Component({
selector: 'app-user',
standalone: true,
template: `<h1>{{ name }}</h1>`,
})
export class UserComponent {
name = 'Angular';
}@Component додає метадані, за якими Angular розуміє:
- що це компонент
- як його рендерити
- як він інтегрується в DI та change detection
- Property decorator - @Input
@Input() title!: string;Дозволяє передавати дані в компонент ззовні
- Method decorator - @HostListener
@HostListener('click')
onClick() {
console.log('Clicked');
}Підписка на подію host-елемента
- Parameter decorator - @Inject
constructor(@Inject(API_URL) private apiUrl: string) {}Явна інʼєкція залежності через Angular DI
- Формують метадані фреймворку
- Дозволяють Angular працювати без reflection у runtime
- Забезпечують:
- Dependency Injection
- Template binding
- Change detection
- Роблять код декларативним і читабельним
- Декоратори залишаються актуальними
- Часто комбінуються з:
- inject() (замість constructor DI)
- Signals
- Standalone API не скасовує декоратори
Коротко
Декоратори в TypeScript дозволяють Angular описувати компоненти, сервіси та DI декларативно; вони є фундаментом архітектури Angular і тісно інтегровані з TypeScript.
79. Як RxJS доповнює застосунки Angular?
RxJS - це бібліотека для реактивного програмування, яка працює з:
- потоками даних (streams)
- асинхронними подіями
- операторами для трансформації та композиції
Angular використовує RxJS під капотом.
- HttpClient
Кожен HTTP-запит повертає Observable.
this.http.get<User[]>('/api/users')
.pipe(map(users => users.filter(u => u.active)));- Forms
Reactive Forms побудовані на RxJS.
this.form.valueChanges.subscribe(value => {
console.log(value);
});- Router
Router надає Observables для навігації.
this.router.events
.pipe(filter(e => e instanceof NavigationEnd))
.subscribe();- State Management
NgRx повністю базується на RxJS.
- Actions
- Effects
- Selectors
- Управління асинхронністю
- HTTP
- WebSockets
- User events
- Timers
- Комбінування потоків
combineLatest([
this.user$,
this.settings$,
]).subscribe();- Контроль життєвого циклу
this.stream$
.pipe(takeUntilDestroyed())
.subscribe();- RxJS все ще важливий
- Але:
- Signals - для локального UI-стану
- RxJS - для async streams та side-effects
- toSignal() / toObservable() для інтеграції
users = toSignal(this.users$);- Не підписуватись вручну в шаблонах → async pipe
- Не зловживати subscribe() в компонентах
- Виносити RxJS-логіку в сервіси
- Signals для синхронного стану
Коротко
RxJS є фундаментом асинхронності в Angular і доповнює його можливості для роботи з потоками даних, тоді як signals спрощують локальний UI-стан.
80. Поясніть призначення Subjects у RxJS та як вони використовуються в Angular.
Subject - це спеціальний тип Observable, який:
- є одночасно Observable і Observer
- дозволяє емітити значення вручну
- підтримує мультикаст (один еміс → багато підписників)
const subject = new Subject<number>();
subject.subscribe(v => console.log(v));
subject.next(1);- Subject
- Не зберігає значення
- Нові підписники не отримують попередні емісії
const s = new Subject<number>();- BehaviorSubject (найпопулярніший)
- Має початкове значення
- Новий підписник одразу отримує останнє значення
const user$ = new BehaviorSubject<User | null>(null);- ReplaySubject
Реплеїть N останніх значень
const logs$ = new ReplaySubject<string>(3);- AsyncSubject
- Віддає останнє значення після complete()
- Рідко використовується
Service як data source (поширений патерн)
@Injectable({ providedIn: 'root' })
export class AuthService {
private userSubject = new BehaviorSubject<User | null>(null);
user$ = this.userSubject.asObservable();
setUser(user: User) {
this.userSubject.next(user);
}
}<span *ngIf="auth.user$ | async as user">
{{ user.name }}
</span>- Subjects - для async streams, events, side-effects
- Signals - для локального синхронного UI-стану
Інтероп:
user = toSignal(this.user$);- Не експортуйте Subject напряму → використовуйте asObservable()
- Для state - BehaviorSubject
- Не зловживати Subjects для простого UI-стану
- Виносьте Subjects у сервіси
- В шаблонах - async pipe
- Використання Subject замість BehaviorSubject для state
- Ручні subscribe() без відписки
- Змішування state і events в одному Subject
Коротко
Subjects дозволяють вручну керувати потоками даних і мультикастити значення; в Angular їх застосовують переважно в сервісах для async-подій та shared-state, тоді як signals краще підходять для локального UI-стану.
81. Які поширені оператори RxJS та як їх використовувати в Angular?
- Оператори трансформації
map
Перетворює значення потоку.
this.http.get<User[]>('/api/users')
.pipe(map(users => users.filter(u => u.active)));switchMap
Перемикається на новий Observable, скасовуючи попередній (ідеально для HTTP + UI events).
this.search$
.pipe(
switchMap(term => this.api.search(term))
);mergeMap
Паралельні запити, без скасування попередніх.
mergeMap(id => this.api.loadById(id))concatMap
Черга запитів, виконує послідовно.
concatMap(task => this.api.run(task))- Оператори фільтрації
filter
filter(user => user.isAdmin)take, takeUntil
Обмеження кількості емісій / контроль lifecycle.
this.stream$
.pipe(takeUntilDestroyed())
.subscribe();- Комбінування потоків
combineLatest
combineLatest([this.user$, this.settings$])withLatestFrom
click$
.pipe(withLatestFrom(this.user$))- Error handling
catchError
catchError(() => of([]))- Utility-оператори
tap
Side-effects (логування, debug).
tap(value => console.log(value))debounceTime
Часто для input / search.
debounceTime(300)- HttpClient → map, switchMap, catchError
- Forms → valueChanges.pipe(debounceTime)
- Router → filter, map
- NgRx Effects → switchMap, mergeMap, concatMap
- RxJS - для асинхронних потоків
- Signals - для локального UI-стану
- У шаблонах - async pipe
- Мінімізувати ручні subscribe()
Коротко
RxJS-оператори дозволяють трансформувати, комбінувати та контролювати асинхронні потоки в Angular; найважливіші - map, switchMap, filter, combineLatest, catchError.
82. Які є найкращі практики для структурування великої програми Angular?
- Feature-based структура (ключова)
- Не за типами (
components/,services/) - За фічами (domains)
src/app/
├─ auth/
│ ├─ auth.routes.ts
│ ├─ auth.component.ts
│ └─ auth.service.ts
├─ dashboard/
├─ shared/
└─ core/Кожна фіча - ізольована та самодостатня
- Standalone-first підхід (Angular 20+)
- NgModules
- Standalone components, directives, pipes
@Component({
standalone: true,
imports: [],
})Краще tree-shaking і простіша архітектура
- Lazy Loading фіч
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES),
}- Менший initial bundle
- Краще масштабування
- Core vs Shared
core/
- Singleton сервіси
- Auth, interceptors, guards
- App-level providers
shared/
- UI components
- Pipes, directives
- Без бізнес-логіки
- Чіткий поділ відповідальностей
- Компоненти → UI + orchestration
- Сервіси → бізнес-логіка
- Store / signals → стан
Жодної складної логіки в шаблонах
- Управління станом
- Signals → локальний UI-стан
- NgRx / ComponentStore → глобальний або shared-стан
- Не зберігати derived state
- Узгоджені конвенції
- Naming conventions
- Folder structure
- Lint rules
- Strict TypeScript
"strict": true- Dependency Direction Rule
feature → shared → core
- core не залежить від feature
- shared не залежить від core
- Тестованість з архітектури
- Компоненти легко мокаються
- Логіка в сервісах → unit tests
- Мінімальний TestBed setup
- Масштабування команди
- Чіткі boundaries між фічами
- Lazy-loaded domains
- Мінімальні cross-feature залежності
- “God components”
- Глобальні shared services
- Barrel-файли з side-effects
- Відсутність lazy loading
Коротко
Великий Angular-застосунок має будуватись за фічами, з standalone + lazy loading, чітким поділом відповідальностей і контрольованим управлінням станом - це основа масштабованості та підтримуваності.
83. Як керувати глобальним станом у програмах Angular?
Глобальний стан - це дані, які:
- використовуються в багатьох фічах
- мають жити довше за окремий компонент
- повинні бути єдиним джерелом правди
Приклади: auth, user, settings, feature flags.
- NgRx Store (enterprise-рішення)
Коли використовувати
- Великий застосунок
- Складна бізнес-логіка
- Багато асинхронних процесів
- Потрібен time-travel debugging
store.dispatch(loadUser());
user$ = store.select(selectUser);Плюси
- Чітка архітектура
- Predictable state
- DevTools
Мінуси
- Великий boilerplate
- Overhead для малих проєктів
- NgRx ComponentStore (middle ground)
Коли використовувати
- Feature-level state
- Складний стан, але без глобального store
@ComponentStore()
export class ProfileStore extends ComponentStore<ProfileState> {}Плюси
- Менше boilerplate
- RxJS-first
- Добре масштабується
- Services + RxJS (класичний підхід)
@Injectable({ providedIn: 'root' })
export class AuthService {
private userSubject = new BehaviorSubject<User | null>(null);
user$ = this.userSubject.asObservable();
}Плюси
- Простота
- Швидко реалізувати
Мінуси
- Легко порушити архітектуру
- Складно масштабувати
- Signals (Angular 20+ - рекомендовано)
Для глобального UI / app-state
@Injectable({ providedIn: 'root' })
export class AppState {
user = signal<User | null>(null);
}Плюси
- Мінімальний boilerplate
- Висока продуктивність
- Ідеально для UI-стану
Мінуси
- Не для складних async flows
- Не зберігати derived state
- Immutability
- Чіткі boundaries
- Signals для UI
- NgRx тільки коли справді потрібно
Коротко
У Angular глобальний стан керується через Signals, Services або NgRx, і правильний вибір залежить від складності застосунку, а не від моди на інструмент.
84. Які є найкращі практики для зв'язку компонентів у великих додатках Angular?
- Parent → Child (Input / Signals)
- Ієрархічний звʼязок
- Дані зверху вниз
@Input() user!: User;Angular 20+
Для реактивності - signals
@Input() user = signal<User | null>(null);- Простий і прозорий звʼязок
- Не для далеких компонентів
- Child → Parent (Output / Events)
Події знизу вгору
@Output() saved = new EventEmitter<User>();<app-form (saved)="onSave($event)" />- Чітка подієва модель
- Не масштабувати на багато рівнів
- Shared Service (рекомендовано для sibling / distant)
- Компоненти не повʼязані ієрархічно
- Потрібен shared state або events
@Injectable({ providedIn: 'root' })
export class UiStateService {
sidebarOpen = signal(false);
}this.ui.sidebarOpen.set(true);- Слабке звʼязування
- Добре масштабується
- RxJS Subjects (для events / async)
- Event bus
- Асинхронні події
private refresh$ = new Subject<void>();
refresh = this.refresh$.asObservable();- Не використовувати як global state
- Завжди
asObservable()
- Global State (NgRx / Signals)
- Дані потрібні в багатьох фічах
- Довготривалий стан (auth, user)
user = this.store.select(selectUser);або
@Injectable({ providedIn: 'root' })
export class AppState {
user = signal<User | null>(null);
}- Чого НЕ робити (anti-patterns)
- Передача через багато рівнів (prop drilling)
- Виклик методів іншого компонента
- Глобальні mutable сервіси
- Shared state без чітких boundaries
- Signals - default choice для UI-стану
- RxJS - для async / streams
- NgRx - тільки для складного глобального стану
- Компоненти мають бути dumb, логіка - в сервісах
Коротко
У великих Angular-додатках звʼязок між компонентами має будуватись через Inputs/Outputs для ієрархії, shared services або state-management для віддалених компонентів, уникаючи жорстких залежностей.
85. Чи можна використовувати Angular для створення мобільних застосунків?
Так. Angular можна використовувати для мобільної розробки трьома основними способами.
- Hybrid Mobile Apps (Angular + WebView)
- Ionic + Angular
- Capacitor
npm install @ionic/angularЯк працює
- Angular → HTML/CSS/JS
- Запускається всередині WebView
- Один код для iOS / Android
Плюси
- Швидка розробка
- Один код-бейс
- Велика екосистема UI
Мінуси
- Обмежена native-продуктивність
- Progressive Web Apps (PWA)
Це Angular-додаток, який:
- працює офлайн
- встановлюється як мобільний app
- запускається з home screen
ng add @angular/pwaПлюси
- Без App Store
- Один код для web + mobile
- Швидке оновлення
Мінуси
- Обмежений доступ до native API
- iOS має обмеження
- Native Mobile Apps (через сторонні фреймворки)
NativeScript + Angular
- Angular + справжні native UI компоненти
Плюси
- Максимальна продуктивність
- Native look & feel
Мінуси
- Складніша розробка
- Менша спільнота
- Standalone components добре працюють з Ionic
- Signals → краща продуктивність UI
- RxJS → async flows (network, sensors)
- Один Angular-код → web + mobile
Коротко
Angular підходить для мобільної розробки через Ionic (hybrid), PWA або NativeScript, і вибір залежить від вимог до продуктивності та доступу до native API.
86. Що таке lonic і як він інтегрується з Angular?
Ionic - це фреймворк для створення кросплатформених застосунків (iOS, Android, Web) на базі web-технологій:
- HTML
- CSS
- JavaScript / TypeScript
Ionic надає:
- набір готових UI-компонентів, стилізованих під iOS та Material Design
- інтеграцію з native-можливостями через Capacitor
Ionic має офіційну Angular-інтеграцію (@ionic/angular) і працює як UI-шар
поверх Angular.
npm install @ionic/angularimport { IonicModule } from '@ionic/angular';
bootstrapApplication(AppComponent, {
providers: [
importProvidersFrom(IonicModule.forRoot())
]
});Angular
- логіка
- routing
- DI
- state management
Ionic
- UI-компоненти (ion-button, ion-list, ion-modal)
- mobile UX
- gestures, animations
<ion-button (click)="save()">Save</ion-button>npm install @capacitor/cameraimport { Camera } from '@capacitor/camera';
const photo = await Camera.getPhoto({
resultType: 'uri'
});Працює на iOS, Android і Web
- Один код для web + mobile
- Повна потужність Angular (standalone, signals, RxJS)
- Велика бібліотека UI
- Швидка розробка MVP
- WebView → не 100% native performance
- Важчий runtime порівняно з чистим native
- Не для high-performance 3D / heavy animations
- Standalone components - повністю підтримуються
- Signals - покращують продуктивність UI
- Lazy loading - критично важливий для mobile
- NgRx / Signals - для state management
Коротко
Ionic - це UI-фреймворк для кросплатформених застосунків, який тісно інтегрується з Angular, дозволяючи створювати мобільні та web-апки з одного код-бейсу.
87. Як додати новий компонент, сервіс або модуль за допомогою інтерфейсу командного рядка Angular?
- Створення компонента
Standalone компонент (default у Angular 20+)
ng generate component user
# або коротко
ng g c userСтворюється:
-
user.component.ts
-
user.component.html
-
user.component.css
-
user.component.spec.ts
Компонент standalone за замовчуванням, без NgModule.
Без HTML / CSS (inline)
ng g c user --inline-template --inline-style- Створення сервісу
ng generate service user
# або
ng g s user@Injectable({ providedIn: 'root' })
export class UserService {}Сервіс автоматично реєструється в DI.
- Створення модуля (legacy / специфічні кейси)
ng generate module admin
# або
ng g m adminУ Angular 20+ NgModule використовується рідко, перевага - standalone.
Директива
ng g directive highlightPipe
ng g pipe capitalizeGuard
ng g guard authResolver
ng g resolver user-
Використовувати standalone components
-
Генерувати через CLI для консистентності
-
Не створювати NgModule без потреби
-
Створювати за feature-структурою
Коротко
Angular CLI дозволяє швидко й консистентно створювати компоненти, сервіси та інші сутності; у Angular 20+ standalone-підхід є стандартом, а NgModule використовується лише у виняткових випадках.
88. Які переваги використання інтерфейсу командного рядка Angular для створення каркасів проектів?
- Швидкий старт проєкту
ng new my-app- Готова структура
- Налаштований білд
- TypeScript, lint, тестування з коробки
- Консистентна архітектура
- Єдина структура файлів
- Однакові підходи в усій команді
- Менше архітектурних помилок
- Standalone-first (Angular 20+)
- Компоненти, директиви, пайпи - standalone за замовчуванням
- Менше boilerplate
- Кращий tree-shaking
- Автоматичні best practices
- AOT
- Production configs
- Environment files
- Strict TypeScript
- Інтеграція з tooling
- Vite / build system
- Testing (unit + e2e)
- SSR (@angular/ssr)
- PWA
- i18n
ng add @angular/pwa
ng add @angular/ssr- Продуктивність і безпека
- Оптимізовані production-білди
- Мінімізація
- Tree shaking
- Безпечні дефолтні налаштування
- Підтримка масштабування
- Lazy loading
- Feature-based структура
- Готовність до enterprise-проєктів
- Ручна конфігурація білду
- Непослідовна структура
- Помилки в налаштуваннях
- Важкий онбординг нових dev’ів
Коротко
Angular CLI прискорює старт, забезпечує консистентну архітектуру, автоматично застосовує best practices і робить Angular-проєкти масштабованими та підтримуваними з першого дня.
89. Як оновити додаток на Angular до останньої версії за допомогою інтерфейсу командного рядка?
- Перевірити поточну версію
ng version- Оновити Angular CLI глобально
npm install -g @angular/cli@latestПеревірити:
ng version- Оновити Angular core та CLI в проєкті
ng update @angular/core @angular/cliCLI:
- оновить package.json
- застосує automated migrations
- покаже breaking changes (якщо є)
- Оновити додаткові пакети Angular
ng update @angular/material
ng update @ngrx/store(за потреби)
- Запустити та перевірити застосунок
ng serve
ng test
ng build- Мажорні оновлення (best practice)
Якщо оновлення через кілька major-версій:
ng update @angular/core@19 @angular/cli@19
ng update @angular/core@20 @angular/cli@20Не стрибати через major-версії
- Перед оновленням зробити commit
- Читати output CLI (warnings / TODO)
- Не оновлювати вручну package.json
- Використовувати офіційні migrations
Рекомендується:
- перейти на standalone components
- перевірити deprecated API
- оновити RxJS
- увімкнути stricter TypeScript
- переглянути zone / zoneless можливості
Коротко
Оновлення Angular виконується через ng update, яке автоматично застосовує міграції, оновлює залежності та допомагає безпечно перейти на останню версію фреймворку.
90. Як можна інтегрувати сторонні бібліотеки в застосунок Angular?
- Встановлення через npm (основний спосіб)
npm install lodashimport { debounce } from 'lodash';- Tree-shaking
- Типізація (якщо є)
- Рекомендований підхід
- Бібліотеки з Angular-обгорткою (preferred)
Приклад: Angular Material, ngx-translate, NgRx
npm install @angular/materialimport { MatButtonModule } from '@angular/material/button';- Нативна інтеграція з Angular
- DI, change detection, SSR-friendly
- Глобальні JS-бібліотеки (legacy)
Додавання через angular.json
"scripts": ["node_modules/some-lib/lib.js"]Використання
declare const SomeLib: any;- Не tree-shakable
- Не рекомендовано для нових проєктів
- Типізація бібліотек
Якщо типів немає:
npm install -D @types/library-nameабо створити вручну:
declare module 'legacy-lib';- Dynamic import (для важких бібліотек)
async loadChart() {
const { Chart } = await import('chart.js');
}- Code splitting
- Менший initial bundle
- Інтеграція через сервіс (best practice)
@Injectable({ providedIn: 'root' })
export class ChartService {
async load() {
const lib = await import('chart.js');
return lib.Chart;
}
}- Інкапсуляція + testability
- Angular 20+ рекомендації
- Віддавати перевагу npm + ES modules
- Lazy-load важкі бібліотеки
- Не підключати глобальні скрипти без потреби
- Обгортати бібліотеки у сервіси
- Перевіряти SSR-сумісність
- Підключення через
<script>у index.html - Відсутність типів
- Використання browser-only API без перевірки платформи
- Імпорт всієї бібліотеки замість named imports
Коротко
Сторонні бібліотеки в Angular найкраще інтегрувати через npm + ES imports, за потреби - lazy loading, обгортати у сервіси та уникати глобальних скриптів для кращої продуктивності й підтримуваності.
91. Що таке змінні середовища в Angular і як їх використовувати?
У Angular змінні середовища - це конфігураційні значення, які:
- відрізняються між
development,production,staging - використовуються для:
- API URLs
- feature flags
- логування
- інтеграцій із сервісами
src/environments/
├─ environment.ts
└─ environment.prod.tsexport const environment = {
production: false,
apiUrl: 'http://localhost:3000',
};export const environment = {
production: true,
apiUrl: 'https://api.example.com',
};import { environment } from '../environments/environment';
this.http.get(`${environment.apiUrl}/users`);Angular підміняє файл автоматично під час build.
У angular.json:
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}Build-time змінні
- Environment файли - build-time
- Після білду значення зашиті в JS
- Не можна змінити без нового build
Для великих систем часто використовують runtime config:
window.__env = {
apiUrl: 'https://api.example.com'
};export const API_URL = window.__env.apiUrl;- Один build → різні середовища
- Підходить для SSR / Kubernetes
- Зберігати секрети (API keys, passwords)
- Вважати environment secure
- Міняти env без rebuild (build-time vars)
- Environment → тільки публічна конфігурація
- Secrets → тільки на бекенді
- Для enterprise:
- Runtime config + SSR
- Типізуйте environment
interface Env { production: boolean; apiUrl: string; }Коротко
Змінні середовища в Angular - це build-time конфігурація, яка дозволяє використовувати різні налаштування для dev/prod; для складних систем застосовують runtime environment, але секрети ніколи не зберігають у frontend.
92. Чи можна використовувати веб-воркери в застосунках Angular і як?
Так. Angular підтримує Web Workers і дозволяє виконувати важкі обчислення у фоновому потоці, не блокуючи UI.
- Винести CPU-heavy задачі з main thread
- Уникнути фризів UI
- Покращити responsiveness
Типові кейси:
- Обробка великих масивів
- Криптографія
- Парсинг файлів
- Data processing (charts, analytics)
ng generate web-worker app
# або
ng g web-worker appСтворюється файл:
src/app/app.worker.ts// <reference lib="webworker" />
addEventListener('message', ({ data }) => {
const result = data * 2; // важка операція
postMessage(result);
});if (typeof Worker !== 'undefined') {
const worker = new Worker(
new URL('./app.worker', import.meta.url)
);
worker.onmessage = ({ data }) => {
console.log('Result:', data);
};
worker.postMessage(10);
}Немає доступу до:
- DOM
- window, document
- Angular DI
- Services, HttpClient
Доступно:
- Чистий JS/TS
- Message passing
- Pure logic
- Передавайте простi данi (structured clone)
- Тримайте логіку pure
- Не створюйте багато workers
- Для async IO → RxJS (не worker)
- Для UI state → signals
- Працює з standalone components
- Не залежить від zones / zoneless
- Добре поєднується з RxJS (fromEvent)
Коротко
Web Workers в Angular використовуються для виконання важких обчислень у фоновому потоці, створюються через Angular CLI і спілкуються з застосунком через postMessage, покращуючи продуктивність UI.
93. Як обробляти налаштування конфігурації в Angular?
- Build-time конфігурація (стандарт Angular)
Environment файли
src/environments/
├─ environment.ts
└─ environment.prod.ts// environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:3000',
};// використання
import { environment } from '../environments/environment';
this.http.get(`${environment.apiUrl}/users`);Плюси: простота, оптимізація Мінуси: значення «зашиті» в білд (потрібен rebuild)
- Runtime конфігурація (enterprise / SSR / Docker)
Завантаження конфігурації при старті
@Injectable({ providedIn: 'root' })
export class AppConfigService {
config!: { apiUrl: string };
load() {
return fetch('/assets/config.json')
.then(r => r.json())
.then(c => (this.config = c));
}
}// app.config.ts
providers: [{
provide: APP_INITIALIZER,
useFactory: (cfg: AppConfigService) => () => cfg.load(),
deps: [AppConfigService],
multi: true,
}]Плюси: один білд → різні середовища Мінуси: трохи більше складності
- Dependency Injection для конфігурації
export const API_URL = new InjectionToken<string>('API_URL');
providers: [{ provide: API_URL, useValue: environment.apiUrl }]constructor(@Inject(API_URL) private apiUrl: string) {}Плюси: типобезпечно, тестабельно
- Feature flags (рекомендовано через Signals)
@Injectable({ providedIn: 'root' })
export class FeatureFlags {
newUI = signal(false);
}<section *ngIf="flags.newUI()">...</section>- Environment - для build-time конфігів
- Runtime config - для enterprise/SSR
- DI tokens - для доступу до конфігів
- Не зберігати секрети у фронтенді
- Типізувати конфігурацію
- Ініціалізувати до старту (APP_INITIALIZER)
Коротко
В Angular конфігурацію керують через environment файли (build-time) або runtime-завантаження + DI; вибір залежить від вимог до деплою, SSR та масштабування.
94. Опишіть процес обміну даними між непов'язаними компонентами.
- Shared Service + Signals (рекомендовано)
- UI- або app-level стан
- Мінімальний boilerplate
- Висока продуктивність
@Injectable({ providedIn: 'root' })
export class UiStateService {
sidebarOpen = signal(false);
}// будь-який компонент
this.ui.sidebarOpen.set(true);<div *ngIf="ui.sidebarOpen()">...</div>- Простий, типобезпечний
- Ідеально для Angular 20+
- Shared Service + RxJS (events / async)
- Подієва модель
- Асинхронні потоки
@Injectable({ providedIn: 'root' })
export class RefreshService {
private refresh$ = new Subject<void>();
refresh = this.refresh$.asObservable();
trigger() {
this.refresh$.next();
}
}this.refreshService.refresh.subscribe(() => reload());- Не використовувати як глобальний state
- Завжди asObservable()
- NgRx Store (глобальний стан)
- Дані потрібні у багатьох фічах
- Складна бізнес-логіка
- Потрібен time-travel debugging
this.store.dispatch(loadUser());
user$ = this.store.select(selectUser);- Масштабованість
- Великий boilerplate
- Router State / Params (контекст навігації)
- Дані повʼязані з навігацією
this.router.navigate(['/details'], {
state: { id: 1 }
});const id = history.state.id;- Тимчасово, не для довготривалого стану
- Передавати дані через багато рівнів (prop drilling)
- Викликати методи іншого компонента
- Використовувати глобальні mutable сервіси
- Змішувати events і state в одному Subject
- Signals - default для стану
- RxJS - для асинхронних потоків
- NgRx - лише коли виправдано складністю
- Компоненти мають бути dumb, логіка - в сервісах
Коротко
Для обміну даними між неповʼязаними компонентами в Angular використовують shared services (signals або RxJS), global state (NgRx) або router state, уникаючи жорстких залежностей між компонентами.
95. Які популярні IDE для розробки Angular є та які функції вони пропонують для розробників?
- Visual Studio Code (VS Code)
Найпопулярніший вибір для Angular
- TypeScript IntelliSense (автодоповнення, перевірка типів)
- Angular Language Service
- Автодоповнення для шаблонів (
*ngFor,ngIf,async) - Перехід до визначення (
Go to Definition) - Error reporting у HTML-шаблонах
- Автодоповнення для шаблонів (
- Emmet
- Debugger для Chrome/Edge
- Extensions
- Angular Snippets
- ESLint
- Prettier
- Nx / Nrwl support
- Jest / Vitest
- Terminal / Git / Tasks вбудовані
Інтеграції та зручності
- Hotkeys, Refactoring
- Workspace settings
- Multi-root support
- Remote Development
- WebStorm (JetBrains)
Потужний комерційний IDE з глибокою підтримкою Angular
- Вбудований Angular support
- Advanced refactorings
- Code analysis & inspections
- Navigation & find usages
- Smart imports
- Live templates
- Integrated debugger
Переваги
- Більш “все-в-одному” ніж VS Code
- Краща підтримка великих проєктів
Потрібна ліцензія
- IntelliJ IDEA Ultimate
Розширена версія WebStorm плюс backend support
- Angular + full-stack (Node / Java / Spring) support
- Unified development experience
- Built-in REST client
- Database tools
Ідеально для enterprise-команд
- Sublime Text
Легкий редактор з плагінами для Angular
- TypeScript support
- Angular snippets
- Build system integration
Менше “розумних” фіч, ніж у VS Code/WebStorm
- Vim / Neovim
Максимальна кастомізація через конфіг та плагіни
- coc.nvim / LSP (TypeScript + Angular Language Service)
- Snippets
- Fuzzy finder
- Git integration
Підходить профі; steep learning curve
- Emacs
Гнучкий редактор із LSP підтримкою
- TypeScript + Angular LSP integration
- Smart completion
- Custom workflows
Потрібне налаштування
- StackBlitz / GitHub Codespaces
Онлайн-середовища для Angular
- Немає локального оточення
- Швидкий прототип
- Live preview
- Інтеграція з GitHub
- Modern editors/IDEs чудово працюють з:
- Standalone components
- Signals
- Vite-based builds
- RxJS patterns
- Розширення CLI інтегрується з Hotkeys / Tasks
Коротко
Найчастіше Angular-розробники використовують VS Code (light, extensible) або WebStorm/IntelliJ (потужний IDE). Обидва дають автодоповнення, підтримку Angular Language Service, refactoring, debugger, інтеграцію з Git, тестами та TypeScript.
96. Як налагоджувати застосунки Angular?
- Browser DevTools (основа)
Console
console.log / warn / error- Логи в компонентах, сервісах, effects
console.log('User:', this.user());Sources
- Breakpoints у TS (source maps)
- Step over / into
- Debugger у IDE (VS Code / WebStorm)
- Запуск ng serve
- Attach до Chrome/Edge
- Breakpoints у TypeScript
// .vscode/launch.json (спрощено)
{
"type": "chrome",
"request": "attach",
"port": 9222
}- Angular DevTools (обовʼязково)
Можливості
- Component tree
- Inputs / Outputs
- Signals state
- Change Detection cycles
- Performance profiling
Працює для standalone, signals, OnPush
- Debugging Change Detection
- Перевіряти зайві ререндери
- Використовувати OnPush
changeDetection: ChangeDetectionStrategy.OnPush- У signals - дивитись, що тригерить
computed()
- RxJS Debugging
tap для логування
this.users$
.pipe(tap(v => console.log(v)))
.subscribe();Контроль підписок
.pipe(takeUntilDestroyed())- HTTP Debugging
- Network tab (requests / headers / payload)
- Http Interceptors для логів
export const logInterceptor = (req, next) => {
console.log(req.url);
return next(req);
};- Template Debugging
<pre>{{ user() | json }}</pre>- Перевірка
*ngIf,*ngFor,trackBy
- Production Debugging
- Source maps (обережно)
- Centralized logging (Sentry, LogRocket)
- Feature flags для safe toggles
- Signals → легше відстежувати state
- Zoneless → менше “магії”, простіше дебажити
- Standalone → менше контексту, швидше пошук проблем
- inject() → прозорий DI
- Debug через console.log без breakpoints
- Відсутні source maps
- Ручні subscribe() без lifecycle-контролю
- Немає trackBy у великих списках
Коротко
Angular-дебагінг базується на Browser DevTools + Angular DevTools + IDE debugger; у Angular 20+ signals і standalone значно спрощують відстеження стану та change detection.
97. Поясніть, як використовувати Angular DevTools.
Angular DevTools - це browser extension (Chrome / Edge), який дозволяє:
- інспектувати дерево компонентів
- аналізувати change detection
- профілювати performance
- переглядати signals / inputs / outputs
Встановлення
- Chrome Web Store → Angular DevTools
- Працює у dev-режимі Angular
- Components Tree
Дозволяє:
- переглянути ієрархію компонентів
- побачити
OnPush/ Default CD - швидко знайти “важкі” компоненти
Корисно для:
- пошуку надмірної вкладеності
- аналізу архітектури
- Change Detection Profiler (ключовий)
Як використовувати
- Відкрити Profiler
- Натиснути Start profiling
- Взаємодіяти з UI
- Натиснути Stop
Що показує
- Скільки разів кожен компонент перевірявся
- Час виконання CD
- Компоненти, які перерендерюються без потреби
Типові висновки:
- де потрібен
OnPush - де мутації state
- де відсутній
trackBy
- Signals Debugging (Angular 16+ / 20+)
- Перегляд значень
signal() - Відстеження, що тригерить
computed() - Пошук зайвих recompute
Значно простіше, ніж RxJS debugging
- Performance + Browser DevTools
Angular DevTools добре комбінується з:
- Chrome Performance tab
- Timeline
- FPS / scripting time
Можна звʼязати Angular CD з browser repaint
- Відсутній
ChangeDetectionStrategy.OnPush - Мутація обʼєктів замість immutability
- Відсутній
trackByу*ngFor - Impure pipes
- Зайві subscriptions
- Великий global state
- Використовувати signals для UI-стану
OnPush- default- Lazy loading фіч
- Мінімізувати global change detection
- Zoneless → простіший performance model
Коротко
Angular DevTools (Augury) - ключовий інструмент для аналізу продуктивності Angular: він показує дерево компонентів, частоту change detection та дозволяє швидко знайти performance bottlenecks у сучасних Angular-застосунках.
98. Як інтегрувати Angular з іншими фреймворками або бібліотеками, такими як React або Vue.js?
- Міграція legacy-застосунку
- Micro-frontend архітектура
- Спільне використання UI між різними командами
- Вставка ізольованого віджета (widget)
- Поступовий перехід між фреймворками
- Web Components (рекомендований підхід)
Кожен фреймворк експортує UI як custom element, незалежний від реалізації.
Angular → Web Component
import { createCustomElement } from '@angular/elements';
const element = createCustomElement(AppComponent, { injector });
customElements.define('my-angular-app', element);<my-angular-app></my-angular-app>Переваги
- Повна ізоляція
- Працює з Angular / React / Vue
- Незалежні деплои
- Framework-agnostic
Недоліки
- Більший bundle
- Обмежений shared state
- Micro-frontends (Module Federation)
Кожен фреймворк - окремий remote app.
Shell (Angular)
├─ Angular remote
├─ React remote
└─ Vue remote
Інструменти
- Webpack Module Federation
- Nx
- Single-SPA
Переваги
- Enterprise-ready
- Незалежні команди
- Незалежні релізи
Недоліки
- Висока складність
- Архітектурна дисципліна
- Вбудовування через iframe (простий, але грубий)
<iframe src="https://react-app.example.com"></iframe>Плюси
- Максимальна ізоляція
- Мінімум інтеграції
Мінуси
- Поганий UX
- Складний communication
- SEO проблеми
- Використання сторонніх бібліотек (не фреймворків)
Приклад: React-based library в Angular
Не рекомендовано напряму
Краще:
- знайти framework-agnostic версію
- або wrapper
- або винести в Web Component
- Shared state між фреймворками
Варіанти
- Custom events
- postMessage
- URL / Router state
- Shared backend (API-driven state)
Не ділити напряму Angular Store / Signals з React
- Web Components - default choice
- Micro-frontends - для enterprise
- Не змішувати Angular і React в одному runtime
- Не імпортувати React у Angular напряму
- Чіткі boundaries між системами
- Angular component всередині React tree напряму
- Спільний глобальний state між фреймворками
- Tight coupling між lifecycle фреймворків
Коротко
Angular можна інтегрувати з React або Vue через Web Components або micro-frontends. Найкращий підхід - ізоляція, а не пряме змішування фреймворків у одному runtime.
99. Чи можна вбудувати застосунок Angular в інший застосунок?
Так, можна. Існує кілька підходів залежно від рівня інтеграції та вимог до ізоляції.
- Web Components (Angular Elements) - рекомендовано
Angular app/компонент експортується як custom element і використовується в будь-якому середовищі.
import { createCustomElement } from '@angular/elements';
const el = createCustomElement(AppComponent, { injector });
customElements.define('my-angular-app', el);<my-angular-app></my-angular-app>Плюси
- Framework-agnostic
- Ізоляція
- Просте вбудовування
Мінуси
- Більший bundle
- Обмежений shared state
- Micro-frontends (Module Federation / single-spa)
Коли
- Великий enterprise
- Незалежні команди/релізи
Плюси
- Незалежні деплои
- Масштабованість
Мінуси
- Висока складність
- Потрібна архітектурна дисципліна
- iframe (простий, але грубий)
<iframe src="https://angular-app.example.com"></iframe>Плюси
- Максимальна ізоляція
- Мінімум інтеграції
Мінуси
- Гірший UX
- Складний обмін даними
- SEO/перформанс обмеження
- Вбудований віджет (script + bootstrap)
- Angular app завантажується як віджет
- Комунікація через Custom Events
Менш рекомендовано, ніж Web Components
- Custom Events
- postMessage
- URL params
- API (backend як джерело правди)
Не ділити напряму Angular state (Signals/NgRx) з хостом
- Web Components - default
- Micro-frontends - для enterprise
- Не змішувати runtime різних фреймворків напряму
- Чіткі boundaries та контракти
Коротко
Angular-застосунок можна вбудувати в інший застосунок. Найкращі підходи - Web Components або micro-frontends; iframe - лише для простих або legacy кейсів.
100. Які проблеми можуть бути під час оновлення Angular-застосунку до новішої версії?
Основні категорії проблем
- Breaking changes Angular API
- Видалені або deprecated API
- Зміни в поведінці Router, Forms, DI
- Обовʼязкові міграції
Приклад
- Застарілі lifecycle hooks / API
- Зміни в SSR або hydration
Рішення: уважно читати output ng update і changelog
- Несумісність сторонніх бібліотек
- Бібліотека не підтримує нову версію Angular
- Peer dependency conflicts
npm ERR! peer dep missingРішення:
- Оновлювати бібліотеки по черзі
- Перевіряти compatibility matrix
- За потреби - тимчасові заміни
- TypeScript / RxJS оновлення
- Строгіші типи
- Помилки компіляції
- Зміни в RxJS операторах
// раніше проходило
someValue!.propertyРішення:
- Виправляти типи
- Не вимикати strict mode
- Оновлювати RxJS поступово
- Зміни в архітектурі (Angular 16+ → 20+)
- Перехід на standalone components
- Менше NgModules
- Нові підходи (signals, inject())
Проблема: legacy-код виглядає застарілим Рішення: мігрувати поступово, не все одразу
- SSR / Build / Tooling
- Зміни в build system (Vite)
- SSR API
- Hydration issues
Рішення:
- Перевіряти SSR окремо
- Тестувати production build
- Тести ламаються
- Jasmine/Karma конфлікти
- Застарілі TestBed патерни
- Async timing issues
Рішення:
- Оновити тест-раннер
- Перейти на Jest / Vitest (за можливості)
- Performance регресії
- Зайві change detection цикли
- Відсутній OnPush
- Legacy patterns без signals
Рішення:
- Angular DevTools
- OnPush + signals
- trackBy у списках
- Завжди commit перед оновленням
- Оновлювати по major-версіях
- Використовувати тільки ng update
- Читати warnings CLI
- Проганяти тести після кожного кроку
Коротко
Основні проблеми при оновленні Angular - це breaking changes, несумісні бібліотеки, строгіші типи та legacy-архітектура. Вирішуються поетапним оновленням, офіційними міграціями та хорошим тест-покриттям.
101. Що таке Signals в Angular і які їх основні типи?
Signals - це нова реактивна примітивна модель в Angular, яка дозволяє відслідковувати залежності між станом і UI на рівні окремих виразів (fine-grained reactivity).
Signals замінюють імперативний change detection і зменшують потребу у Zone.js.
signal() - writable signal для зберігання стану
count = signal(0);
count.set(1);
count.update(v => v + 1);computed() - derived signal, що залежить від інших signals
doubleCount = computed(() => this.count() * 2);effect() - side effects при зміні сигналів
effect(() => {
console.log(this.count());
});input()- реактивна альтернатива @Inputoutput()- signal-based подіїmodel()- двосторонній binding на основі signals
value = input<number>();
changed = output<number>();
state = model<string>();Коротко:
Signals - це основа сучасної реактивності Angular і ключ до zoneless архітектури.
102. Що таке @defer і як він використовується в Angular?
@defer - це механізм відкладеного рендерингу шаблонів, який дозволяє завантажувати та відображати контент лише за певних умов.
Використовується для:
- покращення performance
- зменшення initial load
- оптимізації роботи з DOM
@defer {
<heavy-component />
} @placeholder {
<p>Loading...</p>
} @error {
<p>Error loading content</p>
}- on viewport
- on idle
- on interaction
- on timer
Коротко:
@defer - declarative lazy rendering для компонентів і DOM.
103. Які особливості @for у порівнянні з *ngFor?
@for - це новий control flow синтаксис, оптимізований для сучасного Angular.
Обовʼязковий track
@for (item of items; track item.id) {
<div>{{ item.name }}</div>
}Підтримка @empty
@for (item of items; track item.id) {
<div>{{ item.name }}</div>
} @empty {
<p>No items</p>
}- Краща інтеграція з signals
- Менше runtime overhead
Коротко:
@for - більш явний, безпечний та продуктивний механізм рендерингу списків.
104. Що таке Content Projection в Angular?
Content Projection - це механізм вставки зовнішнього контенту всередину компонента.
ng-content - projection слот
<ng-content></ng-content>select - множинні слоти
<ng-content select="[header]"></ng-content>
<ng-content select="[body]"></ng-content>ng-template - шаблони для умовного або відкладеного рендерингу
<ng-template #tpl>
<p>Projected template</p>
</ng-template>- reusable UI components
- layout components
- design systems
Коротко:
Content Projection дозволяє будувати гнучкі, композиційні компоненти.