Модуль 15

Angular Modules та їх роль у проєкті

Ось твій урок про Angular Modules, згенерований у стилі CS50: енергійно, з аналогіями та акцентом на розумінні "навіщо".


🎓 УРОК: Angular Modules — Архітектура вашого застосунку

Привіт, друзі! Мене звати [Твоє Ім'я], і це... CS50: Angular Edition.

Сьогодні ми не просто пишемо код. Сьогодні ми стаємо архітекторами.

1. 🔥 Вступ: Проблема «Звалища»

Уявіть собі ситуацію. Ви вирішили відкрити супермаркет. У вас є тисячі товарів: молоко, телевізори, шкарпетки, бензопили, яблука.

І ось ви скидаєте це все у одну величезну купу посеред зали.

Я ставлю вам просте запитання: Як швидко покупець знайде там пакет молока? І друге питання: Що станеться, коли ви привезете ще 5000 нових товарів?

Хаос. Це буде повний хаос.

У світі Angular, якщо ми не використовуємо Модулі (або сучасну альтернативу — Standalone API, про яку згодом), наш додаток перетворюється на таку саму купу. Компоненти, пайпи, директиви — все валяється в одному місці.

Навіщо нам ця тема? 1. Порядок: Щоб відділ "Молочка" був окремо від відділу "Електроніка". 2. Продуктивність: Щоб не завантажувати клієнту весь супермаркет, коли він прийшов лише за хлібом (це називається Lazy Loading). 3. Співпраця: Щоб один розробник міг працювати над "Адмінкою", не ламаючи "Кошик покупця".

Сьогодні ми навчимося розкладати цей хаос по поличках.


2. 🧠 Теоретична база: Що таке NgModule?

Давайте заглянемо під капот.

У Angular Модуль (NgModule) — це, по суті, коробка з етикеткою. Це контейнер, який гуртує пов’язані частини коду (компоненти, сервіси тощо) в одну функціональну одиницю.

В кожному модулі є спеціальний декоратор @NgModule(), який містить "інструкцію до коробки".

4 кити, на яких тримається модуль (це треба запам'ятати):

  1. declarations (Оголошення):

    • Аналогія: Це список працівників вашого відділу.
    • Тут ви перераховуєте компоненти, які "живуть" у цьому модулі. Якщо ви не оголосите компонент тут, Angular не знатиме про його існування.
  2. imports (Імпорт):

    • Аналогія: Це інструменти, які ваш відділ позичає в інших відділів.
    • Якщо вашому компоненту потрібні форми, ви імпортуєте FormsModule. Якщо потрібен HTTP — HttpClientModule.
  3. exports (Експорт):

    • Аналогія: Це вітрина вашого відділу.
    • За замовчуванням, усе, що є в модулі — приватне (інкапсульоване). Якщо ви хочете, щоб інші частини програми могли використовувати ваш крутий ButtonComponent, ви мусите додати його в exports.
  4. providers (Провайдери):

    • Аналогія: Служба підтримки.
    • Тут підключаються сервіси. (Хоча в сучасному Angular ми частіше використовуємо providedIn: 'root', знати про це поле все ще важливо для легасі-коду).

💡 Інтуїтивне розуміння: Модуль — це як LEGO-набір. У ньому є інструкція (declarations), детальки з інших наборів (imports) і готова модель, яку ви можете поставити на стіл (exports).


3. 🧪 Приклади: Від простого до реального

Приклад 1: Кореневий модуль (AppModule)

Кожен Angular-додаток (до ери Standalone) починається звідси.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent // 1. Оголошуємо наш головний компонент
  ],
  imports: [
    BrowserModule // 2. Імпортуємо базові інструменти браузера
  ],
  providers: [],
  bootstrap: [AppComponent] // 3. Стартуємо додаток з цього компонента
})
export class AppModule { }

Питання до вас: Чому AppComponent є в declarations, але немає в exports? (Пауза для роздумів) Відповідь: Тому що це кореневий модуль! Його ніхто не імпортує, він сам запускає додаток через bootstrap.


Приклад 2: Feature Module (Модуль функціоналу)

Уявіть, що ми робимо сайт з продажу піци. Створимо PizzaModule.

@NgModule({
  declarations: [
    PizzaListComponent,   // Список піц
    PizzaCardComponent,   // Картка однієї піци
    SecretSauceComponent  // Секретний компонент (тільки для внутрішнього використання!)
  ],
  imports: [
    CommonModule // Потрібен для *ngIf та *ngFor
  ],
  exports: [
    PizzaListComponent // Ми дозволяємо іншим використовувати ТІЛЬКИ список
    // PizzaCardComponent і SecretSauceComponent залишаються приватними!
  ]
})
export class PizzaModule { }

Що тут відбулося? Ми створили "чорну скриньку". Зовнішній світ (наприклад, AppModule) може використовувати <app-pizza-list>, але він поняття не має про існування <app-secret-sauce>. Це і є інкапсуляція.


Приклад 3: Підключення модулів

Тепер AppModule хоче використовувати нашу піцу.

import { PizzaModule } from './pizza/pizza.module';

@NgModule({
  imports: [
    BrowserModule,
    PizzaModule // <--- Ми підключили модуль піци сюди
  ],
  // ...
})
export class AppModule { }

Питання на засипку: Якщо я в AppComponent (який живе в AppModule) спробую використати тег <app-secret-sauce>, що скаже Angular?

Результат: Він видасть помилку! ❌ "Component is not part of any NgModule or the module has not been imported..." Чому? Тому що PizzaModule не додав SecretSauceComponent у свій масив exports.


4. 🛠 Практична частина

А тепер — руки на клавіатуру! Ось ваші завдання.

🔹 Завдання 1: Створення бази Створіть новий модуль SharedModule. Оголосіть у ньому простий компонент HeaderComponent.

🔹 Завдання 2: Експорт/Імпорт Спробуйте використати <app-header> у вашому AppComponent. Очікування: Ви отримаєте помилку. Дія: Додайте HeaderComponent в exports вашого SharedModule, а сам SharedModule імпортуйте в AppModule. Переконайтеся, що запрацювало.

🔹 Завдання 3: "Приватна вечірка" Створіть у SharedModule компонент InternalHelperComponent. Використайте його всередині HeaderComponent. Але не експортуйте його. Перевірте, чи працює Header? (Має працювати). Спробуйте вставити InternalHelper в AppComponent. (Має впасти).

🔹 Завдання 4: Міні-кейс "User Profile" Створіть модуль UserModule. У ньому мають бути: 1. LoginComponent (доступний зовні). 2. SettingsComponent (доступний тільки для LoginComponent або інших внутрішніх частин). 3. Підключіть UserModule до головного модуля.

🔹 Питання "А що, якщо..." Що станеться, якщо ми імпортуємо BrowserModule двічі (в AppModule і в UserModule)? (Спойлер: Angular сваритиметься. У дочірніх модулях треба використовувати CommonModule, а BrowserModule — тільки в головному).


5. 💡 Мислення як у розробника

Як думає Senior Developer, коли створює модулі?

  1. Принцип єдиної відповідальності (SRP): Новачок кидає все в AppModule. Профі створює окремі модулі: AuthModule (авторизація), ProductModule (товари), SharedModule (кнопки, інпути, які юзають всі).

  2. Уникнення "Shared Hell": Помилка новачків — створити один гігантський SharedModule і пхати туди все підряд. З часом він стає таким важким, що гальмує весь додаток. Порада: Тримайте SharedModule легким. Тільки те, що справді використовується скрізь.

  3. Standalone Components (Сучасний погляд): Ви можете спитати: "Девід, я чув, що в Angular 15+ модулі необов'язкові?" Так! Це правда. Standalone Components дозволяють компонентам жити без модулів. АЛЕ:

    • 90% існуючих проектів написані на модулях.
    • Принципи групування та інкапсуляції, які ви вивчили сьогодні, однакові і там, і там.
    • Розуміння модулів — це база для розуміння Архітектури.

6. 🧩 Підсумок

Отже, що ми сьогодні зробили? Ми перетворили звалище коду на структурований склад.

  • Ми знаємо, що declarations — це наші "працівники".
  • Ми знаємо, що imports — це наші "інструменти".
  • Ми знаємо, що exports — це наш "публічний інтерфейс".

Тепер ви вмієте: ✅ Розбивати додаток на логічні частини. ✅ Ховати внутрішню логіку (інкапсуляція). ✅ Перевикористовувати код між різними частинами програми.

Що далі? Уявіть, що у вас 50 модулів. Завантажувати їх усі одразу при запуску сайту — це довго. На наступному уроці ми поговоримо про Lazy Loading (Ліниве завантаження) через Routing. Ми навчимо Angular завантажувати модуль "Адмінка" тільки тоді, коли користувач справді туди клікне.

Це був CS50. Щасти вам з кодом!