首页 > 解决方案 > RangeError:Angular 中超出了最大调用堆栈大小

问题描述

我的 Angular 应用程序类型的 stackOverflow 出现错误。(见最后更新)

基本上我的组件是一个包含两个组件的图表。equipment包含设备信息的组件和socket组件,包括每个设备的连接类型。套接字组件基本上显示了一个下拉列表,其中包含可连接的设备。“容器”组件基本上创建了一个类型为“开始元素”的套接字组件,因此我们有一个“假”连接来选择一个开始元素。从那时起,我们选择一个起始元素,我们可以继续输入图表。当我选择第一个“开始元素”(pic1)时,相应的设备将加载其相应的插座(7 个单元)。然后我在其中一个插座上选择了一个设备(pic2),同样,它装载了一个单元,一切都很好。然后我在相应的套接字(pic3)中再选择一个设备,然后,应用程序崩溃了。 错误3!

我收到此错误:

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?否则我会丢失我的树结构并且无法序列化我的对象......

标签: javascriptangulartypescript

解决方案


在一位好朋友的帮助下,我找到了解决方案。它loadEquipment()位于套接字组件上的方法上。当它更改在此组件上创建的对象 mySocket 时,该组件被再次渲染,并且由于它是来自父套接字的输入,父套接字设备组件也被再次渲染,从而在加载相互引用的组件时创建堆栈溢出整个时间。

我需要将方法更改为:

loadEquipment(eq:Equipment){
    // js workaround for cloning
    this.mySocket.element = JSON.parse(JSON.stringify(eq));
} 

创建对象的克隆可以让更改检测器保持安静并避免重新渲染。


推荐阅读