天天看點

從 Angular Route 中提前擷取資料

作者:吉米龐

#頭條創作挑戰賽#

從 Angular Route 中提前擷取資料

banner

介紹

提前擷取意味着在資料呈現在螢幕之前擷取到資料。本文中,你将學到,在路由更改前怎麼擷取到資料。通過本文,你将學會使用 resolver, 在 Angular App 中應用 resolver,應用到一個公共的預加載導航。

你為什麼應該使用 Resolver

Resolver 在路由跟元件之間扮演着中間件服務的角色。假設你有一個表單,沒有資料時,你想向使用者一個空的表單,當在加載使用者資料時展示一個 loader,然後當資料傳回時,填充表單并隐藏 loader。

通常,我們都會在元件的 ngOnInit() 鈎子函數中擷取資料。也就是說,元件加載完之後,我們發起資料請求。

在 ngOnInit() 中操作,我們需要在每個需要的元件加載後,在其路由頁面中添加 loader 展示。Resolver 可以簡化 loader 的添加使用。你可以隻添加一個适用于每個路由的 loader,而不是每個路由中都添加 loader。

本文将結合示例來解析 resolver 的知識點。以便于你可以牢記它并在項目中使用它。

在應用中使用 Resolver

為了在應用中使用 resolver,你需要準備一些接口。你可以通過 JSONPlaceholder 來模拟,而不需要自己開發。

JSONPlaceholder 是一個很棒的接口資源,你可以借助它更好學習前端的相關概念而不被接口所限制。

現在,接口的問題解決了,我們可以開始 resolver 的應用了。一個 resolver 就是一個中間件服務,是以我們将建立一個服務。

$ ng g s resolvers/demo-resolver --skipTests=true
複制代碼           
--skipTests=true 跳過生成測試檔案

src/app/resolvers 檔案夾中建立了一個服務。resolver 接口中有一個 resolve() 方法,它有兩個參數:route(ActivatedRouteSnapshot 的執行個體)和 state(RouterStateSnapshot 的執行個體)。

loader 通常是在 ngOnInit() 中編寫所有的 AJAX 請求,但是邏輯将會在 resolver 中實作,替代 ngOnInit()。

接着,建立一個服務來擷取 JSONPlaceholder 中清單資料。然後在 resolver 中底調用,接着在路由中配置 resolve資訊,(頁面将會等待)直到 resolver 被處理。在 resolver 被處理之後,我們可以通過路由來擷取資料然後展示在元件中。

建立服務并編寫邏輯擷取清單資料

$ ng g s services/posts --skipTests=true
複制代碼           

現在,我們成功建立了服務,是時候編寫一個 AJAX 請求的邏輯了。

model 的使用能夠幫助我們減少錯誤。

$ ng g class models/post --skipTests=true
複制代碼           

post.ts

export class Post {
  id: number;
  title: string;
  body: string;
  userId: string;
}
複制代碼           

model 就緒,是時候擷取文章 post 的資料了。

post.service.ts

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Post } from "../models/post";

@Injectable({
  providedIn: "root"
})
export class PostsService {
  constructor(private _http: HttpClient) {}

  getPostList() {
    let URL = "https://jsonplaceholder.typicode.com/posts";
    return this._http.get<Post[]>(URL);
  }
}
複制代碼           

現在,這個服務随時可被調用。

demo-resolver.service.ts

import { Injectable } from "@angular/core";
import {
  Resolve,
  ActivatedRouteSnapshot,
  RouterStateSnapshot
} from "@angular/router";
import { PostsService } from "../services/posts.service";

@Injectable({
  providedIn: "root"
})
export class DemoResolverService implements Resolve<any> {
  constructor(private _postsService: PostsService) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this._postsService.getPostList();
  }
}
複制代碼           

文章清單資料從 resolver 中傳回。現在,你需要一個路由去配置 resolver,從路由擷取資料,然後讓資料展示在元件中。為了進行路由跳轉,我們需要建立一個元件。

$ ng g c components/post-list --skipTests=true
複制代碼           

為了路由可見,在 app.component.ts 添加 router-outlet。

<router-outlet></router-outlet>
複制代碼           

現在,你可以配置 app-routing.module.ts 檔案了。下面的片段代碼将有助于你了解路由配置 resolver。

app-routing-module.ts

import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { PostListComponent } from "./components/post-list/post-list.component";
import { DemoResolverService } from "./resolvers/demo-resolver.service";

const routes: Routes = [
  {
    path: "posts",
    component: PostListComponent,
    resolve: {
      posts: DemoResolverService
    }
  },
  {
    path: "",
    redirectTo: "posts",
    pathMatch: "full"
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}
複制代碼           

一個 resolve 已經添加到路由配置中了,它将發起一個 HTTP 請求,然後當 HTTP 請求成功傳回後,允許元件初始化。路由将組裝擷取到的 HTTP 請求傳回的資料。

怎麼應用一個預加載導航

向使用者展示一個請求正在進行,我們在 AppComponent 中編寫一個公共且簡單的 loader。你可以根據需要自定義。

app.component.html

<div class="loader" *ngIf="isLoader">
  <div>Loading...</div>
</div>
<router-outlet></router-outlet>
複制代碼           

app.component.ts

import { Component } from "@angular/core";
import {
  Router,
  RouterEvent,
  NavigationStart,
  NavigationEnd
} from "@angular/router";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"]
})
export class AppComponent {
  isLoader: boolean;

  constructor(private _router: Router) {}

  ngOnInit() {
    this.routerEvents();
  }

  routerEvents() {
    this._router.events.subscribe((event: RouterEvent) => {
      switch (true) {
        case event instanceof NavigationStart: {
          this.isLoader = true;
          break;
        }
        case event instanceof NavigationEnd: {
          this.isLoader = false;
          break;
        }
      }
    });
  }
}
複制代碼           

當導航開始,isLoader 值被賦予 true,頁面中,你将看到下面的效果。

從 Angular Route 中提前擷取資料

當 resolver 處理完之後,它将會被隐藏。

現在,是時候從路由中擷取值并将其展示出來。

port-list.component.ts

import { Component, OnInit } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { Post } from "src/app/models/post";

@Component({
  selector: "app-post-list",
  templateUrl: "./post-list.component.html",
  styleUrls: ["./post-list.component.scss"]
})
export class PostListComponent implements OnInit {
  posts: Post[];

  constructor(private _route: ActivatedRoute) {
    this.posts = [];
  }

  ngOnInit() {
    this.posts = this._route.snapshot.data["posts"];
  }
}
複制代碼           

如上所示,post 的值來自 ActivatedRoute 的快照資訊 data。這值都可以擷取,隻要你在路由中配置了相同的資訊。

我們在 HTML 進行如下渲染。

<div class="post-list grid-container">
  <div class="card" *ngFor="let post of posts">
    <div class="title"><b>{{post?.title}}</b></div>
    <div class="body">{{post.body}}</div>
  </div>
</div>
複制代碼           

CSS 片段樣式讓其看起來更美觀。

port-list.component.css

.grid-container {
2  display: grid;
3  grid-template-columns: calc(100% / 3) calc(100% / 3) calc(100% / 3);
4}
5.card {
6  margin: 10px;
7  box-shadow: black 0 0 2px 0px;
8  padding: 10px;
9}
複制代碼           
推薦使用 scss 預處理器編寫樣式

從路由中擷取資料之後,它會被展示在 HTML 中。效果如下快照。

從 Angular Route 中提前擷取資料

至此,你已經了解完怎麼應用 resolver 在你的項目中了。

總結

結合使用者體驗設計,在 resolver 的加持下,你可以提升你應用的表現。了解更多,你可以戳官網。

本文是譯文,采用的是意譯的方式,其中加上個人的了解和注釋,原文位址是:www.pluralsight.com/guides/pref…