我有一个递归 JavaScript(我的脑筋急转弯逻辑),我在 Angular 19 中使用它从 Youtube 中提取数据。 该脚本正在运行,只是我想知道这应该是一个更好的方法,因为这是我以前的学校 JavaScript。 谢谢您的任何建议。
public getSeriesList() {
this.record.webTubeSeries = [];
this._seqNum = 0;
this.getUrlYouTube()
.pipe(first())
.subscribe({
next: (res: any) => {
this.parseVideoList(res["items"]);
if (res['nextPageToken']) {
let repeatGetNextVideoPage = (_token: string) => {
this.getNextVideoPage(_token)
.subscribe(
(result: any) => {
this.parseVideoList(result["items"]);
if (result["nextPageToken"]) {
repeatGetNextVideoPage(result["nextPageToken"]);
}
}
),
(err: any) => {
console.log("HTTP Error", err.message)
}
}
repeatGetNextVideoPage(res["nextPageToken"]);
}
}
}
);
}
private getNextVideoPage(_token: string) {
let url = (this.urlYouTube + this.videoListId + '&pageToken=' + _token);
return this.http.get(url);
}
private parseVideoList(result: any) {
for (let v of result) {
var item = new WebtubeSeries;
item.videoTitle = v.snippet.title;
item.videoId = v.snippet.resourceId.videoId;
if (v.snippet.thumbnails) {
item.urlThumbNailDefault = v.snippet.thumbnails.default.url;
item.urlThumbNailMedium = v.snippet.thumbnails.medium.url;
item.urlThumbNailHigh = v.snippet.thumbnails.high.url;
item.widthDefault = v.snippet.thumbnails.default.width.toString();
item.heightDefault = v.snippet.thumbnails.default.height.toString();
item.widthMedium = v.snippet.thumbnails.medium.width.toString();
item.heightMedium = v.snippet.thumbnails.medium.height.toString()
item.widthHigh = v.snippet.thumbnails.high.width.toString()
item.heightHigh = v.snippet.thumbnails.high.height.toString();
item.seqNumber = this._seqNum++;
//item.id = item.seqNumber;
this.record.webTubeSeries.push(item);
}
}
}
谢谢。
在 Angular19 中,他们引入了
resource
和 rxResource
,可用于基于输入信号的数据处理。我建议使用 rxResource
这种方法,因为我们可以利用 rxjs 来执行递归。
首先我们在类构造函数中定义类逻辑的构造。
export class WebtubeSeries {
videoTitle: any;
urlThumbNailDefault: any;
urlThumbNailMedium: any;
videoId: any;
urlThumbNailHigh: any;
widthDefault: any;
heightDefault: any;
widthMedium: any;
heightMedium: any;
widthHigh: any;
heightHigh: any;
seqNumber: number;
constructor(v: any, _seqNum = 0) {
this.seqNumber = _seqNum;
// this.videoTitle = v.snippet.title;
// this.videoId = v.snippet.resourceId.videoId;
// if (v.snippet.thumbnails) {
// this.urlThumbNailDefault = v.snippet.thumbnails.default.url;
// this.urlThumbNailMedium = v.snippet.thumbnails.medium.url;
// this.urlThumbNailHigh = v.snippet.thumbnails.high.url;
// this.widthDefault = v.snippet.thumbnails.default.width.toString();
// this.heightDefault = v.snippet.thumbnails.default.height.toString();
// this.widthMedium = v.snippet.thumbnails.medium.width.toString();
// this.heightMedium = v.snippet.thumbnails.medium.height.toString();
// this.widthHigh = v.snippet.thumbnails.high.width.toString();
// this.heightHigh = v.snippet.thumbnails.high.height.toString();
// this.seqNumber = _seqNum;
// }
// this.seqNumber = 0;
}
}
然后,我们可以使用名为
rxResource
的属性定义 loader
来进行 API 调用。
在加载器内部,我们使用
expand
用于递归 API 调用,当没有下一个 API 令牌时,我们还使用 takeUntil
来停止序列。
最后,我们使用
reduce
合并结果并构建最终结果。
youtube = rxResource({
loader: () => {
let _seqNum = 0;
let records: any = [];
return this.getUrlYouTube().pipe(
expand((response: any) =>
this.getNextVideoPage(response['nextPageToken'])
),
takeWhile((response: any) => !!response['nextPageToken'], true),
reduce((all: any, data: any) => all.concat(data.items), []),
map((response: any) => {
console.log(response);
return this.parseVideoList(response, _seqNum);
})
);
},
});
import { Component } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { bootstrapApplication } from '@angular/platform-browser';
import { of, first, EMPTY, expand, takeWhile, tap, reduce, map } from 'rxjs';
import { CommonModule } from '@angular/common';
export class WebtubeSeries {
videoTitle: any;
urlThumbNailDefault: any;
urlThumbNailMedium: any;
videoId: any;
urlThumbNailHigh: any;
widthDefault: any;
heightDefault: any;
widthMedium: any;
heightMedium: any;
widthHigh: any;
heightHigh: any;
seqNumber: number;
constructor(v: any, _seqNum = 0) {
this.seqNumber = _seqNum;
// this.videoTitle = v.snippet.title;
// this.videoId = v.snippet.resourceId.videoId;
// if (v.snippet.thumbnails) {
// this.urlThumbNailDefault = v.snippet.thumbnails.default.url;
// this.urlThumbNailMedium = v.snippet.thumbnails.medium.url;
// this.urlThumbNailHigh = v.snippet.thumbnails.high.url;
// this.widthDefault = v.snippet.thumbnails.default.width.toString();
// this.heightDefault = v.snippet.thumbnails.default.height.toString();
// this.widthMedium = v.snippet.thumbnails.medium.width.toString();
// this.heightMedium = v.snippet.thumbnails.medium.height.toString();
// this.widthHigh = v.snippet.thumbnails.high.width.toString();
// this.heightHigh = v.snippet.thumbnails.high.height.toString();
// this.seqNumber = _seqNum;
// }
// this.seqNumber = 0;
}
}
@Component({
selector: 'app-root',
imports: [CommonModule],
template: `
{{youtube.value() | json}}
`,
})
export class App {
a = 0;
record: any = {
webTubeSeries: [],
};
getUrlYouTube() {
this.a++;
const randon = Math.random();
return of({
items: [0, 0, 0, 0, 0],
nextPageToken: randon,
});
}
youtube = rxResource({
loader: () => {
let _seqNum = 0;
let records: any = [];
return this.getUrlYouTube().pipe(
expand((response: any) =>
this.getNextVideoPage(response['nextPageToken'])
),
takeWhile((response: any) => !!response['nextPageToken'], true),
reduce((all: any, data: any) => all.concat(data.items), []),
map((response: any) => {
console.log(response);
return this.parseVideoList(response, _seqNum);
})
);
},
});
private parseVideoList(response: any, _seqNum: number) {
const result = [];
for (let v of response) {
console.log(v);
_seqNum++;
// if (v.snippet.thumbnails) {
result.push(new WebtubeSeries(v, _seqNum));
// }
}
return result;
}
private getNextVideoPage(_token: string) {
this.a++;
const randon = Math.random();
return of({
items: [1, 2, 3, 4, 5],
nextPageToken: this.a > 10 ? null : randon,
});
// let url = this.urlYouTube + this.videoListId + '&pageToken=' + _token;
// return this.http.get(url);
}
}
bootstrapApplication(App);