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.
Bootstrap error: RangeError: Maximum call stack size exceeded
Exception: error : Maximum call stack size exceeded
RangeError: Maximum call stack size exceeded
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"
]
}
}
}
}
}
}
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 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.
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 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>