An open API service indexing awesome lists of open source software.

https://github.com/devloversteam/angular-interview-questions


https://github.com/devloversteam/angular-interview-questions

Last synced: 5 months ago
JSON representation

Awesome Lists containing this project

README

          


Angular

Найпопулярніші запитання та відповіді на співбесіді з Angular

1. Що таке Angular та які його ключові можливості?

#### Angular

**Angular** - це повноцінний frontend-фреймворк від Google для побудови
масштабованих веб-додатків із підтримкою SPA, SSR, SSG та hybrid rendering.

Angular надає структурований підхід до розробки великих застосунків, включаючи
архітектуру, маршрутизацію, управління станом і продуктивністю "out of the box".

#### Ключові можливості сучасного Angular:

- **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 та які є його типи?

#### Angular

- Data-binding - це механізм синхронізації даних між компонентом і шаблоном.

#### Типи data-binding в Angular:

1. **Interpolation** - одностороннє відображення даних у HTML:

```HTML

{{ userName }}


```

2. **Property binding** - передача значень у властивості
DOM-елементів/компонентів:

```HTML

```

3. **Event binding** - реакція на події DOM:

```HTML
Save
```

4. **Two-way binding** - синхронізація стану між шаблоном і компонентом
([(...)]):

```HTML

```

**Коротко**

В Angular доступні 4 основні типи зв’язування даних - interpolation, property
binding, event binding, two-way binding.

3. Опиши архітектуру Angular-додатку.

#### 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

- Компонент - це основний будівельний блок Angular-додатку, що відповідає за
частину UI та пов’язану з нею логіку.

#### Складається з:

- класу (логіка, стан),

- шаблону HTML,

- стилів,

- метаданих (selector, imports тощо).

#### Використання:

```TypeScript
import { Component, signal } from '@angular/core';

@Component({
selector: 'app-user-card',
standalone: true,
template: `

{{ name() }}


Change
`
})
export class UserCardComponent {
name = signal('Viktor');
changeName() {
this.name.set('Updated Name');
}
}
```

У шаблоні іншого компонента можна підключити:

```HTML

```

**Коротко**

Компонент = ізольований блок UI + логіка. В Angular він створюється як
standalone, без NgModules.

5. Що таке директиви в Angular та які з них найчастіше використовуються?

#### 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 і навіщо його використовують?

#### Angular

Сервіс - це клас із бізнес-логікою або функціоналом, який не пов’язаний напряму
з UI.

#### Використовується для:

- повторного використання коду,

- роботи з API,

- керування станом,

- інкапсуляції логіки поза компонентом.

#### Приклад:

```TypeScript
import { Injectable, signal } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class UserService {
userName = signal('Guest');

setUser(name: string) {
this.userName.set(name);
}
}
```

#### Використання у компоненті:

```TypeScript
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';

@Component({
selector: 'app-header',
standalone: true,
template: `

Welcome, {{ userService.userName() }}

`
})
export class HeaderComponent {
userService = inject(UserService);
}
```

**Коротко**

Сервіс створюють через `@Injectable`, а використовують для бізнес-логіки та
спільного стану між компонентами.

7. Поясни, що таке dependency injection (DI) в Angular.

#### Angular

Dependency Injection (DI) - це механізм Angular, який автоматично створює та
надає об’єкти (сервіси, токени) компонентам чи іншим сервісам замість ручного
створення через new.

#### Навіщо:

- спрощує тестування (можна підмінити залежності mock-ами),

- забезпечує повторне використання сервісів,

- керує життєвим циклом об’єктів (singleton, scoped).

#### Приклад:

```TypeScript
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ApiService {
getData() {
return ['item1', 'item2'];
}
}
```

#### Використання у компоненті:

```TypeScript
import { Component, inject } from '@angular/core';
import { ApiService } from './api.service';

@Component({
selector: 'app-list',
standalone: true,
template: `

  • {{ item }}
  • `
    })
    export class ListComponent {
    api = inject(ApiService);
    data = this.api.getData();
    }
    ```

    **Коротко**

    DI в Angular = автоматичне надання залежностей (наприклад, сервісів) компонентам
    без `new`.

    8. Що таке модуль в Angular і для чого він використовується?

    #### Angular

    - У попередніх версіях Angular (до 15) модулі (NgModule) були обов’язковими для
    структурування застосунку. В Angular 20 модулі більше не потрібні, оскільки
    з’явилися standalone components.

    #### Проте модулі ще існують і можуть застосовуватись для:

    - сумісності зі старим кодом,

    - групування функціоналу (напр. Angular Material ще має модулі),

    - поступової міграції на standalone API.

    #### Приклад старого підходу:

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

    #### Актуальний підхід (Angular 20, без модуля):

    ```TypeScript
    bootstrapApplication(AppComponent, {
    providers: []
    });
    ```

    **Коротко**

    Модулі в Angular зараз - це легасі-інструмент, який замінено на standalone
    компоненти. Їхня головна роль сьогодні - лише для підтримки старого коду чи
    бібліотек.

    9. Як обробляти події в Angular?

    #### Angular

    В Angular події обробляються через event binding, тобто підписку на подію DOM
    або кастомної події компонента.

    #### Синтаксис:

    ```HTML
    Click me
    ```

    #### У компоненті:

    ```ts
    import { Component } from '@angular/core';

    @Component({
    selector: 'app-button',
    standalone: true,
    template: `Click me`,
    })
    export class ButtonComponent {
    onClick() {
    console.log('Button clicked!');
    }
    }
    ```

    #### Кастомна подія (для дочірнього компонента):

    ```TypeScript
    import { Component, EventEmitter, Output } from '@angular/core';

    @Component({
    selector: 'app-child',
    standalone: true,
    template: `Notify`
    })
    export class ChildComponent {
    @Output() notify = new EventEmitter();
    notifyParent() {
    this.notify.emit('Hello from child');
    }
    }
    ```

    #### У батьківському компоненті:

    ```HTML

    ```

    Коротко: в Angular події обробляються через `(eventName)="handler()"` для DOM та
    через `@Output` + `EventEmitter` для кастомних подій.

    10. Що таке двостороннє зв’язування (two-way binding) і як його реалізувати в Angular?

    #### Angular

    Двостороннє зв’язування (two-way binding) - це синхронізація стану між
    компонентом і шаблоном, коли:

    - зміни в UI оновлюють стан компонента,
    - зміни в компоненті автоматично відображаються в UI.

    #### Класичний підхід (ngModel, template-driven forms):

    ```HTML

    Hello, {{ name }}


    ```

    ```TypeScript
    @Component({
    selector: 'app-input',
    standalone: true,
    template: ``
    })
    export class InputComponent {
    name = 'Evan';
    }
    ```

    #### Сучасний підхід з Signals (локальний стан):

    ```TypeScript
    import { Component, signal } from '@angular/core';

    @Component({
    selector: 'app-input',
    standalone: true,
    template: `

    Hello, {{ name() }}


    `
    })
    export class InputComponent {
    name = signal('Evan');
    }
    ```

    Signals забезпечують реактивність, але two-way binding тут реалізується вручну
    через event → set().

    #### Two-way binding між компонентами з model():

    ```TypeScript
    import { Component, model } from '@angular/core';

    @Component({
    selector: 'app-input',
    standalone: true,
    template: `

    `
    })
    export class InputComponent {
    name = model('Evan');
    }
    ```

    ```HTML

    ```

    **Коротко**

    - `ngModel` - legacy two-way binding
    - `signal()` - сучасна реактивність (локальний стан)
    - `model()` - рекомендований спосіб two-way binding у сучасному Angular

    11. Поясни різницю між компонентом і директивою в Angular?

    #### Angular

    - Компонент

    - це спеціальний тип директиви, який має шаблон (HTML) + стилі + логіку;

    - використовується для створення UI-елементів;

    - приклад: `@Component({ selector: 'app-user', template: '

    User

    ' })`.

    - Директива

    - не має власного шаблону;

    - змінює поведінку або вигляд існуючих елементів/компонентів;

    - може бути structural (`@if`, `@for`) або attribute (`ngClass`, `ngStyle`).

    #### Приклад кастомної директиви (attribute):

    ```TypeScript
    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');
    }
    }
    ```

    Використання у шаблоні:

    ```HTML

    Text with highlight


    ```

    **Коротко**

    компонент = директива + шаблон, а директива = поведінка без власного UI.

    12. Що таке пайпи (Pipes) в Angular та де їх варто використовувати?

    #### Angular

    Pipe - це клас, який трансформує дані без зміни їхнього оригінального стану.
    Використовується у шаблонах для форматування значень.

    #### Приклади вбудованих пайпів:

    - `date` → форматування дат

    - `currency` → вивід валют

    - `uppercase` / `lowercase` → зміна регістру

    - `async` → робота з Promise / Observable

    #### Приклад використання:

    ```HTML

    {{ today | date:'dd/MM/yyyy' }}


    {{ price | currency:'USD' }}


    ```

    #### Кастомний pipe:

    ```TypeScript
    import { Pipe, PipeTransform } from '@angular/core';

    @Pipe({
    name: 'exclaim',
    standalone: true
    })
    export class ExclaimPipe implements PipeTransform {
    transform(value: string): string {
    return value + '!';
    }
    }
    ```

    У шаблоні:

    ```HTML

    {{ 'Hello' | exclaim }}

    ```

    **Коротко**

    Pipes потрібні для форматування та трансформації даних у шаблоні, щоб не
    захаращувати логіку компонента.

    13. Як обробляти надсилання форм (form submissions) в Angular?

    #### Angular

    **В Angular є два основні підходи:**

    1. **Template-driven forms** (простий варіант, з `ngModel`):

    ```HTML


    Send

    ```

    ```TypeScript
    onSubmit(value: any) {
    console.log('Form submitted:', value);
    }
    ```

    2. **Reactive forms** (рекомендований для складних кейсів):

    ```TypeScript
    import { Component } from '@angular/core';
    import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';

    @Component({
    selector: 'app-login',
    standalone: true,
    imports: [ReactiveFormsModule],
    template: `


    Login

    `
    })
    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

    **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

    В Angular для роботи з HTTP використовується HttpClient, який надає методи get,
    post, put, delete тощо.

    #### Кроки:

    1. Імпортувати HttpClientModule у bootstrapApplication.

    2. Інжектити HttpClient у сервіс чи компонент.

    3. Виконати запит і підписатися (або використовувати async pipe).

    #### Приклад сервісу:

    ```TypeScript
    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');
    }
    }
    ```

    #### Використання у компоненті:

    ```TypeScript
    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: `


    • {{ user.name }}


    `
    })
    export class UsersComponent {
    api = inject(ApiService);
    users$ = this.api.getUsers();
    }
    ```

    **Коротко**

    В Angular 20 HTTP-запити робляться через HttpClient, а результат часто
    обробляється в шаблоні через async pipe.

    16. Як передати дані з батьківського компонента до дочірнього?

    #### Angular

    Передача даних відбувається через input-зв’язування (@Input() декоратор).
    Батьківський компонент передає значення дочірньому через атрибут у шаблоні.

    #### Приклад:

    **child.component.ts**

    ```TypeScript
    import { Component, Input } from '@angular/core';

    @Component({
    selector: 'app-child',
    standalone: true,
    template: `

    Message: {{ message }}

    `
    })
    export class ChildComponent {
    @Input() message = '';
    }
    ```

    **parent.component.ts**

    ```TypeScript
    import { Component } from '@angular/core';
    import { ChildComponent } from './child.component';

    @Component({
    selector: 'app-parent',
    standalone: true,
    imports: [ChildComponent],
    template: ``
    })
    export class ParentComponent {
    parentMessage = 'Hello from Parent!';
    }
    ```

    **Коротко:**

    - Дані від батька до дитини передаються через @Input() - це property binding
    [property]="value".

    17. Як передати подію або дані від дочірнього компонента до батьківського?

    #### Angular

    Для передачі подій вгору використовується @Output() разом із EventEmitter.
    Дочірній компонент «викидає» подію, а батьківський підписується на неї через
    (eventName) у шаблоні.

    **child.component.ts**

    ```TypeScript
    import { Component, EventEmitter, Output } from '@angular/core';

    @Component({
    selector: 'app-child',
    standalone: true,
    template: `Send`
    })
    export class ChildComponent {
    @Output() message = new EventEmitter();

    sendMessage() {
    this.message.emit('Hello from Child!');
    }
    }
    ```

    **parent.component.ts**

    ```TypeScript
    import { Component } from '@angular/core';
    import { ChildComponent } from './child.component';

    @Component({
    selector: 'app-parent',
    standalone: true,
    imports: [ChildComponent],
    template: ``
    })
    export class ParentComponent {
    onMessage(data: string) {
    console.log('Received from child:', data);
    }
    }
    ```

    **Коротко**

    Передача даних child → parent відбувається через `@Output()` і
    `(event) binding`. Дитина емітить подію, батько слухає.

    18. Які є життєві цикли (lifecycle hooks) компонентів в Angular і що вони означають?

    #### Angular

    Lifecycle hooks - це методи, які Angular викликає на різних етапах «життя»
    компонента: створення, оновлення, знищення.

    #### Основні хуки Angular:

    | Хук | Коли викликається | Типове використання |
    | --------------------------- | -------------------------------------------------------- | ----------------------------------------------------------- |
    | **ngOnChanges(changes)** | Коли змінюються @Input властивості | Реакція на зміни вхідних даних від батьківського компонента |
    | **ngOnInit()** | Один раз після ініціалізації компоненту | Ініціалізація даних, запитів до API |
    | **ngDoCheck()** | На кожній зміні (детекції) | Кастомна логіка перевірки змін |
    | **ngAfterContentInit()** | Один раз після вставлення контенту (ng-content) | Робота з проєктованим контентом |
    | **ngAfterContentChecked()** | Після кожної перевірки контенту | Оновлення після змін у проєктованому контенті |
    | **ngAfterViewInit()** | Один раз після ініціалізації view (дочірніх компонентів) | Доступ до елементів через ViewChild/ViewChildren |
    | **ngAfterViewChecked()** | Після кожної перевірки view | Оновлення DOM після перевірки |
    | **ngOnDestroy()** | Перед знищенням компоненту | Очищення підписок, таймерів, ресурсів |

    #### Приклад:

    ```TypeScript
    import { Component, OnInit, OnDestroy } from '@angular/core';

    @Component({
    selector: 'app-demo',
    standalone: true,
    template: `

    Lifecycle demo

    `
    })
    export class DemoComponent implements OnInit, OnDestroy {
    ngOnInit() {
    console.log('Component initialized');
    }

    ngOnDestroy() {
    console.log('Component destroyed');
    }
    }
    ```

    **Коротко**

    Lifecycle hooks - це хуки життєвого циклу компонента, які дають змогу реагувати
    на створення, оновлення та знищення елемента.

    19. Що таке ViewEncapsulation в Angular і для чого воно використовується?

    #### Angular

    ViewEncapsulation - це механізм інкапсуляції стилів у Angular, який визначає, як
    CSS компоненту впливає на DOM (чи лише на цей компонент, чи на весь застосунок).

    | Тип інкапсуляції | Опис | Особливість |
    | ------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------- |
    | **Emulated** _(default)_ | Angular імітує поведінку Shadow DOM, додаючи унікальні атрибути до елементів. | Стилі діють лише всередині цього компонента. |
    | **ShadowDom** | Використовує нативний Shadow DOM браузера. | Повна ізоляція стилів, немає витоку назовні. |
    | **None** | Без інкапсуляції. | Стилі поширюються глобально на весь застосунок. |

    #### Приклад:

    ```TypeScript
    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

    - В Angular умовне стилювання реалізується через директиви прив’язки стилів та
    класів - `ngClass` і `ngStyle`.

    | Метод | Приклад | Опис |
    | ---------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------- |
    | **[ngClass]** | `

    ` | Додає або забирає CSS-класи залежно від умови. |
    | **[ngStyle]** | `
    ` | Застосовує стилі напряму через об’єкт. |
    | **Класова прив’язка** | `
    ` | Додає клас, якщо умова `true`. |
    | **Стильова прив’язка** | `
    ` | Змінює конкретний CSS-властивість залежно від умови. |

    **Коротко:**

    Використовуй `ngClass` для керування класами та `ngStyle` або `[style.prop]` для
    динамічних inline-стилів. Це дає повний контроль над виглядом елементів залежно
    від стану компонента.

    21. У чому різниця між структурними та атрибутними директивами в Angular?

    #### Angular

    - Директиви в Angular бувають структурні та атрибутні, і вони впливають на DOM
    по-різному.

    | Тип директиви | Опис | Приклади | Вплив на DOM |
    | --------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------- |
    | **Структурна (Structural)** | Змінює **структуру DOM** - додає, видаляє або змінює елементи. | `*ngIf`, `*ngFor`, `*ngSwitchCase` | Створює або прибирає елементи в дереві DOM. |
    | **Атрибутна (Attribute)** | Змінює **вигляд або поведінку** наявного елемента. | `ngClass`, `ngStyle`, `ngModel`, кастомні директиви (наприклад, `appHighlight`) | Не змінює структуру DOM, лише властивості або стилі елемента. |

    **Коротко:**

    Структурні директиви керують тим, що є в DOM, атрибутні директиви - тим, як це
    виглядає або поводиться.

    22. Як створити власну структурну директиву в Angular?

    #### Angular

    Структурна директива змінює DOM (додає або видаляє елементи). Щоб створити
    кастомну структурну директиву:

    | Крок | Опис |
    | ---- | ---------------------------------------------------------------------------------- |
    | 1 | Створити директиву з декоратором `@Directive` і `standalone: true`. |
    | 2 | Інжектити `TemplateRef` і `ViewContainerRef` для доступу до шаблону та контейнера. |
    | 3 | Створити метод або сеттер, який вирішує, коли вставляти або видаляти шаблон. |
    | 4 | Використовувати директиву через `*yourDirective` у шаблоні. |

    #### Приклад кастомної структурної директиви:

    ```TypeScript
    import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

    @Directive({
    selector: '[appUnless]',
    standalone: true
    })
    export class UnlessDirective {
    constructor(
    private templateRef: TemplateRef,
    private viewContainer: ViewContainerRef
    ) {}

    @Input() set appUnless(condition: boolean) {
    this.viewContainer.clear();
    if (!condition) {
    this.viewContainer.createEmbeddedView(this.templateRef);
    }
    }
    }
    ```

    #### Використання у шаблоні:

    ```HTML

    You are not logged in!


    ```

    **Коротко:**

    Кастомна структурна директива керує DOM через `ViewContainerRef` і
    `TemplateRef`. Використовується з `*` синтаксисом у шаблоні.

    23. Як зробити сервіс singleton в Angular?

    #### Angular

    У Angular singleton-сервіс - це сервіс, який створюється лише один раз і
    використовується у всьому застосунку. Для цього потрібно вказати, де він
    надається (provided).

    | Спосіб | Приклад | Пояснення |
    | ------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
    | **1. Через `providedIn: 'root'`** | `@Injectable({ providedIn: 'root' })` | Найпоширеніший спосіб. Сервіс реєструється в головному інжекторі, створюється один раз для всього застосунку. |
    | **2. Через модуль (deprecated підхід)** | Додати в `providers` масив модуля (`@NgModule`) | Використовується рідше. Сервіс буде singleton лише в межах цього модуля. |
    | **3. Через компонент (локальний інжектор)** | Додати в `providers` масив компонента | Сервіс не буде singleton - створюється новий екземпляр для кожного компонента. |

    #### Приклад:

    ```TypeScript
    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 у сервісах для обміну даними між компонентами?

    #### Angular

    Observables у сервісах дозволяють реактивно ділитися даними між компонентами -
    без прямої передачі через `@Input()` чи `@Output()`.

    | Підхід | Опис | Типовий випадок використання |
    | ------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------ |
    | **Subject** | Дає змогу як передавати (`next()`), так і підписуватись (`subscribe()`) на дані. | Динамічне оновлення стану між компонентами. |
    | **BehaviorSubject** | Зберігає останнє значення, яке автоматично отримують нові підписники. | Поточний стан (наприклад, авторизація, вибраний користувач). |
    | **ReplaySubject** | Передає певну кількість останніх значень новим підписникам. | Історія подій або кешування даних. |

    #### Приклад (через BehaviorSubject):

    **data.service.ts**

    ```TypeScript
    import { Injectable } from '@angular/core';
    import { BehaviorSubject } from 'rxjs';

    @Injectable({ providedIn: 'root' })
    export class DataService {
    private messageSource = new BehaviorSubject('Hello');
    message$ = this.messageSource.asObservable();

    updateMessage(newMsg: string) {
    this.messageSource.next(newMsg);
    }
    }
    ```

    **component-a.ts**

    ```TypeScript
    @Component({...})
    export class ComponentA {
    constructor(private dataService: DataService) {}
    sendMessage() {
    this.dataService.updateMessage('Message from A');
    }
    }
    ```

    **component-b.ts**

    ```TypeScript
    @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

    - У 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` | Новий екземпляр для кожного екземпляра компонента | Для локального стану або ізольованої логіки. |

    #### Приклад:

    ```TypeScript
    @Injectable({
    providedIn: 'root'
    })
    export class UserService {}
    ```

    **або**

    ```TypeScript
    @Component({
    selector: 'app-profile',
    providers: [UserService]
    })
    export class ProfileComponent {}
    ```

    **Коротко:**

    Найчастіше використовується `providedIn: 'root'` - це дає один спільний
    екземпляр (singleton). Інші способи - для lazy-loading, ізоляції або особливих
    випадків.

    26. Поясни, що таке providedIn у сервісах Angular і яку роль воно відіграє?

    #### Angular

    - `providedIn` - це параметр у декораторі `@Injectable`, який визначає, де
    Angular має зареєструвати сервіс у DI (Dependency Injection) системі. Від
    нього залежить область дії (scope) сервісу та кількість створених екземплярів.

    | Значення `providedIn` | Опис | Область дії | Використання |
    | ------------------------------ | ------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------ |
    | `'root'` | Сервіс реєструється у головному інжекторі застосунку. | Глобальна (singleton у всьому застосунку). | ✅ Найпоширеніший і рекомендований спосіб. |
    | `'platform'` | Один інжектор для всієї платформи (кілька Angular app на сторінці). | Спільний між застосунками. | Рідкісний випадок використання. |
    | `'any'` | Кожен lazy-loaded модуль отримує власний екземпляр. | Локальна для модуля або компонента. | Для незалежних частин застосунку. |
    | Клас або модуль (`SomeModule`) | Сервіс буде створено лише в межах цього модуля. | Локальна. | Використовується для модульної ізоляції. |

    #### Приклад:

    ```TypeScript
    @Injectable({
    providedIn: 'root'
    })
    export class LoggerService {
    log(message: string) {
    console.log(`[LOG]: ${message}`);
    }
    }
    ```

    **Коротко:**

    `providedIn` визначає, де саме Angular створює сервіс і чи буде він спільним
    (singleton). У більшості випадків використовують `providedIn: 'root'` - це
    просто, ефективно і підтримує tree-shaking.

    27. Як у Angular використовувати HttpClient для обробки JSON-даних?

    #### Angular

    `HttpClient` - це сервіс Angular для виконання HTTP-запитів. Він автоматично
    перетворює JSON-відповіді в об’єкти JavaScript, тому додаткового парсингу не
    потрібно.

    | Крок | Опис |
    | ---- | ----------------------------------------------------------------- |
    | 1 | Імпортуй `HttpClientModule` у кореневий або standalone компонент. |
    | 2 | Інжектуй `HttpClient` у сервіс або компонент. |
    | 3 | Використовуй методи `get()`, `post()`, `put()`, `delete()` тощо. |
    | 4 | Angular автоматично обробляє JSON через RxJS `Observable`. |

    #### Приклад:

    ```TypeScript
    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 {
    return this.http.get(this.apiUrl);
    }

    addUser(user: User): Observable {
    return this.http.post(this.apiUrl, user);
    }
    }
    ```

    **component.ts**

    ```TypeScript
    @Component({...})
    export class AppComponent {
    users$ = this.userService.getUsers();

    constructor(private userService: UserService) {}
    }
    ```

    #### Особливості:

    - `HttpClient` автоматично парсить JSON у JS-об’єкти.

    - Можна вказати generic тип (``), щоб отримати типізовану відповідь.

    - Повертає Observable, тому можна застосовувати оператори RxJS (`map`,
    `catchError`, тощо).

    **Коротко:**

    `HttpClient` - це зручний API для роботи з JSON у Angular. Він типізований,
    реактивний і не потребує ручного `JSON.parse()`.

    28. Як обробляти REST API-запити та помилки у сервісах Angular?

    #### Angular

    - REST-запити в Angular виконуються через `HttpClient`, а обробка помилок -
    через RxJS оператор `catchError`. Усе це зазвичай інкапсулюється в окремому
    сервісі, щоб компоненти залишалися “чистими”.

    | Крок | Опис |
    | ---- | ---------------------------------------------------------------- |
    | 1 | Створи сервіс (`@Injectable`) і підключи `HttpClient`. |
    | 2 | Використовуй методи `get()`, `post()`, `put()`, `delete()`. |
    | 3 | Обгорни запити у `pipe()` з `catchError()` для обробки помилок. |
    | 4 | Поверни типізований `Observable`, щоб компонент міг підписатися. |

    #### Приклад:

    ```TypeScript
    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 {
    return this.http.get(this.apiUrl).pipe(
    catchError(this.handleError)
    );
    }

    addProduct(product: Product): Observable {
    return this.http.post(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-застосунках?

    #### Angular

    Routing в Angular визначає, який компонент відображається при переході на певний
    URL. Він налаштовується через масив маршрутів і RouterModule (або
    `provideRouter` для standalone API).

    | Крок | Опис |
    | ---- | ----------------------------------------------------------------------------------------------- |
    | 1 | Створити масив маршрутів (`Routes[]`), де кожен об’єкт описує шлях і компонент. |
    | 2 | Імпортувати `RouterModule.forRoot(routes)` або використати `provideRouter(routes)` у `main.ts`. |
    | 3 | Додати `` у шаблон, щоб рендерити активний маршрут. |
    | 4 | Використовувати директиви `[routerLink]` для навігації. |

    #### Приклад (standalone routing):

    **app.routes.ts**

    ```TypeScript
    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**

    ```TypeScript
    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**

    ```HTML

    Home
    About

    ```

    #### Додаткові можливості:

    - **Route Guards** (`canActivate`, `canDeactivate`) - для захисту маршрутів.

    - **Lazy Loading** - динамічне підвантаження модулів або компонентів.

    - **Route Parameters** (`:id`) - для передачі динамічних значень у маршруті.

    **Коротко:**

    Маршрутизація в Angular конфігурується через масив `Routes` і `RouterModule` або
    provideRouter(). Компоненти рендеряться у ``, а переходи
    виконуються через `[routerLink]`.

    30. Як у Angular створити маршрут, який динамічно завантажує модуль лише під час доступу до нього (lazy loading)?

    #### Angular

    Так, у сучасному Angular (v16–20) це робиться через **_lazy loading_** з
    використанням динамічного `import()` у файлі маршрутизації. Це дозволяє не
    включати модуль у основний bundle, а завантажувати його лише при навігації.

    #### Приклад:

    ```TypeScript
    // 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-компонентів:

    ```TypeScript
    {
    path: 'dashboard',
    loadComponent: () =>
    import('./dashboard/dashboard.component').then(c => c.DashboardComponent),
    }
    ```

    **Коротко:**

    - `loadChildren` або `loadComponent` використовуються для lazy loading.
    - Модуль/компонент завантажується лише при першому переході на відповідний
    маршрут.
    - Це оптимізує стартову швидкість застосунку.

    31. Що таке RouterOutlet в Angular і як його використовують?

    #### Angular

    - `` - це директива, яка визначає місце у шаблоні, куди Angular
    підставляє компонент, що відповідає активному маршруту. Вона є “контейнером”
    для відображення контенту згідно з конфігурацією маршрутизатора.

    #### Приклад:

    ```HTML

    Home
    About

    ```

    ```TypeScript
    // 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 (захисники маршрутів)?

    #### Angular

    - Route guards - це сервіси, які контролюють доступ до маршрутів. Вони
    реалізують спеціальні інтерфейси (`CanActivate`, `CanDeactivate`, `CanLoad`,
    `CanMatch`, `Resolve`) і використовуються в конфігурації маршрутизатора.

    #### Приклад (CanActivate):

    ```TypeScript
    // auth.guard.ts
    import { CanActivateFn } from '@angular/router';

    export const authGuard: CanActivateFn = (route, state) => {
    const isLoggedIn = !!localStorage.getItem('token');
    return isLoggedIn; // або redirectUrl при потребі
    };
    ```

    ```TypeScript
    // 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 у маршрутизації?

    #### Angular

    `ActivatedRoute` дає доступ до інформації про поточний активний маршрут, включно
    з параметрами, query-параметрами, фрагментами URL і даними, переданими через
    `data`. Використовується всередині компонентів для отримання контексту маршруту.

    #### Приклад:

    ```TypeScript
    import { Component, OnInit } from '@angular/core';
    import { ActivatedRoute } from '@angular/router';

    @Component({
    selector: 'app-user',
    template: `

    User ID: {{ userId }}

    `
    })
    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 і як до них звертатися?

    #### Angular

    Параметри маршруту - це змінні частини URL, які визначаються у маршрутах та
    дозволяють передавати дані у компонент.

    #### Приклад:

    ```TypeScript
    // app.routes.ts
    import { Routes } from '@angular/router';
    import { UserComponent } from './user.component';

    export const routes: Routes = [
    { path: 'user/:id', component: UserComponent },
    ];
    ```

    ```TypeScript
    // user.component.ts
    import { Component, OnInit } from '@angular/core';
    import { ActivatedRoute } from '@angular/router';

    @Component({
    selector: 'app-user',
    template: `

    User ID: {{ userId }}

    `
    })
    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)?

    #### Angular

    Для цього використовують **_Route Resolver_** - сервіс, який реалізує інтерфейс
    `Resolve`. Angular чекає, поки resolver отримає дані, і передає їх у
    компонент через `ActivatedRoute.data`.

    #### Приклад:

    ```TypeScript
    // 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 {
    constructor(private userService: UserService) {}

    resolve() {
    return this.userService.getUser(); // може повертати Observable або Promise
    }
    }
    ```

    ```TypeScript
    // 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 }
    }
    ];
    ```

    ```TypeScript
    // user.component.ts
    ngOnInit() {
    this.route.data.subscribe(data => {
    console.log(data.userData); // доступ до preload-даних
    });
    }
    ```

    **Коротко:**

    - Resolver завантажує дані перед активацією маршруту.
    - Повертає `Observable`, `Promise` або просте значення.
    - Дані доступні через `ActivatedRoute.data` у компоненті.

    36. Як реалізувати lazy loading модулів або компонентів у Angular?

    #### Angular

    Lazy loading дозволяє завантажувати модулі чи компоненти тільки при переході на
    відповідний маршрут, щоб зменшити початковий розмір bundle.

    #### Приклад для модуля (loadChildren):

    ```TypeScript
    // app.routes.ts
    import { Routes } from '@angular/router';

    export const routes: Routes = [
    {
    path: 'admin',
    loadChildren: () =>
    import('./admin/admin.module').then(m => m.AdminModule),
    },
    ];
    ```

    #### Приклад для standalone-компонента (loadComponent):

    ```TypeScript
    {
    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.

    #### Angular

    - **Template-driven форми** будуються переважно у HTML-шаблоні за допомогою
    директив (ngModel, ngForm). Вони простіші, підходять для невеликих форм, але
    менш контрольовані - логіка зосереджена у шаблоні.

    - **Reactive форми** створюються в TypeScript-коді за допомогою FormGroup,
    FormControl, FormBuilder. Вони більш предиктивні, масштабовані й краще
    підходять для складних форм, валідації та тестування.

    #### Приклад:

    **Template-driven:**

    ```HTML

    ```

    **Reactive:**

    ```TypeScript
    form = new FormGroup({
    email: new FormControl('', { nonNullable: true, validators: [Validators.required] })
    });
    ```

    ```HTML

    ```

    **Коротко:**

    - Template-driven - декларативний підхід у шаблоні.
    - Reactive - імперативний підхід у коді, з повним контролем над станом форми.

    38. Як виконується валідація користувацького введення у формах Angular?

    #### Angular

    В Angular є вбудована, кастомна та асинхронна валідація. Валідація визначається
    або через HTML-атрибути (у Template-driven формах), або через `Validators` у
    Reactive формах.

    **Reactive форма з валідацією:**

    ```TypeScript
    form = new FormGroup({
    email: new FormControl('', {
    nonNullable: true,
    validators: [Validators.required, Validators.email]
    }),
    password: new FormControl('', {
    validators: [Validators.required, Validators.minLength(6)]
    })
    });
    ```

    **HTML:**

    ```HTML



    Invalid email

    ```

    **Кастомний валідатор (приклад):**

    ```TypeScript
    function forbiddenNameValidator(control: FormControl) {
    return control.value === 'admin' ? { forbiddenName: true } : null;
    }
    ```

    **Асинхронний валідатор (приклад):**

    ```TypeScript
    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?

    #### Angular

    - Для динамічної роботи з полями форми використовують `FormArray` або методи
    `addControl()` / `removeControl()` у `FormGroup`.
    - Це дозволяє створювати або видаляти поля на льоту - наприклад, динамічні
    списки чи масиви інпутів.

    **Приклад із FormArray:**

    ```TypeScript
    form = new FormGroup({
    users: new FormArray>([])
    });

    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:**

    ```HTML




    Remove


    Add User

    ```

    **Коротко:**

    - Використовуй FormArray для списків контролів.
    - Використовуй `addControl()` / `removeControl()` у `FormGroup` для динамічних
    окремих полів.

    40. Що таке FormGroup у Angular і як він працює?

    #### Angular

    - `FormGroup` - це об’єкт, який об’єднує кілька `FormControl` або навіть інших
    `FormGroup` у єдину структуру. Він дозволяє керувати станом, значеннями та
    валідацією всієї групи як одного цілого.

    **Ключові моменти:**

    - `FormGroup` зберігає набір контролів у вигляді об’єкта.

    - Дозволяє отримати стан (`valid`, `dirty`, `touched`) або значення (`value`)
    всієї групи.

    - Може мати групову валідацію (на рівні всієї форми).

    **Приклад:**

    ```TypeScript
    form = new FormGroup({ user: new FormGroup({ name: new FormControl('',
    Validators.required), email: new FormControl('', Validators.email) }) });
    ```

    **HTML:**

    ```HTML




    ```

    **Коротко:**

    - `FormGroup` = контейнер для контролів → дає змогу керувати групою полів як
    єдиним об’єктом (для валідації, оновлення, сабміту).

    41. Як створити власні (custom) валідатори у формах Angular?

    #### Angular

    Кастомний валідатор - це функція, яка приймає `FormControl` або
    `AbstractControl` і повертає об’єкт помилки `{ [key: string]: any }` або `null`,
    якщо помилок немає. Її можна використовувати в Reactive Forms або
    Template-driven.

    **Синхронний валідатор (приклад):**

    ```TypeScript
    import { AbstractControl, ValidationErrors } from '@angular/forms';

    export function forbiddenWordValidator(control: AbstractControl):
    ValidationErrors | null { const forbidden = control.value?.toLowerCase() ===
    'admin'; return forbidden ? { forbiddenWord: true } : null; }
    ```

    **Використання:**

    ```TypeScript
    form = new FormGroup({ username: new FormControl('', [forbiddenWordValidator])
    });
    ```

    **Асинхронний валідатор (приклад):**

    ```TypeScript
    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.

    #### Angular

    - `formArrayName` використовується в шаблоні для прив’язки до `FormArray`
    усередині Reactive Forms. Це дозволяє відображати та керувати динамічними
    наборами полів (наприклад, списком телефонів, тегів чи користувачів).

    **Приклад:**

    ```TypeScript
    form = new FormGroup({
    phones: new FormArray>([
    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:**

    ```HTML




    Remove

    Add Phone

    ```

    **Коротко:**

    - `formArrayName` - це директива для доступу до `FormArray` у шаблоні.
    - Кожен елемент масиву - окремий FormControl або FormGroup.
    - Використовується для динамічних форм, де кількість полів може змінюватися.

    43. Як відправити дані форми з Angular-додатку на бекенд-сервіс?

    #### Angular

    - У Angular форма зазвичай відправляється через сервіс, який використовує
    `HttpClient` для HTTP-запиту (`POST`, `PUT` тощо). Після сабміту зчитують
    `form.value`, перевіряють `form.valid` і викликають метод сервісу.

    **Приклад:**

    ```TypeScript
    // user.service.ts
    @Injectable({ providedIn: 'root' })
    export class UserService {
    constructor(private http: HttpClient) {}

    submitUser(data: any) {
    return this.http.post('/api/users', data);
    }
    }
    ```

    ```TypeScript
    // 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:**

    ```HTML



    Save

    ```

    **Коротко:**

    - Отримуєш `form.value`.
    - Перевіряєш `form.valid`.
    - Відправляєш через `HttpClient` (звичайно через сервіс).
    - Обробляєш відповідь у `subscribe()`.

    44. Що таке виявлення змін (change detection) і як його реалізує Angular?

    #### 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-застосунку?

    #### Angular

    1. **Використання Signals**

    Fine-grained reactivity → оновлюється тільки та частина DOM, яка залежить від
    сигналу.

    2. **Standalone Components**

    Менший бандл, швидший старт, немає модульних оверхедів.

    3. **Lazy loading та route-level code splitting**

    Завантажувати лише той код, який потрібен на даному маршруті.

    4. **OnPush (для legacy компонентів без сигналів)**

    Зменшує кількість викликів change detection у старих компонентах.

    5. **trackBy у ngFor**

    Запобігає перерендеру списків:

    ```HTML


  • ```

    6. **Оптимізація RxJS**

    takeUntil, shareReplay, уникання непотрібних сабскрипцій.

    7. **Async Pipe замість manual subscribe**

    Запобігає memory leaks і зайвим CD-циклами.

    8. **Оптимізація шаблону**

    Мінімізувати важкі обчислення у template (винести в getters або signals).

    9. **Preloading strategies**

    Оптимізує навігацію між маршрутами (наприклад, PreloadAllModules або custom).

    10. **Build-level оптимізації**

    ng build --configuration production

    minification, treeshaking, локальні i18n-файли

    image optimization (WebP/AVIF)

    46. Що таке зони (Zones) в Angular і яку роль вони відіграють?

    #### Angular

    Зони (Zone.js) - це механізм, який перехоплює всі асинхронні операції (події,
    таймери, проміси) і автоматично запускає change detection після їх виконання.

    #### Навіщо Angular використовує зони:

    Щоб не писати вручну, коли саме оновлювати UI.

    Будь-який async виклик → Angular знає, що могли змінитися дані → оновлює вʼю.

    #### Як це працює:

    Zone.js патчить setTimeout, XHR, addEventListener тощо.

    Після завершення async-дії зона викликає Angular change detection.

    #### У сучасному Angular 16–20+:

    Зони більше не потрібні для реактивного рендерингу (Signals).

    Є режим Noop Zone / Zoneless, де Angular оновлює UI точково без глобального CD.

    **Коротко:**

    Zones - старий механізм для автозапуску change detection. У нових версіях
    Angular його замінює сигнал-базована реактивність.

    47. Як у Angular налаштувати SSR за допомогою Angular Universal

    #### Angular

    1. Увімкнення SSR

    ```bash
    ng add @angular/ssr
    ```

    Автоматично створюється сервер, SSR bootstrap і hydration.

    2. Bootstrap

    **Browser**

    ```TypeScript
    bootstrapApplication(AppComponent, appConfig);
    ```

    **Server**

    ```TypeScript
    export default () =>
    bootstrapApplication(AppComponent, appConfig);
    ```

    3. Hydration

    ```TypeScript
    provideClientHydration()
    ```

    Angular підхоплює HTML, а не рендерить заново.

    4. Сервер (Node / Express)

    ```TypeScript
    renderApplication(bootstrap, { url, document })
    ```

    5. SSR-safe код

    ```TypeScript
    isPlatformBrowser(PLATFORM_ID)
    ```

    без window, document напряму

    6. Дані

    - API викликаємо **на сервері**

    - Передаємо в браузер через `TransferState`

    - Signals працюють з SSR без проблем

    7. Коли використовувати

    SEO, швидкий FCP

    8. Коли не використовувати

    Адмінки, real-time dashboards

    48. У чому різниця між Ahead-of-Time (AOT) та Just-in-Time (JIT) компіляцією в Angular і коли використовується кожна з них?

    #### Angular

    #### AOT (Ahead-of-Time)

    - Компіляція під час білду

    - Angular-шаблони → JS до запуску в браузері

    - Швидший старт

    - Кращий performance

    - Менший бандл

    - Ранні compile-time помилки

    - Безпека (немає runtime compiler)

    **Default у production**

    #### JIT (Just-in-Time)

    - Компіляція в браузері під час виконання

    - Потрібен Angular compiler у runtime

    - Повільніший старт

    - Більший бандл

    - Зручно для dev (швидкий rebuild)

    **Використовується в dev-режимі**

    49. Опишіть декоратори, доступні в Angular.

    #### Angular

    Angular використовує TypeScript-декоратори для опису метаданих компонентів,
    директив, сервісів та DI.

    1. Класові декоратори

    **@Component**

    Описує UI-компонент.

    ```TypeScript
    @Component({ selector: 'app-user', standalone: true, template: `{{ name }}` })
    export class UserComponent { name = 'Viktor'; }
    ```

    **@Directive**

    Створює кастомну директиву (attribute або structural).

    ```TypeScript
    @Directive({ selector: '[appHighlight]' }) export class HighlightDirective {}
    ```

    **@Pipe**

    Створює pipe для трансформації даних у шаблонах.

    ```TypeScript
    @Pipe({ name: 'uppercase' }) export class UppercasePipe { transform(value:
    string) { return value.toUpperCase(); } }
    ```

    **@Injectable**

    Позначає клас як сервіс для DI.

    ```TypeScript
    @Injectable({ providedIn: 'root' }) export class UserService {}
    ```

    2. Property decorators (взаємодія з шаблоном)

    **@Input**

    Передача даних у компонент.

    ```TypeScript
    @Input() title!: string;
    ```

    **@Output**

    Передача подій з компонента.

    ```TypeScript
    @Output() saved = new EventEmitter();
    ```

    **@HostBinding**

    Байндінг до властивостей host-елемента.

    ```TypeScript
    @HostBinding('class.active') isActive = true;
    ```

    **@HostListener**

    Підписка на події host-елемента.

    ```TypeScript
    @HostListener('click') onClick() {}
    ```

    3. Dependency Injection decorators

    **@Inject**

    Явна інʼєкція залежності.

    ```TypeScript
    constructor(@Inject(API_URL) private apiUrl: string) {}
    ```

    **@Optional**

    Залежність може бути відсутня.

    ```TypeScript
    constructor(@Optional() private logger?: LoggerService) {}
    ```

    **@Self**, **@SkipSelf**

    Контроль області пошуку залежностей.

    ```TypeScript
    constructor(@Self() private control: NgControl) {}
    ```

    4. View / Content decorators

    **@ViewChild** / **@ViewChildren**

    Доступ до елементів власного шаблону.

    ```TypeScript
    @ViewChild('input') input!: ElementRef;
    ```

    **@ContentChild** / **@ContentChildren**

    Доступ до проєктованого контенту (ng-content).

    ```TypeScript
    @ContentChild(TemplateRef) tpl!: TemplateRef;
    ```

    5. Стан у Angular 20+

    - Декоратори **все ще підтримуються**

    - Але часто замінюються:

    - `inject()` замість constructor DI

    - Signals замість `@Input` + `ngOnChanges`

    - **Standalone API не скасовує декоратори**, лише спрощує архітектуру

    50. Як реалізувати анімаційні переходи в Angular-додатку?

    #### Angular

    Angular має вбудовану систему анімацій через `@angular/animations`. У Angular
    20+ використовую `provideAnimations()` у конфігурації:

    ```TypeScript
    // app.config.ts
    import { provideAnimations } from '@angular/platform-browser/animations';

    export const appConfig: ApplicationConfig = {
    providers: [provideAnimations()]
    };
    ```

    1. Анімація станів компонента

    ```TypeScript
    typescriptimport { trigger, state, style, transition, animate } from '@angular/animations';

    @Component({
    template: `

    Content
    `,
    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);
    }
    ```

    2. Анімація роутів

    ```TypeScript
    // app.component.ts
    @Component({
    template: `




    `,
    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 })
    ])
    ])
    ]
    })
    ```

    3. Анімація списків (stagger)

    ```TypeScript
    animations: [
    trigger('listAnimation', [
    transition('* => *', [
    query(':enter', [
    style({ opacity: 0, transform: 'translateY(-20px)' }),
    stagger(100, [
    animate('300ms', style({ opacity: 1, transform: 'translateY(0)' }))
    ])
    ], { optional: true })
    ])
    ])
    ]
    ```

    #### Best Practices

    - Використовую `transform` та `opacity` для GPU-acceleration
    - `:enter` / `:leave` для появи/зникнення елементів
    - Створюю reusable анімації через `animation()` та `useAnimation()`
    - Відстежую події: `(@trigger.done)="onAnimationDone($event)"`

    51. Як створюються власні директиви в Angular?

    #### Angular

    1. Типи директив

    - **Attribute directive** - змінює поведінку або вигляд елемента приклад:
    highlight, tooltip

    - **Structural directive** - змінює структуру DOM приклад: *ngIf, *ngFor

    2. Attribute directive

    **Приклад: директива підсвічування**

    ```TypeScript
    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);
    }
    }
    ```

    **Використання**

    ```HTML

    Highlighted text


    ```

    3. Structural directive

    **Приклад: кастомний `*appIf`**

    ```TypeScript
    import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

    @Directive({
    selector: '[appIf]',
    standalone: true,
    })
    export class AppIfDirective {
    constructor(
    private tpl: TemplateRef,
    private vcr: ViewContainerRef
    ) {}

    @Input()
    set appIf(condition: boolean) {
    this.vcr.clear();
    if (condition) {
    this.vcr.createEmbeddedView(this.tpl);
    }
    }
    }
    ```

    **Використання**

    ```HTML

    Visible content

    ```

    4. Best practices

    - Використовуйте standalone директиви

    - Для реактивності - signals + effect

    - Уникайте прямої роботи з DOM (краще Renderer2, якщо потрібно)

    - Мінімізуйте side-effects у constructor

    - Structural директиви завжди працюють через TemplateRef + ViewContainerRef

    **Коротко**

    Власні директиви створюються через `@Directive`, бувають attribute та structural
    і в Angular 20+ зазвичай є standalone з реактивністю на signals.

    52. Чи можете ви пояснити використання директив ngClass та ngStyle?

    #### Angular

    Використання директив `ngClass` та `ngStyle` в Angular

    #### `ngClass`

    Директива `ngClass` використовується для **динамічного додавання або видалення
    CSS-класів** на елементі.

    #### Основні форми використання

    1. Обʼєкт (найпоширеніше)

    ```HTML


    ```

    2. Масив

    ```HTML


    ```

    3. Рядок

    ```HTML


    ```

    #### Best practices

    - Використовуйте для умовної стилізації

    - Працює ефективно з `ChangeDetectionStrategy.OnPush`

    - Добре поєднується з signals

    ```TypeScript
    isActive = signal(true);
    ```

    #### `ngStyle`

    Директива `ngStyle` використовується для динамічного задання inline-стилів.

    #### Приклад

    ```HTML


    ```

    ```TypeScript
    textColor = 'red';
    fontSize = 16;
    ```

    #### Best practices

    - Використовуйте лише коли стилі не можна описати класами

    - Уникайте великої кількості inline-стилів (performance + maintainability)

    #### Angular 20+ рекомендації

    - Віддавайте перевагу `ngClass`

    - Для складної логіки - computed signals

    - Для дизайн-систем - класи + CSS variables

    #### Коротке резюме

    - `ngClass` - для керування CSS-класами,
    - `ngStyle` - для динамічних inline-стилів.

    53. Як ви взаємодієте з DOM безпосередньо за допомогою директив?

    #### Angular

    Взаємодія з DOM за допомогою директив в Angular

    #### Основні способи

    1. `ElementRef` (обмежено)

    Дає доступ до нативного DOM-елемента.

    ```TypeScript
    constructor(private el: ElementRef) {
    this.el.nativeElement.focus();
    }
    ```

    **Недолік:** прямий доступ до DOM Не рекомендовано для SSR та безпеки

    2. Renderer2 (рекомендовано)

    Абстракція над DOM - безпечна та SSR-friendly.

    ```TypeScript
    constructor(
    private el: ElementRef,
    private renderer: Renderer2
    ) {}

    ngOnInit() {
    this.renderer.setStyle(
    this.el.nativeElement,
    'background-color',
    'yellow'
    );
    }
    ```

    - Працює з SSR
    - Безпечний (XSS)
    - Кросплатформений

    3. @HostBinding Біндінг до властивостей host-елемента.

    ```TypeScript
    @HostBinding('class.active') isActive = true;
    ```

    - Чисто
    - Без прямого DOM-доступу

    4. @HostListener

    Підписка на події host-елемента.

    ```TypeScript
    @HostListener('mouseenter')
    onHover() {
    this.isActive = true;
    }
    ```

    #### Angular 20+ підхід (best practice)

    - Уникати nativeElement напряму

    - Використовувати Renderer2

    - Для стилів і класів - `@HostBinding`

    - Для подій - `@HostListener`

    - Для реактивності - signals + effect

    - Перевіряти платформу (isPlatformBrowser) при роботі з DOM API

    **Коротко**

    У Angular взаємодія з DOM у директивах має відбуватись через Renderer2 або
    host-декоратори, а не через прямий доступ до nativeElement.

    54. Коли слід використовувати Renderer2 і які його переваги?

    #### Angular

    Коли слід використовувати `Renderer2` і які його переваги

    #### Коли використовувати `Renderer2`

    Використовуйте `Renderer2`, коли потрібно:

    - Динамічно змінювати **стилі**, **класи**, **атрибути**
    - Додавати або видаляти **DOM-елементи**
    - Працювати з **подіями** зсередини директив
    - Забезпечити **SSR-сумісність**
    - Уникнути **XSS-ризиків**
    - Писати **кросплатформений** код (browser / server / web workers)

    ## Приклад використання

    ```TypeScript
    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'
    );
    }
    }
    ```

    #### Переваги `Renderer2`

    1. Безпека

    - Захищає від XSS
    - Не дозволяє небезпечні DOM-операції напряму

    2. SSR-friendly

    - Працює коректно при Server-Side Rendering
    - Не ламається через відсутність window / document

    3. Абстракція над DOM

    - Angular вирішує як саме застосувати зміни
    - Підтримує різні платформи

    4. Краща підтримка Angular lifecycle

    - Інтегрується з change detection
    - Менше побічних ефектів

    #### Коли НЕ варто використовувати `Renderer2`

    - Для простого керування класами → краще `ngClass`
    - Для обробки подій → краще `@HostListener`
    - Для стилів → `@HostBinding`
    - Для читання значень (read-only) → допустимо `ElementRef`

    #### Angular 20+ рекомендації

    - Не використовувати nativeElement напряму
    - Renderer2 - стандарт для директив
    - Поєднувати з signals для реактивності
    - Перевіряти платформу при складних DOM-операціях

    **Коротко**

    Renderer2 - це безпечний, SSR-сумісний та кросплатформений спосіб взаємодії з
    DOM, який слід використовувати замість прямого доступу до nativeElement.

    55. Як створити власний канал в Angular?

    #### Angular

    Як створити власний Pipe (канал) в Angular

    1. Створення pipe

    Приклад: простий pipe для форматування імені

    ```TypeScript
    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);
    }
    }
    ```

    2. Використання в шаблоні

    ```HTML

    {{ 'angular' | capitalize }}


    ```

    3. Pure vs Impure pipes

    **Pure pipe (за замовчуванням)**

    - Викликається лише при зміні input

    - Краща продуктивність

    ```TypeScript
    @Pipe({ name: 'myPipe', pure: true })
    ```

    **Impure pipe**

    - Викликається на кожен change detection

    - Використовувати обережно

    ```TypeScript
    @Pipe({ name: 'myPipe', pure: false })
    ```

    4. Pipe з параметрами

    ```TypeScript
    @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;
    }
    }
    ```

    ```HTML

    {{ text | truncate:20 }}


    ```

    5. Best practices (Angular 20+)

    - Використовуйте standalone pipes

    - Тримайте pipes pure

    - Без side-effects

    - Не використовуйте pipes для складної бізнес-логіки

    - Для реактивних сценаріїв - signals або computed values

    **Коротко**

    Pipe в Angular створюється через @Pipe, реалізує PipeTransform і
    використовується для декларативної трансформації даних у шаблоні.

    56. Опишіть чисті та нечисті канали.

    #### Angular

    Чисті (Pure) та нечисті (Impure) канали в Angular

    #### Чисті канали (Pure Pipes)

    #### Характеристики

    - Викликаються **лише тоді**, коли змінюється **посилання на input**
    - Значення кешується Angular
    - Висока продуктивність
    - Повністю детерміновані (без side-effects)

    ```TypeScript
    @Pipe({
    name: 'uppercase',
    standalone: true,
    pure: true, // default
    })
    export class UppercasePipe {}
    ```

    #### Приклад використання

    ```HTML

    {{ name | uppercase }}


    ```

    #### Коли використовувати

    - Форматування рядків

    - Обчислення на основі immutable-даних

    - 99% кейсів

    #### Нечисті канали (Impure Pipes)

    #### Характеристики

    - Викликаються на кожен цикл change detection
    - Не кешуються
    - Значно гірша продуктивність
    - Можуть мати side-effects (небажано)

    ```TypeScript
    Копіювати код
    @Pipe({
    name: 'timeAgo',
    standalone: true,
    pure: false,
    })
    export class TimeAgoPipe {}
    ```

    #### Приклад використання

    ```HTML

    {{ timestamp | timeAgo }}


    ```

    #### Angular 20+ рекомендації

    - Завжди починайте з pure pipe

    - Уникайте impure pipes

    - Для реактивних сценаріїв:

    - Signals (`computed`)

    - RxJS + `async`

    - Impure pipe - останній варіант

    **Коротко**

    Pure pipes - швидкі та безпечні, Impure pipes - повільні й використовуються лише
    у виняткових випадках.

    57. Що таке асинхронний канал і як він використовується?

    #### Angular

    Асинхронний канал (`AsyncPipe`) в Angular

    #### Що таке `async` pipe

    `async` - це **вбудований Angular pipe**, який:

    - підписується на `Observable` або `Promise`
    - автоматично оновлює шаблон при нових значеннях
    - **сам відписуєт