1. Що таке Angular та які його ключові особливості?
- Angular — це сучасний фронтенд-фреймворк від Google для побудови SPA та масштабованих веб-додатків.
-
Standalone Components — більше немає потреби у NgModules.
-
Signals — новий реактивний підхід до роботи зі станом.
-
Control flow (@if, @for, @switch) — нативний синтаксис замість *ngIf та *ngFor.
-
DI (Dependency Injection) — гнучка система залежностей із підтримкою tree-shaking.
-
Router API — сучасна маршрутизація без модулів, з lazy loading.
-
TypeScript + строгі типи — безпечна розробка на TS.
-
Оптимізований рендер — швидкий change detection, підготовка до zoneless архітектури.
Коротко: Angular — це full-fledged фреймворк із вбудованим DI, реактивністю через signals та сучасними standalone підходами, що дозволяють писати масштабовані додатки без зайвої складності
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?
- Двостороннє зв’язування — це синхронізація стану між компонентом і шаблоном, коли зміни в UI автоматично оновлюють дані компонента і навпаки.
<input [(ngModel)]="name" />
<p>Hello, {{ name }}</p>import { Component } from '@angular/core';
@Component({
selector: 'app-input',
standalone: true,
template: `<input [(ngModel)]="name" />`
})
export class InputComponent {
name = 'Viktor';
}import { Component, signal } from '@angular/core';
@Component({
selector: 'app-input',
standalone: true,
template: `<input [value]="name()" (input)="name.set($any($event.target).value)" />`
})
export class InputComponent {
name = signal('Viktor');
}Коротко: two-way binding = синхронізація стану між UI та компонентом. В Angular 20 можна робити через [(ngModel)] або signals для сучасної реактивності.
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().