Guards and Resolvers (Phần 3)
- 14-01-2024
- Toanngo92
- 0 Comments
Route Resolvers
Sau khi các Route Guards đã chạy và đã vượt qua được các guards này, thì đến lượt Route Resolvers được kích hoạt. Thông thường, đây là nơi mà chúng ta có thể chuẩn bị trước dữ liệu để sử dụng trong component sẽ được activate.
Liệu có cần sử dụng Resolvers không?
Thường thì, chúng ta có thể tiếp cận component trước và từ ngOnInit
, chúng ta có thể thực hiện các bước để lấy dữ liệu cần thiết cho component đó. Ví dụ, trong component ArticleDetailComponent
, chúng ta có thể làm như sau:
export class ArticleDetailComponent implements OnInit {
article$: Observable<Article>;
constructor(private _route: ActivatedRoute, private _api: ArticleService) {}
ngOnInit(): void {
this.article$ = this._route.paramMap.pipe(
map((params) => params.get("slug")),
switchMap((slug) => this._api.getArticleBySlug(slug))
);
}
}
Chính xác, khi component được kích hoạt, chúng ta thực hiện kết nối đến nguồn dữ liệu để lấy thông tin. Trong nhiều trường hợp, quá trình này là bất đồng bộ, như đã thấy ở trên.
Lúc này, chúng ta có thể thấy rằng chúng ta đã triển khai một chỉ báo tải để thông báo cho người dùng biết chúng ta đang lấy dữ liệu.
Resolvers là gì và tại sao lại cần chúng?
Interface mà các class có thể implement để trở thành một nhà cung cấp dữ liệu. Một class nhà cung cấp dữ liệu có thể được sử dụng với router để giải quyết dữ liệu trong quá trình điều hướng. Interface định nghĩa một phương thức resolve()
sẽ được gọi khi điều hướng bắt đầu. Router sau đó sẽ chờ cho đến khi dữ liệu được giải quyết trước khi route cuối cùng được kích hoạt.
interface Resolve<T> {
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<T> | Promise<T> | T;
}
Resolver chỉ là một dạng của service, đã triển khai interface Resolve. Các Resolvers sẽ thực thi hàm resolve và Router sẽ chờ đến khi dữ liệu được giải quyết trước khi activate components.
Ví dụ, chúng ta có thể tạo một Resolver để lấy thông tin về một Article như sau:
@Injectable({
providedIn: "root",
})
export class ArticleResolver implements Resolve<Article> {
constructor(private articleService: ArticleService) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<Article> | Promise<Article> | Article {
const slug = route.paramMap.get("slug");
return this.articleService.getArticleBySlug(slug);
}
}
Sau đó, trong cấu hình routing của chi tiết bài viết, chúng ta thêm một khóa mới:
const routes: Routes = [
{
path: "article",
component: ArticleComponent,
children: [
{
path: "",
component: ArticleListComponent,
},
{
path: ":slug",
component: ArticleDetailComponent,
resolve: {
article: ArticleResolver,
},
},
],
},
];
Cuối cùng, chúng ta thay đổi component để lấy dữ liệu từ thuộc tính data:
export class ArticleDetailComponent implements OnInit {
article$: Observable<Article>;
constructor(private _route: ActivatedRoute, private _api: ArticleService) {}
ngOnInit(): void {
this.article$ = this._route.data.pipe(map((data) => data.article));
}
}
Bây giờ, khi chúng ta thực hiện thử nghiệm, thay vì hiển thị chỉ báo tải trong component, chúng ta đã lấy dữ liệu trước khi activate component. Do đó, sau khi điều hướng, Resolver phải hoàn thành giải quyết và chúng ta mới thấy component được render ra màn hình. Do đó, không còn hiển thị chỉ báo tải nữa.
Lưu ý:
- Nếu Resolver không giải quyết được, bạn sẽ không thể nhìn thấy component của mình được render.
- Không nên chia sẻ giữa nhiều Resolvers với nhau.
- Đối với các luồng trả về nhiều giá trị, bạn không nên sử dụng cho Resolvers, ví dụ như WebSocket.
Trong quan điểm của mình, Resolvers nên chỉ được sử dụng để lấy một phần dữ liệu, sau đó component có thể thực hiện các kết nối khác.