昨天第一個例子和相關的元件我們已經完成介紹了,接下來我們繼續進行新的 demo 開發,接下來幾天我們會使用 NG-ZORRO 開發 Angular 版本的 Hacker News
,過程中會使用一些全新的元件並加以介紹,這也會是我們在進階學習前的最後一個實戰案例,今天我們會來搭建該專案一些必要的配置和介面。
Hacker News 是一家關於計算機黑客和創業公司的社會化新聞網站,由保羅·格雷厄姆的創業孵化器 Y Combinator 建立。與其它社會化新聞網站不同的是 Hacker News 沒有踩或反對一條提交新聞的選項(不過評論還是可以被有足夠 Karma 的使用者投反對票);只可以贊或是完全不投票。簡而言之,Hacker News 允許提交任何可以被理解為“任何滿足人們求知慾”的新聞。
官方網站:https://news.ycombinator.com/
我們可以看到, hacker news
提供了 公共的 API 供我們呼叫,我們可以為其建立一個 service
來管理相關的介面。
之前我們講過,對於 http 介面請求
最好是建立獨立的服務(service
),可以給我們呼叫和後期維護帶來很大便利。先來建立 service
和 hacker news
元件。
$ cd ng-zorro-ironman2020
$ ng g c components/demos/hacker-news --skip-import
$ ng g s services/hacker-news
檢視 官方 API 文件,檢視基本結構
https://hacker-news.firebaseio.com/v0/item/8863.json?print=pretty
{
"by" : "dhouston",
"descendants" : 71,
"id" : 8863,
"kids" : [ 8952, 9224, 8917, 8884, 8887, 8943, 8869, 8958, 9005, 9671, 8940, 9067, 8908, 9055, 8865, 8881, 8872, 8873, 8955, 10403, 8903, 8928, 9125, 8998, 8901, 8902, 8907, 8894, 8878, 8870, 8980, 8934, 8876 ],
"score" : 111,
"time" : 1175714200,
"title" : "My YC app: Dropbox - Throw away your USB drive",
"type" : "story",
"url" : "http://www.getdropbox.com/u/2/screencast.html"
}
Tips:提供給大家一個快速將 json 格式格式化為 Angular interface 結構的網站 jsontots
export interface IStory {
by: string;
descendants: number;
id: number;
kids: number[];
score: number;
time: number;
title: string;
type: string;
url: string;
}
檢視 API 我們得知,訪問 https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty
得出 story
的 id 列表:
[ 20960832, 20960721, 20959831, 20958397, ... ]
但是這並不是我們想要的結果,我們希望返回的是包含總新聞資料量和新聞詳情列表的結果,比如:
{
total: 100,
data: IStory[]
}
從上面我們已經得知如何通過 ID
來獲取新聞詳情,通過 RxJS 的 mergeMap
和 forkJoin
操作符我們可以輕易地解決這個問題(該部分內容我們不展開討論,有興趣的同學可以專門搜尋 RxJS 相關文章學習),返回需要查詢的新聞 id 內容。
看一下在 hacker-news.service.ts
裡我們是如何實現的(為了減少單次請求次數,在程式碼中我們加入了前端分頁引數):
getStories(page: number = 1, pageSize: number = 20, type = IBaseStoryType.TOP): Observable<IResultList> {
return this.http.get<any>(`https://hacker-news.firebaseio.com/v0/beststories.json?print=pretty`).pipe(
mergeMap((ids) => forkJoin([of(ids.length), ...ids.slice((page - 1) * pageSize, page * pageSize).map(id => this.getStory(id))])),
map(data => {
return {
total: data[0],
data: data.slice(1)
};
})
);
}
getStory(itemId: number): Observable<IStory> {
return this.http.get<IStory>(`https://hacker-news.firebaseio.com/v0/item/${itemId}.json?print=pretty`);
}
我們可以看到 mergeMap
拿到介面返回的 id
陣列後通過 forkJoin
請求了各個 id 的詳情並返回,在返回陣列的第一項我們存放了總資料量(僅僅是為了前端分頁設計可暫時忽略),看一下 map
之後的結果:
{
"total":200,
"data":[
{
"by":"anigbrowl",
"descendants":550,
"id":20955103,
"kids":[
20956457,
20955562,
...
],
"score":1799,
"time":1568314973,
"title":"California bans private prisons",
"type":"story",
"url":"https://www.theguardian.com/us-news/2019/sep/12/california-private-prison-ban-immigration-ice"
},
...
]
}
但是這種方案有個弊端,就是我們每次都要發起 20 次 http 請求然後返回結果陣列,導致很麻煩,不過我們倒是從這個方法中能瞭解一些 RxJS 操作符的使用方式。
那麼有沒有一個介面就返回所有我們想要的資料呢?答案當然是肯定的。
同樣的它也提供了一些 API 來供我們使用,不同於上述 API,這個介面直接返回了詳情給我們使用,無需我們二次處理,看一下 線上返回資料,返回瞭如下結構,hits
屬性即為命中的結果(為了簡潔我刪除了部分屬性):
{
"hits":[
{
"title":"Stephen Hawking has died",
"url":"http://www.bbc.com/news/uk-43396008",
"author":"Cogito",
"points":6015,
"story_text":null,
"comment_text":null,
"num_comments":436,
"_tags":[
"story",
"author_Cogito",
"story_16582136"
],
"objectID":"16582136",
"_highlightResult":{
"title":{
"value":"Stephen Hawking has died",
"matchLevel":"none",
"matchedWords":[]
},
"url":{
"value":"http://www.bbc.com/news/uk-43396008",
"matchLevel":"none",
"matchedWords":[
]
},
"author":{
"value":"Cogito",
"matchLevel":"none",
"matchedWords":[
]
}
}
}
],
"nbHits":18914697,
"page":0,
"nbPages":1000,
"hitsPerPage":1,
"query":"",
}
檢視單個新聞的詳情:https://news.ycombinator.com/item?id=20961506,返回結構如下,我們還可以直接在詳情中得到評論內容。
export interface IReplyComment {
id: number;
created_at: Date;
created_at_i: number;
type: string;
author: string;
title?: any;
url?: any;
text: string;
points?: any;
parent_id: number;
story_id: number;
children: IReplyComment[];
options: any[];
}
export interface IStory {
id: number;
created_at: Date;
created_at_i: number;
type: string;
author: string;
title: string;
url: string;
text?: any;
points: number;
parent_id?: any;
story_id?: any;
children: IReplyComment[];
options: any[];
}
今天我們做了一些專案開始前的必備準備工作,包括 API 介面, interface 設計、背景知識等,既然介面已經設計完成,我們在明天也會正式開始 hacker news
專案的開發。
Github 今日線上程式碼:https://github.com/simplejason/ng-zorro-ironman2020/tree/day-12-hacker-news-start
Hacker News:https://news.ycombinator.com/
Algolia API:https://hn.algolia.com/api