Angular递归表格点击父行子行闪显问题求助
解决Angular递归表格子行显示后立即消失的问题
看起来你遇到的核心问题是原生DOM操作和Angular模板驱动渲染的冲突,再加上<a>标签的默认跳转行为没彻底阻止,导致子行刚显示就被重新渲染隐藏了。我给你一步步拆解修复方案:
1. 先堵上语法漏洞(你代码里的几个小错误)
先把你TypeScript代码里的语法问题修复,这些也可能导致组件异常:
// 修复后的TypeScript代码 sampletree() { var ele = <HTMLTableElement>document.getElementById("tableid"); ele.addEventListener("click", (ev: MouseEvent) => { var element = ev.target as HTMLElement; if (element.tagName !== "A") return false; ev.preventDefault(); var row = element.closest("tr"); var cls = row.classList[0]; var lvl = +(cls.slice(-1)) + 1; cls = cls.slice(0, -1) + lvl; while ((row = nextTr(row)) && (row.className.includes(cls) || row.className.includes("open"))) row.classList.toggle("open"); }); function nextTr(row) { while ((row = row.nextSibling) && row.nodeType != 1); return row; } } getTableParent1(): void { this.service.getTableParent1() .subscribe(TableParent1 => this.TableParent1 = TableParent1); // 补全闭合符号 } getChild(event: Event): void { event.preventDefault(); // 新增:阻止默认跳转 event.stopPropagation(); // 新增:阻止事件冒泡 this.service.getTablechild1() .subscribe(Tablechild1 => this.Tablechild1 = Tablechild1); // 修正变量名+补全闭合符号 } ngOnInit() { this.getTableParent1(); // 修正方法名 // this.sampletree(); 先注释掉,后面换成Angular的状态管理 }
2. 彻底阻止<a>标签的默认行为
你页面里的<a href="#">会触发页面跳转(回到顶部),这会触发Angular的变更检测甚至组件重新渲染,直接导致子行消失。修改HTML里的点击事件:
<!-- 父行的<a>标签修改 --> <a href="#" (click)="getChild($event)">{{parent.isOpen ? '-' : '+'}}</a>
3. 用Angular状态管理替代原生DOM操作
原生的addEventListener和类切换很容易和Angular的模板渲染冲突,建议给每个父行维护一个isOpen状态,用*ngIf控制子行显示:
第一步:给父行数据添加状态属性
getTableParent1(): void { this.service.getTableParent1() .subscribe(tableParent1 => { // 给每个父项添加isOpen状态,默认关闭 this.TableParent1 = tableParent1.map(item => ({...item, isOpen: false, childData: []})); }); }
第二步:修改HTML模板,用状态控制子行
如果你的子数据是和父行一一对应的,建议把子数据绑定到对应的父项上,避免所有子行同时显示:
<table class="table table-bordered table-hover" id="tableid"> <thead> <tr> <th>test1</th> <th>test2</th> </tr> </thead> <tbody> <!-- 父行 --> <tr class="level0" *ngFor="let parent of TableParent1"> <td> <a href="#" (click)="toggleChild(parent, $event)"> {{parent.isOpen ? '-' : '+'}} </a> {{parent.test2}} </td> <td>{{parent.test1}}</td> </tr> <!-- 子行:仅对应父行打开时显示 --> <ng-container *ngFor="let parent of TableParent1"> <tr class="level1" *ngFor="let child of parent.childData" *ngIf="parent.isOpen"> <td><a href="#" (click)="toggleGrandChild(child, $event)">+</a>{{child.test1}}</td> <td>{{child.test2}}</td> </tr> </ng-container> </tbody> </table>
第三步:实现状态切换和子数据加载方法
toggleChild(parent: any, event: Event): void { event.preventDefault(); event.stopPropagation(); // 切换展开/收起状态 parent.isOpen = !parent.isOpen; // 仅展开时加载子数据(避免重复请求) if (parent.isOpen && parent.childData.length === 0) { // 假设父行有唯一id,传给接口获取对应子数据 this.service.getTablechild1(parent.id) .subscribe(childData => { parent.childData = childData; }); } } // 子行的展开方法(如果需要递归) toggleGrandChild(child: any, event: Event): void { event.preventDefault(); event.stopPropagation(); // 同理,可以给子项添加isOpen状态,加载更深层级的数据 }
4. 简化CSS,交给Angular控制显示
移除原来原生DOM依赖的CSS规则,让*ngIf全权负责显示逻辑:
/* 保留基础样式 */ tr td { padding: 0.2em 0.4em; } tr td:first-of-type { position: relative; padding: 0.2em 1em; } tr td a { color: #0e879a; text-decoration: inherit; position: relative; left: -0.73em; font-size: 19px; } tr.level1 td:first-of-type { padding-left: 1.5em; } tr.level2 td:first-of-type { padding-left: 2em; } /* 移除原生display控制,交给*ngIf处理 */ tbody>tr { display: table-row; }
核心思路总结
避免混合原生DOM操作和Angular的模板渲染——Angular的变更检测会自动管理DOM,原生操作很容易打破这种平衡。用组件内部的状态(比如isOpen)来控制子行的显示/隐藏,同时彻底阻止<a>标签的默认跳转行为,就能解决子行显示后立即消失的问题了。
内容的提问来源于stack exchange,提问作者njeoo
相关产品推荐
相关产品推荐

