You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular:如何用Observable实现嵌套查询树获取评论子列表?

实现Hacker News嵌套评论树

要构建完整的评论树,核心是通过递归方式获取每个评论的子评论并填充到children属性中,以下是具体实现步骤:

1. 修正接口类型(可选但推荐)

统一TypeScript类型规范,将Comments接口中的大写String改为小写string:

export interface Comments {
    by: string;
    id: number;
    parent: number;
    text: string;
    time: number;
    deleted?: boolean;
    dead?: boolean;
    kids?: number[];
    children?: Comments[];
    type: string;
}

2. 扩展服务层,实现递归获取嵌套评论

在StorieCommentService中添加递归方法,获取单条评论及其所有子评论,同时过滤无效评论(已删除/标记为dead的):

@Injectable({
    providedIn: 'root',
})
export class StorieCommentService {
    private apiBaseUrl: string = 'https://hacker-news.firebaseio.com/v0';

    constructor(private http: HttpClient) {}

    // 原有方法保持不变
    getObservableDataStory(id: number): Observable<Story> {
        const urlReq = `${this.apiBaseUrl}/item/${id}.json`;
        return this.http.get<Story>(urlReq);
    }

    getObservableDataComment(id: number): Observable<Comments> {
        const urlReq = `${this.apiBaseUrl}/item/${id}.json`;
        return this.http.get<Comments>(urlReq);
    }

    // 新增:获取单条评论及其所有嵌套子评论
    private getCommentWithChildren(id: number): Observable<Comments | null> {
        return this.getObservableDataComment(id).pipe(
            // 过滤已删除或无效的评论
            map(comment => {
                if (comment.deleted || comment.dead) return null;
                return comment;
            }),
            concatMap(comment => {
                if (!comment || !comment.kids || comment.kids.length === 0) {
                    return of({ ...comment, children: [] } as Comments);
                }
                // 递归获取所有子评论
                return forkJoin(comment.kids.map(kidId => this.getCommentWithChildren(kidId))).pipe(
                    map(children => ({
                        ...comment,
                        children: children.filter(child => child !== null) // 过滤无效子评论
                    }))
                );
            })
        );
    }

    // 新增:批量获取带嵌套子评论的评论列表
    getDataCommentsWithChildren(ids: number[]): Observable<Comments[]> {
        if (!ids || ids.length === 0) {
            return of([]);
        }
        return forkJoin(ids.map(id => this.getCommentWithChildren(id))).pipe(
            map(comments => comments.filter(comment => comment !== null) as Comments[])
        );
    }
}

3. 修改组件层,调用新的评论获取方法

替换原有loadCommentsData方法的调用逻辑,同时处理评论ID为空的边界情况:

@Component({
    selector: 'app-main-comment',
    templateUrl: './main-comment.component.html',
    styleUrls: ['./main-comment.component.css'],
})
export class MainCommentComponent implements OnInit {
    story!: Story;
    comments!: Comments[];
    isLoading = false; // 新增加载状态

    constructor(
        private activatedRoute: ActivatedRoute,
        private storieCommentService: StorieCommentService
    ) {}

    ngOnInit() {
        this.loadStoryData();
    }

    loadStoryData() {
        this.activatedRoute.params
            .pipe(
                switchMap(({ id }) =>
                    this.storieCommentService.getObservableDataStory(id)
                )
            )
            .subscribe({
                next: story => {
                    this.story = story;
                    if (story.kids && story.kids.length > 0) {
                        this.loadCommentsData(story.kids);
                    } else {
                        this.comments = [];
                    }
                },
                error: err => console.error('加载文章失败:', err)
            });
    }

    loadCommentsData(ids: number[]) {
        this.isLoading = true;
        this.storieCommentService
            .getDataCommentsWithChildren(ids)
            .pipe(finalize(() => this.isLoading = false))
            .subscribe({
                next: comments => this.comments = comments,
                error: err => console.error('加载评论失败:', err)
            });
    }
}

关键逻辑说明

  • 递归获取:getCommentWithChildren方法会在获取到当前评论后,自动遍历其kids数组,递归调用自身获取每个子评论的完整树结构。
  • 无效评论过滤:在每一层递归中都会过滤已删除或标记为dead的评论,避免无效节点出现在评论树中。
  • 批量处理:使用forkJoin批量发起子评论请求,提升请求效率,同时保证所有子评论加载完成后再组装到父评论的children属性中。

内容的提问来源于stack exchange,提问作者SoleeL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 15:06:26