Tìm hiểu về Angular Module và tầm vực ảnh hưởng (scope).

Bài viết dịch từ, bài nguyên tác: Understanding Angular modules (NgModule) and their scopes của tác giả Cyrille Tuzi có chỉnh sửa bổ sung 1 số ý .

Mình thấy tác giả có 2 bài khá hay viết về module trong angular nen mình xin mạn phép chọn trước 1 bài để dịch.

NgModule là cấu trúc cơ bản đầu tiên chúng ta sẽ phải đối mặt khi viết dự án bằng Angular. Nó cũng là thứ phức tạp và khá rối rắm vì tầm vực (scope) ảnh hưởng khác nhau khi khai báo module theo các cách khác nhau. Mặc dù đội ngũ phát triển của Angular đã hoàn thiện toàn bộ bảng FAQ về module, nhưng chúng còn khá rối rắm để có thể dạy cho người khác.

Tại sao phải dùng ngModule?

Angular module là một trong những sự thay đổi về mặt concept của Angular 2+ so với AngularJS. Module trong Angular sẽ được dùng để kết nối, gom các thành phần của Angular (component, directive, pipe, service…) lại với nhau. Điểm khác biệt chính của angular module so với thế hệ tiền nhiệm theo mình nghĩ chính là việc nó quản lý chặt chẽ những tài nguyên nào được public ra cho module khác sử dụng (component, directive, pipe) bằng cách khai báo thông qua thuộc tính exports và những tài nguyên nào chỉ sử dụng nội bộ bằng cách chỉ khai báo thông qua declarations.

Có 2 thuộc tính chủ yếu của Angular Module mình cần để tâm khi khai báo mới là:

  • declarationsDùng để khai báo những thành phần chúng ta sẽ dùng ở trên template (thường chủ yếu là các component, directive và pipe).
  • providersDùng để khai báo các service dùng trong toàn bộ các module của con (dù có lazy loading module hay không vẫn available).

NOTE: Kể từ version của Angular 6, các service không cần đăng kí ở trong module mà chúng ta có thể sử dụng từ khóa providedIn‘root’ để xác định tầm ảnh hưởng của service.

Tầm vực của các thành phần trong Angular Module

Chúng ta sẽ bắt đầu nhầm lẫn khi tầm vực của component/directive/pipe (mình gọi tắt chung là component) sẽ không giống với service.

Tầm vực của những component (được khai báo trong thuộc tính declarations) chỉ có thể sử dụng được trong nội bộ module đó.

Tầm vực của những service (được khai báo trong thuộc tính providers) có thể sử dụng trong toàn bộ dự án. Điều đó có nghĩa là đối với feature module (không lazy load) các service chỉ cần được khai báo ở bất kỳ module con nào, khi import vào module chính sẽ public single instance trên toàn bộ module con và module chính.

Ví dụ: https://stackblitz.com/edit/angular-6c7pix

Trong ví dụ, mình có 1 accountService, và 2 module userModule và orderModule được inject vào AppModule. Bạn có thể chỉ cần khai báo đăng ký providers cho accountService ở bất kỳ feature module nào (hoặc có thể khai báo vào appModule) đều có thể sử dụng service đó ở bất kì module nào.

Vậy câu hỏi của chúng ta là:

Khi nào nên import module nào?

Ở trên chúng ta đã làm rõ được khác biệt về scope của component và service trong module rồi. Trong Angular chúng ta có khá nhiều module trong 1 ứng dụng vậy chúng ta phải import các module đó theo cách như thế nào?

  • Nếu module đó được import để sử dụng các component, chúng ta sẽ phải import vào các module nào chúng ta muốn sử dụng vì scope của component chỉ có tầm vực locally.
  • Nếu module đó được import để sử dụng các service, chúng ta chỉ nên import nó 1 lần trong module chính.

Nếu không, chúng ta có thể sẽ gặp phải lổi không tìm thấy component vì chúng ta chưa import module đó vào.

HOẶC khi import module chứa service nhiều hơn một lần, chúng ta có thể gây ra nhiều hệ lụy khi dùng chung với module lazy loading.

Nói về tính chất của service trong lazy loading module, chúng ta sẽ xem phần sau ^^.

Sau đây tác giả tóm tắt 1 số module built-in của angular và cách import chúng trong dự án angular của chúng ta.

Cách import các Angular module

Đây là bản tóm tắt:

Module được import khi nào bạn muốn cũng được (tức là nhiều lần)

  • CommonModule – Chứa tất cả các thành phần của Angular (structure directive như *ngIf, *ngFor..): Module này ta sẽ import ở nhiều module con nhưng không import vào module AppModule vì nó là 1 phần của BrowserModule.
  • FormsModule / ReactiveFormsModule
  • MatXModule cũng nhưng các module UI theme.
  • ….
  • Bất kỳ module nào cung cấp component, directive, pipe.

Module chỉ được import 1 lần duy nhất

  • HttpClientModule.
  • BrowserAnimationsModule hay NoopAnimationsModule
  • Những module nào chỉ cung cấp services: Lý do chúng ta sẽ nói khi qua vấn đề về lazyloading.

Module phức hợp?

Nó có thể phức tạp hơn khi có thể có module vừa chứa cả components và services?

Có 1 thí dụ trong số đó chính là RouterModule. Nó cung cấp component router-outlet, directive routerLink và service như ActivatedRoute, Routers.

Trong trường hợp này, RouterModule khi import trong AppModule sẽ khác biệt đôi chút so với import trong các feature module.

Đối với AppModule

Untitled

Đối với các feature module

Untitled.png

Vì sao? Vì khi lần đầu trong AppModule, forRoot sẽ cung cấp các component, directive và cả service. Trong khi ở những feature module, forChild chỉ cung cấp các component, directives mà không có service.

Lazy load module

Cuối cùng, với những module được lazyload (theo cú pháp như hình)

Untitled.png

Vì nó sẽ khác đôi chút lúc đóng gói module, và nó chỉ load on demand nên những lazy load module sẽ không bao gồm global scope của app.

Đối với component, sẽ không có thay đổi, chúng ta vẫn import module như ở trên (tức là import commonModule và các module cung cấp components).

Đối với service, sẽ có chút khác biệt

  • Chúng ta vẫn có thể thấy được những service được provide ở AppModule.
  • Tuy nhiên những service chúng ta provide trong lazy load module sẽ có tính chất: (1) Nó chỉ available trong lazy load module trở xuống, các module bên ngoài sẽ không thấy và (2) Nếu lazy load module provide service giống với AppModule thì nó sẽ tao ra instance mới.

Lí do cho những khác biệt?

Vì sao có sự khác biệt giữa tầm vực của các component và service?

Tác giả bài viết có đưa ra giải thích như sau;

  • Service thì giống như 1 class ES6, nó được import rồi export nên nó sẽ không thể xung đột và singleton instance là cái bạn mong muốn.
  • Trong khi với component, giả sử có 2 thư viện cùng cung cấp component có cùng HTML tag với nhau thì sẽ khiến đụng độ, vậy nên component sẽ không thể global.

Mình chưa hiểu rõ lắm nên cũng k extend ra được nhiều.

Hết


Tái bút: Mọi người có thể tham khảo thêm bài

Architecture in Angular projects

Give some ideas