0

Issue

I'm updating an application from Angular 4 to Angular 18 with Django as the backend. When i build the application it gives a infinity loop with the app.component.ts. Added some debuggers and it run the constructor on a loop. But it only happens when i add the . If i add the content of the index without it the loop doesn't happen.

When looping the developers tools is blank and no console show.

Errors

Bootstrap error: RangeError: Maximum call stack size exceeded

Exception: error : Maximum call stack size exceeded

RangeError: Maximum call stack size exceeded

App Flow

Environment

  • Angular CLI: 18.2.4
  • Node: 18.20.4
  • npm: 10.7.0
  • OS: Linux x64

Configuration

Here's my angular.json file:


{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "static": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser-esbuild",
          "options": {
            "allowedCommonJsDependencies": ["handsontable"],
            "outputPath": "src/dist/dev",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": [
              "zone.js",
              "@angular/localize/init"
            ],
            "tsConfig": "tsconfig.app.json",
            "assets": [
              {
                "glob": "**/*",
                "input": "public",
                "output": "/assets"
              }
            ],
            "styles": [
              "src/app/shared/styles.css",
              "handsontable/custom_handsontable.css",
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "node_modules/font-awesome/css/font-awesome.min.css",
              "node_modules/handsontable/dist/handsontable.full.min.css"

            ],
            "scripts": [
              "node_modules/jquery/dist/jquery.min.js",
              "node_modules/bootstrap/dist/js/bootstrap.min.js"
            ]
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "3mb",
                  "maximumError": "3mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "4kb"
                }
              ],
              "outputHashing": "none",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ]
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "buildTarget": "static:build:production"
            },
            "development": {
              "buildTarget": "static:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "buildTarget": "static:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "polyfills": [
              "zone.js",
              "zone.js/testing",
              "@angular/localize/init"
            ],
            "tsConfig": "tsconfig.spec.json",
            "assets": [
              {
                "glob": "**/*",
                "input": "public",
                "output": "/assets"
              }
            ],
            "styles": [
              "src/app/shared/styles.css",
              "handsontable/custom_handsontable.css",
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "node_modules/font-awesome/css/font-awesome.min.css",
              "node_modules/handsontable/dist/handsontable.full.min.css"
            ],
            "scripts": [
              "node_modules/jquery/dist/jquery.min.js",
              "node_modules/bootstrap/dist/js/bootstrap.min.js"
            ]
          }
        }
      }
    }
  }
}

Code

app.module.ts


import { ErrorHandler, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app.routing';
import { GlobalErrorHandlerService } from './shared/global-error-handler-service';
import { SharedModule } from './shared/shared.module';
import { ProgramsModule } from './programs/programs.module';
import { TeamsModule } from './teams/teams.module';
import { AllocationsModule } from './allocations/allocations.module';
import { ExportModule } from './export/export.module';
import { ValidationModule } from './validation/validation.module';
import { ProjectsService } from 'src/services';


@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    FormsModule,
    BrowserAnimationsModule, // Required for animations
    ToastrModule.forRoot({
      timeOut: 3000,
      positionClass: 'toast-top-right',
      preventDuplicates: true,
    }), // ToastrModule added
    ProgramsModule,
    TeamsModule,
    AllocationsModule,
    ValidationModule,
    ExportModule,
    SharedModule,
    RouterModule,
    AppRoutingModule,
  ],
  providers: [{ provide: ErrorHandler, useClass: GlobalErrorHandlerService }, ProjectsService],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule {
  constructor() {
    console.log('AppModule loaded');
    debugger;
  }
}


app.component.ts

import { filter } from 'rxjs/operators';
import '../utils';
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';

import {
  ResourcesService,
  TeamsService,
  AllocationsService,
  ScrumTeamService,
  ProjectsService,
  BusinessUnitService,
  BusinessLineService,
  BusinessCategoryService,
  GlobalListenersService,
  ProductClassesService,
  ExportService,
  SessionService,
  ADUserQueryService,
  HeaderService,
  ResourceFunctionsService,
  ResourceCostCentersService,
  InitiativesService,
} from '../services';

@Component({
  selector: 'app-root',
  templateUrl: '../index.html',
  providers: [
    GlobalListenersService,
    ResourcesService,
    TeamsService,
    AllocationsService,
    ScrumTeamService,
    BusinessUnitService,
    BusinessLineService,
    BusinessCategoryService,
    ProjectsService,
    ProductClassesService,
    InitiativesService,
    HeaderService,
    ExportService,
    SessionService,
    ADUserQueryService,
    ResourceFunctionsService,
    ResourceCostCentersService,
  ],
})
export class AppComponent {
  protected activeRouteTitle!: string;
  protected menuToggle = false;
  protected supported = true;
  private initCount = 0;

  constructor(private route: ActivatedRoute, private router: Router) {
    console.log("App Component Constructor");
    this.router.events
      .pipe(filter((event) => event instanceof NavigationEnd))
      .subscribe(() => {
        console.log('Navigation Start');
        debugger;
        let currentRoute = this.route.root;
        console.log('Current Route', currentRoute);
        while (
          currentRoute.children[0] !== undefined &&
          currentRoute.snapshot.data['title'] == undefined
        ) {
          console.log('Navigation While Loop');
          currentRoute = currentRoute.children[0];
          break;
        }
        console.log('Navigation URL Choosen');
        this.activeRouteTitle = currentRoute.snapshot.data['title'];
      });
    debugger;
  }

  ngOnInit() {
    if (navigator.userAgent.indexOf('.NET') != -1) {
      this.supported = false;
    }
    console.log("App Component NgOnInit");
    debugger;
  }

  ngOnDestroy() {
    console.log('AppComponent destroyed');
    debugger;
  }

  toggleMenu(event: any) {
    let el = event.target;
    if (el.classList.contains('toggle-nav')) {
      return;
    }
    this.menuToggle = el.id == 'menu-toggler' ? !this.menuToggle : false;
  }
}

app.routing.ts:


import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AllocationsComponent } from './allocations/allocations.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { CanDeactivateGuard } from './shared/routing.guards';

const routes: Routes = [
  { path: '', redirectTo: '/allocations', pathMatch: 'full' },
  { 
    path: 'allocations', 
    component: AllocationsComponent, 
    canDeactivate: [CanDeactivateGuard],
    data: { title: 'Allocations' }
  },
  { path: '**', component: PageNotFoundComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { enableTracing: true })],
  exports: [RouterModule]
})
export class AppRoutingModule {
  constructor() {
    console.log('AppRoutingModule loaded');
  }
}


index.html


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>App</title>
  <base href="/static/src/dist/dev/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="shortcut icon" sizes="16x16" href="/public/favicon.ico" type="image/jpeg" />

</head>

<body>
  <app-root>
    <nav id="header" class="navbar navbar-expand-md" (click)="toggleMenu($event)">
      <div class="navbar-brand">{{ activeRouteTitle }}</div>
      <ul id="menu-navbar" class="navbar-nav mr-auto main-menu toggle-nav" [class.active]="menuToggle">
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/allocations" routerLinkActive="active"><span>Allocations</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/validation" routerLinkActive="active"><span>Validation</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/groups" routerLinkActive="active"><span>Resources</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/projects" routerLinkActive="active"><span>Projects</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/export" routerLinkActive="active"><span>Export</span></a>
        </li>
      </ul>
      <div class="menu-hamburger"><i id="menu-toggler" class="fa fa-bars" aria-hidden="true"></i></div>
    </nav>
    <div *ngIf="!supported" class="ie-info"><span>Internet Explorer is not a&nbsp;supported browser. </span></div>
    <div class="ispinner ispinner--gray ispinner--animating ispinner--large">
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
    </div>
    <div (click)="menuToggle = false">
      <ng-template ngbModalContainer></ng-template>
      <router-outlet></router-outlet>
    </div>
  </app-root>
</body>
</html>

Tried removing from app.module.ts the bootstrap AppComponent and loop is gonne, but system don't work.

2
  • sounds like AppComponent tries to render itself recursively. do you have <app-root in the template of app component? Commented Nov 2, 2024 at 17:48
  • The name is wrong but it’s using the index that I include on the question. There is only the on that holds basically the body. If I add only the tags without the app-root don’t loop but don’t render correctly. Commented Nov 3, 2024 at 0:29

1 Answer 1

0

As the comment says first create a app html file, then reference that instead, the index.html is auto rendered.

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  providers: [
    ...

Then make sure you have eouter-outlet set in the app.component.html. This is used to render the routing component.

<nav id="header" class="navbar navbar-expand-md" (click)="toggleMenu($event)">
      <div class="navbar-brand">{{ activeRouteTitle }}</div>
      <ul id="menu-navbar" class="navbar-nav mr-auto main-menu toggle-nav" [class.active]="menuToggle">
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/allocations" routerLinkActive="active"><span>Allocations</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/validation" routerLinkActive="active"><span>Validation</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/groups" routerLinkActive="active"><span>Resources</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/projects" routerLinkActive="active"><span>Projects</span></a>
        </li>
        <li class="nav-item toggle-nav">
          <a class="nav-link" routerLink="/export" routerLinkActive="active"><span>Export</span></a>
        </li>
      </ul>
      <div class="menu-hamburger"><i id="menu-toggler" class="fa fa-bars" aria-hidden="true"></i></div>
    </nav>
    <div *ngIf="!supported" class="ie-info"><span>Internet Explorer is not a&nbsp;supported browser. </span></div>
    <div class="ispinner ispinner--gray ispinner--animating ispinner--large">
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
      <div class="ispinner__blade"></div>
    </div>
    <div (click)="menuToggle = false">
      <ng-template ngbModalContainer></ng-template>
      <router-outlet></router-outlet>
    </div>

The index.html should contain only the app-root selector and nothing else.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>App</title>
  <base href="/static/src/dist/dev/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="shortcut icon" sizes="16x16" href="/public/favicon.ico" type="image/jpeg" />

</head>

<body>
  <app-root></app-root>
</body>
</html>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.