Tìm hiểu Angular Router
- 10-01-2024
- Toanngo92
- 0 Comments
Trước kia, khi bạn truy cập một trang web, thông thường server sẽ gửi toàn bộ nội dung của trang đó cho bạn xem. Khi bạn di chuyển từ trang này sang trang khác, ví dụ như từ trang chủ của một cửa hàng online sang trang chứa thông tin về giày dép, server sẽ lại gửi lại toàn bộ nội dung của trang đó cho bạn, từ đầu đến phần thông tin bạn muốn xem. Điều này khiến trang web phải tải lại hoàn toàn mỗi khi bạn click vào một liên kết, có thể thấy được trên thanh trình duyệt bằng biểu tượng xoay xoay hoặc hiện sự chờ đợi.
Nhưng gần đây, một khái niệm mới được áp dụng là Single Page Application (SPA). Trong loại ứng dụng này, mọi thứ cần thiết để hiển thị trên trang web được gói gọn trong một file JS. Khi bạn tương tác trong ứng dụng này, thay vì tải lại toàn bộ trang, đa số thời gian sẽ gọi các yêu cầu đến server thông qua API để lấy dữ liệu mới và cập nhật chỉ phần nội dung cần thay đổi. Điều này làm cho trải nghiệm người dùng trở nên mượt mà hơn, không cần phải chờ đợi lâu mỗi khi chuyển từ trang này sang trang khác.
Vậy làm sao để ứng dụng Angular biết được bạn muốn xem phần nào và làm thế nào để thực hiện điều đó? Đó chính là vai trò của Angular Router.
Khi bạn sử dụng ứng dụng, bạn muốn di chuyển giữa các trang khác nhau mà người phát triển đã thiết lập sẵn. Ví dụ như khi bạn vào web888.com, bạn sẽ thấy một danh sách các bài viết. Bạn click vào một bài viết, và bạn muốn chia sẻ liên kết đến bài viết đó với người khác. Bạn sẽ copy đường link có dạng web888.com/tim-hieu-angular-router và gửi đi.
Khi ai đó mở liên kết đó, ứng dụng phải hiển thị đúng bài viết mà bạn đã xem. Điều này cần sự cấu hình đặc biệt trong ứng dụng Angular để khi bạn mở liên kết như vậy, nó hiển thị đúng nội dung bạn muốn xem, không phải trang chủ hoặc bài viết khác.
Mục lục
Khởi tạo một ứng dụng có hỗ trợ Angular Router
Để tạo một ứng dụng có thể sử dụng router trong Angular bằng Command Line Interface (CLI), bạn chỉ cần chạy một dòng lệnh nhỏ như sau:
ng new day27-routing --routing
Lưu ý một vài điều quan trọng:
day27-routing
: Đây là tên của ứng dụng bạn muốn tạo. Bạn có thể đặt tên tuỳ ý cho ứng dụng của mình.--routing
: Đây là một phần mô tả cho CLI biết rằng bạn muốn tạo ứng dụng với Router đi kèm.
Khi bạn chạy lệnh này, CLI sẽ hỏi bạn một số câu hỏi khác như lựa chọn kiểu giao diện bạn muốn sử dụng, ví dụ như SCSS hoặc CSS. Bạn có thể chọn theo sở thích cá nhân của mình. Ví dụ, nếu bạn chọn SCSS, chỉ cần chọn SCSS khi được hỏi và tiếp tục quá trình tạo ứng dụng của mình.
Thêm components để thực hiện cho việc điều hướng (routing)
Để thực hiện việc điều hướng trong ứng dụng của bạn, bạn cần tạo các thành phần để hiển thị danh sách các bài viết và để hiển thị chi tiết từng bài viết.
Để tạo một thành phần mới sử dụng CLI, bạn có thể sử dụng các dòng lệnh sau đây. Ví dụ, để tạo thành phần để hiển thị danh sách các bài viết, bạn có thể chạy lệnh sau với “article-list” là tên của thành phần:
ng generate component article-list
Đây sẽ tạo ra một thành phần có tên là “article-list” để bạn có thể sử dụng để hiển thị danh sách các bài viết.
Tương tự, để tạo thành phần cho việc hiển thị chi tiết từng bài viết, bạn có thể sử dụng lệnh sau:
ng generate component article-detail
Đây sẽ tạo ra một thành phần có tên là “article-detail” để bạn có thể sử dụng để hiển thị thông tin chi tiết của từng bài viết.
Khi bạn thực hiện các lệnh này, CLI sẽ tạo ra các thành phần tương ứng để bạn có thể sử dụng trong ứng dụng của mình.
Config router cơ bản
Để cấu hình router trong ứng dụng của bạn, sau khi đã tạo ứng dụng theo các bước trước đó, CLI đã tự động tạo một module có tên là AppRoutingModule và tự động import nó vào AppModule cho bạn. Điều này giúp quản lý việc routing một cách dễ dàng hơn.
Trong Angular, có ba thành phần chính khi làm việc với Router. Đầu tiên, bạn cần import RouterModule và Routes vào trong module của bạn, ở đây là AppRoutingModule:
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
Lưu ý rằng AppRoutingModule import và export RouterModule. Điều này có ý nghĩa là khi bạn import AppRoutingModule vào các module khác, bạn không cần phải import lại RouterModule để sử dụng vì nó đã được re-export từ AppRoutingModule.
RouterModule cung cấp hai method là forRoot và forChild. forRoot được gọi duy nhất khi bạn config route trong AppRoutingModule và dùng để khởi tạo và cấu hình router. Trong khi đó, forChild được gọi trong các module khác để config routes.
Việc sử dụng forRoot liên quan đến việc khởi tạo service internal của RouterModule. Điều này quan trọng để đảm bảo rằng các service của RouterModule được khởi tạo và chỉ có một instance duy nhất. Gọi forRoot nhiều lần trong các module khác nhau có thể dẫn đến behavior không đoán được khi sử dụng Router.
Sau khi đã cấu hình, bạn có thể config routes trong mảng Routes như sau:
const routes: Routes = [
{
path: 'detail',
component: ArticleDetailComponent,
},
{
path: '',
component: ArticleListComponent,
},
];
Mỗi đối tượng Route trong mảng có hai phần chính quan trọng là path và component. Ví dụ ở trên, khi bạn mở ứng dụng, component mặc định được load là ArticleListComponent. Nếu bạn mở đường dẫn /detail, router sẽ load component ArticleDetailComponent tương ứng.
Để sử dụng route trong ứng dụng của bạn, bạn có thể sử dụng routerLink trong thẻ <a>
để gán URL đã config và <router-outlet>
sẽ là nơi mà các component tương ứng được load khi bạn mở các URL tương ứng.
<ul class="nav nav-pills card-header-pills">
<li class="nav-item">
<a class="nav-link" routerLink="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="detail">Detail</a>
</li>
</ul>
<router-outlet></router-outlet>
Sau khi thực hiện, bạn có thể thấy router hoạt động như mong đợi, các component sẽ được load tương ứng khi bạn di chuyển qua các URL đã được cấu hình.
Thứ tự route khi config
Khi bạn cấu hình route ở bước hai, bạn đã thấy rằng việc sắp xếp thứ tự trong mảng Routes rất quan trọng và ảnh hưởng đến cách route được load. Điều này đặc biệt quan trọng khi bạn có nhiều route khác nhau.
Thứ tự của các route trong mảng này quyết định cách mà router xác định route nào được áp dụng trước. Routes chi tiết hơn, chứa nhiều thông tin cụ thể hơn nên nên được config trước các route ít chi tiết hơn.
Ví dụ, nếu bạn có các route như sau:
/detail/123/edit
: Để hiển thị form chỉnh sửa cho mục có id 123./detail/123
: Để hiển thị thông tin chi tiết cho mục có id 123.
Trong trường hợp này, route cho việc chỉnh sửa (/detail/123/edit
) nên được đặt ở trước. Những route chứa thông tin cụ thể hoặc hoạt động cụ thể hơn nên được xác định trước, còn những route generic hoặc ít chi tiết hơn nên để phía sau. Điều này giúp router xác định route phù hợp với URL được truy cập một cách chính xác và logic.
Lấy thông tin từ route
Khi xây dựng ứng dụng, thường cần trao đổi dữ liệu giữa các route và thường điều này được thực hiện thông qua URL. Ví dụ, khi mở một bài viết, cần có id hoặc slug trên URL để tìm thông tin chi tiết của bài viết.
Để hiển thị danh sách bài viết dựa trên dữ liệu có sẵn, chúng ta sử dụng một service để lấy thông tin từ nguồn dữ liệu, sau đó render danh sách này trong component danh sách bài viết.
Trong ví dụ này, chúng ta có một danh sách các bài viết và sử dụng một service để lấy danh sách này. Trong component danh sách bài viết, chúng ta sử dụng *ngFor
để render danh sách các bài viết và tạo các button để chuyển đến route chi tiết của từng bài viết. Tuy nhiên, khi nhấn vào các button này, chưa có tương ứng hoạt động gì xảy ra.
Để giải quyết vấn đề này, chúng ta cần cấu hình route để route chi tiết có thể nhận slug từ URL. Chúng ta sửa phần config của router như sau:
const routes: Routes = [
{
path: ":slug",
component: ArticleDetailComponent,
},
{
path: "",
component: ArticleListComponent,
},
];
Thay vì path: 'detail'
, ta sửa thành path: ':slug'
. Dấu hai chấm định nghĩa một parameter trên URL. Phần sau dấu hai chấm là tên của parameter mà ta muốn lấy từ ArticleDetailComponent
.
Trong ArticleDetailComponent
, để lấy slug từ URL, ta sử dụng ActivatedRoute để lấy thông tin của params có tên là slug từ route snapshot.
Kết quả là, ngay cả khi reload page, ArticleDetailComponent
vẫn có thể lấy thông tin về slug từ URL và sử dụng nó để lấy dữ liệu và hiển thị thông tin của bài viết như mong muốn.
Qua các bài viết tiếp theo, chúng ta sẽ tìm hiểu cách xử lý lỗi khi không tìm thấy bài viết.