You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Angular项目中Bootstrap Modal无法正确传递对应书籍数据的问题求助

嘿,我帮你找出问题啦!

你现在遇到的问题,根源在于DOM元素id的唯一性Bootstrap Modal的触发逻辑

  • 你在*ngFor循环里重复创建了多个id为exampleModal的Modal,但HTML里id必须是唯一的,浏览器只会识别第一个带这个id的元素。所以不管你点哪本书的Summary按钮,都会打开第一个Modal,自然显示的是第一本书的数据。
  • 另外,就算id不重复,直接在循环内的Modal里绑定{{item.name}}也会因为循环的变量作用域问题,导致Modal无法正确捕获对应书籍的数据。
最推荐的解决方案(Angular化实现)

咱们用Angular的方式来控制Modal,只需要一个Modal实例就够了,点击按钮时把对应书籍的数据存到一个变量里,再绑定到Modal上,既避免id冲突,又符合Angular的组件化思路。

第一步:在组件类里加个变量存选中的书

打开books.component.ts,添加一个变量来存储当前要展示的书籍:

export class BooksComponent implements OnInit {
  // ... 你原来的那些变量都保留
  public selectedBook: Book | undefined; // 新增:专门存要在Modal里展示的书

第二步:修改HTML里的按钮和Modal位置

  • 把原来按钮上的data-toggledata-target删掉,换成点击事件,用来设置选中的书籍并打开Modal。
  • 把整个Modal从*ngFor循环里面移出来,放到循环外面,这样整个页面只有一个Modal,不会重复生成。
  • 把Modal里的{{item.name}}{{item.summary}}改成绑定selectedBook的属性,还要用可选链?.避免初始化时报错。

修改后的HTML代码:

<body>
 <div class="search-hero">
 <input class="form-control" type="text" name="search" [(ngModel)]="searchText" autocomplete="off" placeholder="&#61442; Start searching for a book by name, price or type" />
 </div>
 <!-- 循环生成书籍卡片 -->
 <div class="container" *ngFor="let item of books | filter: searchText">
 <img src="{{ item.image }}" alt="img" class="image" />
 <div class="overlay overlayFade">
 <div class="text">
 <!-- 其他输入框和按钮都不变,只改Summary按钮 -->
 <button type="button" class="btn btn-primary" (click)="openSummaryModal(item)">
 Summary
 </button>
 <div class="alert alert-success" *ngIf="message">{{ message }}</div>
 </div>
 </div>
 </div>

 <!-- 把Modal移到循环外面,只保留一个 -->
 <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
 <div class="modal-dialog" role="document">
 <div class="modal-content">
 <div class="modal-header">
 <h5 class="modal-title" id="exampleModalLabel">{{selectedBook?.name}}</h5>
 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
 <span aria-hidden="true">&times;</span>
 </button>
 </div>
 <div class="modal-body">
 {{selectedBook?.summary}}
 </div>
 <div class="modal-footer">
 <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
 <button type="button" class="btn btn-primary">Save changes</button>
 </div>
 </div>
 </div>
 </div>
</body>

第三步:在组件类里加打开Modal的方法

回到books.component.ts,添加一个方法,负责设置选中的书籍,然后手动触发Modal显示:

// 新增方法:打开书籍详情Modal
openSummaryModal(book: Book) {
  this.selectedBook = book;
  // 用Bootstrap的Modal API手动打开Modal
  const modalElement = document.getElementById('exampleModal');
  if (modalElement) {
    // 这里用(window as any)是为了避免TypeScript报错,如果你全局引入了Bootstrap,就能用bootstrap.Modal
    const modal = new (window as any).bootstrap.Modal(modalElement);
    modal.show();
  }
}

小提示

  • selectedBook?.name这种可选链语法,是为了防止页面刚加载时selectedBook还没赋值,导致控制台报错。
  • 如果你项目里没有全局引入Bootstrap的JS文件,记得要先安装并导入,或者用ngx-bootstrap这种Angular封装的Bootstrap库,这样控制Modal会更顺手。
备选方案(多Modal方式,不推荐)

如果你非要给每本书都做一个Modal,那得给每个Modal设置唯一的id:

  1. 按钮的data-target改成动态绑定:[attr.data-target]="'#exampleModal-' + item.id"
  2. Modal的id改成[id]="'exampleModal-' + item.id"

修改后的按钮:

<button type="button" class="btn btn-primary" data-toggle="modal" [attr.data-target]="'#exampleModal-' + item.id">
 Summary
</button>

修改后的Modal:

<div class="modal fade" [id]="'exampleModal-' + item.id" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
 <!-- Modal内容和原来一样就行 -->
</div>

不过这种方式会生成很多Modal元素,书籍多了之后会影响页面性能,所以还是推荐第一种单Modal的方案。

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

火山引擎 最新活动