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

Angular项目添加图书分类筛选后出现TypeError读取'books'失败求助

问题:Angular按分类展示图书时出现"Cannot read properties of undefined (reading 'books')"错误

尝试按分类展示图书(如访问http://localhost:4200/books/category/1),原本无分类筛选功能正常,添加分类筛选代码后出现以下错误:

book-list.component.ts:52 Error occured. TypeError: Cannot read properties of undefined (reading 'books')


相关代码

Service类(book.service.ts)

import { Book } from './../common/book';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, map } from 'rxjs';
import { environment } from 'src/environments/environment';

@Injectable({
  providedIn: 'root',
})
export class BookService {
  private baseUrl: string = environment.apiUrl + "/book";

  constructor(private httpClient: HttpClient) {}

  getBookList(theCategoryId: number): Observable<Book[]> {
    const searchUrl = `${environment.apiUrl}/book/category/${theCategoryId}`;

    return this.httpClient.get<GetResponse>(searchUrl).pipe(map(
      response => response._embedded.books
    ));
  }
}

interface GetResponse {
  _embedded: {
    books: Book[]
  }
}

组件TS文件(book-list.component.ts)

import { environment } from './../../../environments/environment';
import { Book } from './../../common/book';
import { BookService } from './../../services/book.service';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-book-list',
  templateUrl: './book-list.component.html',
  styleUrls: './book-list.component.css'
})
export class BookListComponent implements OnInit {

  books: Book[] = [];
  currentCategoryId: number = 1;

  constructor(
    private bookService: BookService,
    private route: ActivatedRoute
    ) { }

  ngOnInit(): void {
    this.route.paramMap.subscribe(() => {
      this.listBooks();
    });
  }

  listBooks() {
    const hasCategoryId: boolean = this.route.snapshot.paramMap.has('id');
    
    if (hasCategoryId) {
      this.currentCategoryId = +this.route.snapshot.paramMap.get('id')!;
    }
    else {
      this.currentCategoryId = 1;
    }

    this.bookService.getBookList(this.currentCategoryId).subscribe(
      data => {
        this.books = data;
        console.log(data);
    }, error => {
      console.log('Error occured.', error);
    });
  }
}

HTML文件(book-list.component.html)

<!-- Product info -->
<section class="section has-background-grey-light">
    <div class="container">
        <div class="columns is-vcentered is-multiline">
            <div class="column is-3-desktop is-12-tablet is-12-mobile" *ngFor="let book of books">
                <div class="card">
                    <div class="card-image">
                        <figure class="image is-4by3">
                            <img src="{{ book?.imageUrl }}" alt="Placeholder image">
                        </figure>
                    </div>
                    <div class="card-content has-text-left">
                        <div class="media">
                            <div class="media-content">
                                <p class="is-size-5-tablet is-size-5-mobile is-size-5-desktop title">{{ book?.name }}</p>
                                <p class="is-size-6-tablet is-size-8-mobile is-size-6-desktop subtitle">by {{ book?.author }}</p>
                            </div>
                        </div>
                    </div>
                    <div class="card-footer">
                        <div class="card-footer-item">
                            <div class="buttons">
                                <a class="button is-info">
                                    <strong>Reserve</strong>
                                </a>
                                <a class="button is-light">
                                  Rate
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>
<!-- Product info end -->

控制台报错信息

Error occured. TypeError: Cannot read properties of undefined (reading 'books')
    at book.service.ts:21:38
    at map.js:7:1
    at OperatorSubscriber._next (OperatorSubscriber.js:13:1)
    at OperatorSubscriber.next (Subscriber.js:31:1)
    at map.js:7:1
    at OperatorSubscriber._next (OperatorSubscriber.js:13:1)
    at OperatorSubscriber.next (Subscriber.js:31:1)
    at filter.js:6:50
    at OperatorSubscriber._next (OperatorSubscriber.js:13:1)
    at OperatorSubscriber.next (Subscriber.js:31:1)

解决方法

问题根源

报错指向book.service.ts第21行的response._embedded.books,说明API返回的响应中**_embedded字段不存在或未定义**。常见原因:

  • API接口返回格式不符合预期(如分类无图书时返回空对象,而非包含_embedded.books的结构)
  • 请求URL路径错误,导致API返回非预期结果

修复步骤

1. 验证API响应格式

直接通过浏览器或Postman访问${environment.apiUrl}/book/category/1,确认返回的JSON结构是否包含_embedded.books。正常响应示例:

{
  "_embedded": {
    "books": [
      { "id": 1, "name": "示例图书", "author": "作者", "imageUrl": "..." }
    ]
  }
}

如果返回的是直接的图书数组或无_embedded字段,需要调整Service的处理逻辑。

2. 添加空值保护逻辑

修改Service中的getBookList方法,使用可选链操作符和默认值避免崩溃:

getBookList(theCategoryId: number): Observable<Book[]> {
  const searchUrl = `${environment.apiUrl}/book/category/${theCategoryId}`;

  return this.httpClient.get<GetResponse>(searchUrl).pipe(
    map(response => response?._embedded?.books ?? [])
  );
}
  • ?.:避免访问undefined对象的属性时抛出错误
  • ?? []:当books为undefined或null时返回空数组,保证组件能接收到合法的数组类型

3. 检查并修正URL路径

确认environment.apiUrl的取值,避免路径重复。例如如果environment.apiUrl已经包含/book,拼接后会出现/api/book/book/category/1的错误路径,需要调整为:

const searchUrl = `${environment.apiUrl}/category/${theCategoryId}`;

4. 优化模板的空状态提示

在HTML中添加空数组的提示,提升用户体验:

<!-- 在columns容器内添加 -->
<div *ngIf="books.length === 0" class="column is-full has-text-centered">
  <p class="is-size-4 has-text-grey">该分类下暂无图书</p>
</div>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:57:51