【发布时间】:2021-09-07 17:04:32
【问题描述】:
我在我的 Angular 应用程序类型的 stackOverflow 上遇到错误。 (见最后更新)
基本上我的组件是一个包含两个组件的图表。 equipment 组件包含设备信息,socket 组件包含每个设备的连接类型。套接字组件基本上显示了一个下拉列表,其中包含可连接的设备。
“容器”组件基本上创建了一个类型为“起始元素”的套接字组件,因此我们有一个“假”连接来选择起始元素。从那时起,我们选择一个起始元素,我们可以继续输入图表。
当我选择第一个“开始元素”(pic1)时,相应的设备将加载其相应的插座(7 个单元)。然后我在其中一个插座上选择了一个设备(pic2),同样,它装载了一个单元,一切都很好。然后我在相应的套接字(pic3)中再选择一个设备,然后,应用程序崩溃了。
我收到此错误:
ERROR RangeError: Maximum call stack size exceeded
at refreshView (core.js:9461)
at refreshEmbeddedViews (core.js:10591)
at refreshView (core.js:9490)
at refreshComponent (core.js:10637)
at refreshChildComponents (core.js:9263)
at refreshView (core.js:9516)
at refreshEmbeddedViews (core.js:10591)
at refreshView (core.js:9490)
at refreshComponent (core.js:10637)
at refreshChildComponents (core.js:9263)
我制作了一些控制台日志,在发生错误的单击处,第一个开始元素被呈现(ngOnInit),第二个也是如此,在我单击的第三个中,Socket onInit 和 Equipment onInit 被调用没有结束。 看来设备和插座都在不停地调用自己。
我检查了我的应用程序,我只调用了两次套接字组件。一旦在容器组件上创建“开始元素”套接字,然后在设备组件上的 ngfor 上。而关于设备,它在整个应用程序中只调用一次,一旦mySocket.element被选中并且不为空,因此ngIf显示组件。
我只有一个模块app.module,所以这里一样,没有对组件的多次调用。
这里是socket组件:
import { Component, Input, OnInit } from '@angular/core';
import { Equipment } from 'src/app/model/Equipment';
import { Socket } from 'src/app/model/Socket';
import { EquipmentService } from 'src/app/services/equipment.service';
@Component({
selector: 'app-socket',
templateUrl: './socket.component.html',
styleUrls: ['./socket.component.css']
})
export class SocketComponent implements OnInit {
@Input() mySocket: Socket;
@Input() parentEquipment: Equipment;
myElements: Equipment[];
constructor( private _equipmentNewService:EquipmentService) { }
ngOnInit(): void {
console.log("Myparentequipment:", this.parentEquipment)
this.myElements = this._equipmentNewService.getListForGivenInput(this.mySocket.type);
}
loadEquipment(eq:Equipment){
this.mySocket.element = eq;
}
amIFirstConnection(){
return !this.amIOnlyConnection() && this.mySocket === this.parentEquipment.eqType.connections[0];
}
amILastConnection(){
return !this.amIOnlyConnection() && this.mySocket === this.parentEquipment.eqType.connections[this.parentEquipment.eqType.connections.length-1];
}
amIMiddleConnection(){
return !this.amIOnlyConnection() && !this.amIFirstConnection() && !this.amILastConnection();
}
amIOnlyConnection(){
return this.parentEquipment.eqType.connections.length===1
}
}
这里是html文件:
<!-- this is just to create the diagram lines, ignore it! -->
<div *ngIf="mySocket.type !== 'Start Element'">
<div *ngIf="amIOnlyConnection()" class="d-flex someMarginTop">
<div class="smallSpacer schema"></div>
<div class="mainSpacer schema borderLeft"></div>
</div>
<div *ngIf="amIFirstConnection()" class="d-flex someMarginTop">
<div class="smallSpacer schema"></div>
<div class="mainSpacer schema borderLeft borderTop"></div>
</div>
<div *ngIf="amIMiddleConnection()" class="d-flex someMarginTop">
<div class="smallSpacer schema borderTop"></div>
<div class="mainSpacer schema borderTop borderLeft"></div>
</div>
<div *ngIf="amILastConnection()" class="d-flex someMarginTop">
<div class="smallSpacer schema borderTop"></div>
<div class="mainSpacer schema borderLeft"></div>
</div>
</div>
<!-- here we display the info -->
<div class="dropdown someMargin">
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
{{mySocket.name}}...
</button>
<ul class="dropdown-menu listSocket" aria-labelledby="dropdownMenuButton1">
<li *ngFor="let item of myElements">
<div class="d-flex"><a class="dropdown-item" (click)="loadEquipment(item)"><span style="margin-right:5px">{{item.eqType.productNumber}}</span></a></div>
</li>
</ul>
<!-- and here the call to equipment -->
<div *ngIf="mySocket.element">
<app-equipment [equipment]="mySocket.element" [parentSocket]="this.mySocket"></app-equipment>
</div>
</div>
现在是设备组件:
import { Component, Input, OnInit } from '@angular/core';
import { Equipment } from 'src/app/model/Equipment';
import { Socket } from 'src/app/model/Socket';
@Component({
selector: 'app-equipment',
templateUrl: './equipment.component.html',
styleUrls: ['./equipment.component.css']
})
export class EquipmentComponent implements OnInit {
@Input() equipment: Equipment;
@Input() parentSocket: Socket;
constructor() {
}
ngOnInit(): void {
console.log("Myparentsocket:", this.parentSocket)
this.equipment.available = false;
}
removeMyselfFromSocket() {
this.parentSocket.element = null;
}
}
这里是html文件:
<div class="someMargin somePadding col-md-auto border rounded eqContainer animated fadeIn fast">
<div style="float:right">
<button type="button" class="btn btn-outline-danger px-3" style="padding: 0px 7px !important;" (click)="removeMyselfFromSocket()"><i class="fas fa-trash fa-xs"></i></button>
</div>
<div class="d-flex">
<div>
<!-- <img [src]="equipment.eqType.img" class="img-fluid imgTestModel" [alt]="equipment.eqType.productNumber"> -->
</div>
<div style="margin-left:5px" class="bg-light border rounded somePadding" style="margin-right:30px">
<!-- <p class="title p-lessMargin text-primary">{{equipment.eqType.productNumber}}</p> -->
<p class="ids p-lessMargin">ProductNO: <span class="ids-content">ABC8493RT</span> </p>
<p class="ids p-lessMargin">SerialNO: <span class="ids-content">15698247896</span></p>
<p class="ids p-lessMargin">SwRev: <span class="ids-content">2.2.145</span></p>
</div>
</div>
<div class="d-flex">
<app-socket *ngFor="let socket of equipment.eqType.connections" [mySocket]="socket" [parentEquipment]="equipment"></app-socket>
</div>
</div>
我希望我附上了足够的信息,我很乐意得到一些帮助!
提前致谢:)
更新 1: 在套接字组件上更改此方法后(以避免始终调用更改检测器):
equipmentSelected:Equipment;
loadEquipment(eq:Equipment){
this.equipmentSelected = eq;
}
我现在可以在我的树中添加更多孩子。现在的问题是,我该怎么做才能保留this.mySocket.element = eq?否则我会丢失我的树形结构并且无法序列化我的对象...
【问题讨论】:
-
识别问题并不容易。我看到两个主要的地方可能是原因:模板中的突变和函数不是那么容易被 Angular 变化检测器处理
-
这不是一个简单而简短的话题。需要时间去理解。这篇文章会有所帮助:blog.angular-university.io/…
-
非常感谢文章的链接!读完之后,我做了一个改动。在函数 loadEquipment() 中的 socket 组件中,而不是 this.mySocket.element = eq;我创建了一个新变量并这样做:this.equipmentSelected = eq;现在我需要弄清楚如何保留 this.mySocket.element 上的值,否则我会丢失树结构。你有什么想法吗?
-
您有 2 个相互引用的组件。这几乎肯定是您出现堆栈溢出的原因。我会仔细看看您是如何构建应用程序的,并考虑进行重组以避免这种循环引用。
-
所以最后在 loadEquipment() 中我只是使用了这样的对象的克隆:this.mySocket.element = JSON.parse(JSON.stringify(eq));现在它可以工作了。我不完全理解为什么它会这样工作,很高兴能从你们中的一个人那里得到一个很好的答案! :)
标签: javascript angular typescript