Angular 4父传子组件编辑异常:点击子组件编辑始终显示首个团队数据
问题原因分析
这个问题的核心是重复DOM ID导致的选择器冲突:
你每个<app-team-box>子组件里都渲染了一个id为edit_team_modal的模态框,当页面上存在多个相同id的元素时,jQuery的$("#edit_team_modal")只会选中第一个匹配的元素。所以不管你点击哪个团队的编辑按钮,打开的都是第一个子组件里的模态框,自然显示的是第一个团队的数据。
解决方案
下面提供几种靠谱的解决思路,按推荐程度排序:
方案1:使用Angular模板引用变量替代jQuery ID选择器
这种方案不需要改动太多结构,只是避免重复id的问题:
修改子组件模板(team-box.component.html)
给模态框添加模板引用变量,去掉固定id:
<div class="ibox"> <div class="ibox-title"> <h5>{{team.name}}</h5> <a class="btn btn-xs" (click)="openEditTeamModal()">Edit</a> </div> </div> <!-- 用模板引用变量#editModal替代固定id --> <div class="modal fade" #editModal> <div class="modal-dialog"> <div class="modal-content"> <form role="form" (ngSubmit)="editTeam()" novalidate> <div class="modal-body"> <div class="row" *ngIf="team"> <label>Team name:</label> <input type="text" id="name" name="name" [(ngModel)]="team.name" #name="ngModel"> </div> </div> <div class="modal-footer"> <button type="submit">Save</button> </div> </form> </div> </div> </div>
修改子组件逻辑(team-box.component.ts)
用@ViewChild获取模板引用的模态框,避免全局选择器:
import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core'; // ...其他导入 export class TeamBoxComponent implements OnInit { @Input() team; // 获取模板引用的模态框元素 @ViewChild('editModal') editModal: ElementRef; constructor(private teamService: TeamService) {} openEditTeamModal(): void { // 直接操作当前组件的模态框,不再用全局ID选择器 $(this.editModal.nativeElement).modal(); $(".modal").appendTo("html"); } editTeam(): void { this.teamService.update(this.team).subscribe((team: Response) => { $(this.editModal.nativeElement).modal("hide"); }); } }
方案2:改用Angular专属的模态框组件(推荐)
尽量避免在Angular项目中混用jQuery操作DOM,推荐使用Angular生态的模态框组件,比如ng-bootstrap的NgbModal,它会动态创建独立的模态框实例,从根源上避免ID冲突:
步骤1:创建独立的模态框内容组件
比如新建edit-team-modal.component.ts和对应的模板:
import { Component, Input } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; @Component({ template: ` <div class="modal-header"> <h4 class="modal-title">Edit Team</h4> <button type="button" class="close" (click)="activeModal.dismiss('Cross click')"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <label>Team name:</label> <input type="text" [(ngModel)]="team.name"> </div> <div class="modal-footer"> <button type="button" class="btn btn-primary" (click)="activeModal.close(team)">Save</button> <button type="button" class="btn btn-secondary" (click)="activeModal.dismiss('Cancel click')">Cancel</button> </div> ` }) export class EditTeamModalComponent { @Input() team; constructor(public activeModal: NgbActiveModal) {} }
步骤2:修改子组件逻辑
import { Component, OnInit, Input } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { EditTeamModalComponent } from './edit-team-modal.component'; // ...其他导入 export class TeamBoxComponent implements OnInit { @Input() team; constructor(private teamService: TeamService, private modalService: NgbModal) {} openEditTeamModal(): void { const modalRef = this.modalService.open(EditTeamModalComponent); modalRef.componentInstance.team = { ...this.team }; // 传递副本避免直接修改原数据 modalRef.result.then((updatedTeam) => { // 用户点击保存,调用更新接口 this.teamService.update(updatedTeam).subscribe(() => { // 更新原组件的team数据 Object.assign(this.team, updatedTeam); }); }, () => { // 用户取消,无需操作 }); } }
方案3:将模态框移到父组件
把模态框统一放在父组件中,子组件点击编辑时通过事件通知父组件传递选中的team:
父组件模板(team.component.html)
<app-team-box *ngFor="let team of teams" [team]="team" (edit)="openEditModal($event)"></app-team-box> <!-- 父组件中的唯一模态框 --> <div class="modal fade" id="edit_team_modal"> <div class="modal-dialog"> <div class="modal-content"> <form role="form" (ngSubmit)="saveEdit()" novalidate> <div class="modal-body"> <div class="row" *ngIf="selectedTeam"> <label>Team name:</label> <input type="text" id="name" name="name" [(ngModel)]="selectedTeam.name" #name="ngModel"> </div> </div> <div class="modal-footer"> <button type="submit">Save</button> </div> </form> </div> </div> </div>
父组件逻辑(team.component.ts)
import { Component } from '@angular/core'; // ...其他导入 @Component({ selector: 'app-team', templateUrl: './team.component.html' }) export class TeamComponent { teams = [...]; // 你的团队数组 selectedTeam; constructor(private teamService: TeamService) {} openEditModal(team): void { this.selectedTeam = { ...team }; // 复制一份,避免直接修改原数据 $("#edit_team_modal").modal(); } saveEdit(): void { this.teamService.update(this.selectedTeam).subscribe(() => { $("#edit_team_modal").modal("hide"); // 找到原数组中的团队并更新 const index = this.teams.findIndex(t => t.id === this.selectedTeam.id); if (index !== -1) { this.teams[index] = { ...this.selectedTeam }; } }); } }
子组件修改(team-box.component.ts)
添加输出事件:
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; // ...其他导入 export class TeamBoxComponent implements OnInit { @Input() team; @Output() edit = new EventEmitter(); constructor() {} openEditTeamModal(): void { this.edit.emit(this.team); } }
内容的提问来源于stack exchange,提问作者Aleksandar Pupovac
相关产品推荐
相关产品推荐

