Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e3b41be
Initial plan
Copilot Jan 29, 2026
51da0bf
Add ChangeDetectorRef to fix ExpressionChangedAfterItHasBeenCheckedError
Copilot Jan 29, 2026
a73e7f2
Use markForCheck instead of detectChanges for better change detection
Copilot Jan 29, 2026
04dd2f4
Add clarifying comment to trigger CI pipeline
Copilot Jan 29, 2026
ba1c583
Fix multiple ExpressionChangedAfterItHasBeenCheckedError issues acros…
Copilot Jan 29, 2026
a2f1db3
Use detectChanges instead of markForCheck for default change detectio…
Copilot Jan 29, 2026
822e55a
Fix ExpressionChangedAfterItHasBeenCheckedError in 5 additional compo…
Copilot Jan 29, 2026
9312d14
Remove unnecessary detectChanges call from synchronous else branch
Copilot Jan 29, 2026
b4664bd
Fix FoldersComponent unit test by adding ChangeDetectorRef mock
Copilot Jan 29, 2026
364b07b
Fix TranslateService mock to return proper observables in FoldersComp…
Copilot Jan 29, 2026
e36d573
Add missing getParsedResult and getCurrentLang methods to TranslateSe…
Copilot Jan 29, 2026
135f325
Remove detectChanges() calls from EformsPageComponent to fix Angular …
Copilot Jan 29, 2026
c5c4a1e
Use markForCheck() instead of detectChanges() for Angular 21 compatib…
Copilot Jan 29, 2026
df76929
Fix mtx-select dropdownPosition attribute for Angular 21 compatibility
Copilot Jan 29, 2026
50db9eb
Fix all mtx-select dropdownPosition attributes across codebase for An…
Copilot Jan 29, 2026
1591db5
Fix TranslateService mocks in all unit tests for Angular 21 compatibi…
Copilot Jan 29, 2026
3105cfa
Fix TranslatePipe subscription errors by using EventEmitter for event…
Copilot Jan 29, 2026
e1c0271
Add complete TranslateService interface to all test mocks
Copilot Jan 29, 2026
677a689
Fix mtx-select dropdown z-index issues in modals for Angular 21
Copilot Jan 29, 2026
98754d8
Fix Angular 21 modal/dialog positioning and rendering issues
Copilot Jan 29, 2026
408d1a0
Fix modal backdrop to be semi-transparent instead of completely opaque
Copilot Jan 29, 2026
0fbca24
Revert appendTo="body" additions from modal components
Copilot Jan 29, 2026
ec0f939
Fix Angular 21 CDK overlay structure with nested backdrop
Copilot Jan 29, 2026
fa5dc4a
Fix popover top layer blocking dropdowns by using maximum z-index
Copilot Jan 29, 2026
8106a52
Revert all mtx-select and modal CSS/dialog fixes that broke tooltips
Copilot Jan 29, 2026
9c1cd41
Restore missing appendTo="body" attributes in modal dialog mtx-select…
Copilot Jan 29, 2026
ebb6efe
Fix appendTo syntax to use property binding per mtx-select documentation
Copilot Jan 29, 2026
49e54f6
Remove appendTo attribute to use default behavior with Angular 21 pop…
Copilot Jan 29, 2026
1176bc4
Implement custom OverlayContainer for Angular 21 popover top layer co…
Copilot Jan 29, 2026
aa112fe
Add automatic overlay container management in AppComponent for all di…
Copilot Jan 29, 2026
4c5f0c5
Apply proper Angular 21 solution: use appendTo='.cdk-overlay-pane' fo…
Copilot Jan 29, 2026
4ec6ffa
Remove appendTo attribute to fix dropdown positioning in Angular 21 m…
Copilot Jan 29, 2026
af263eb
Restore appendTo cdk-overlay-pane with CSS fix for correct dropdown p…
Copilot Jan 30, 2026
696f758
Remove position:fixed CSS hack per ng-select #2738 - use pure appendT…
Copilot Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NO_ERRORS_SCHEMA , EventEmitter } from '@angular/core';
import { MockTranslatePipe } from 'src/test-helpers';
import { TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs';
Expand All @@ -17,12 +17,22 @@ describe('EmailRecipientTagNewComponent', () => {
const mockTranslateService = {
instant: vi.fn((key: string) => key),
get: vi.fn((key: string) => of(key)),
use: vi.fn(),
use: vi.fn(() => of(null)),
setDefaultLang: vi.fn(),
getDefaultLang: vi.fn(() => 'en'),
addLangs: vi.fn(),
getLangs: vi.fn(() => ['en']),
getBrowserLang: vi.fn(() => 'en'),
getBrowserCultureLang: vi.fn(() => 'en'),
currentLang: 'en',
stream: vi.fn()
defaultLang: 'en',
stream: vi.fn((key: string) => of(key)),
getParsedResult: vi.fn((translations: any, key: string) => key),
getCurrentLang: vi.fn(() => 'en'),
onLangChange: new EventEmitter(),
onTranslationChange: new EventEmitter(),
onDefaultLangChange: new EventEmitter()
};
mockTranslateService.stream.mockReturnValue(of('Test'));
const mockDialogRef = {
close: vi.fn(),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NO_ERRORS_SCHEMA , EventEmitter } from '@angular/core';
import { MockTranslatePipe } from 'src/test-helpers';
import { TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs';
Expand All @@ -17,12 +17,22 @@ describe('EmailRecipientTagDeleteComponent', () => {
const mockTranslateService = {
instant: vi.fn((key: string) => key),
get: vi.fn((key: string) => of(key)),
use: vi.fn(),
use: vi.fn(() => of(null)),
setDefaultLang: vi.fn(),
getDefaultLang: vi.fn(() => 'en'),
addLangs: vi.fn(),
getLangs: vi.fn(() => ['en']),
getBrowserLang: vi.fn(() => 'en'),
getBrowserCultureLang: vi.fn(() => 'en'),
currentLang: 'en',
stream: vi.fn()
defaultLang: 'en',
stream: vi.fn((key: string) => of(key)),
getParsedResult: vi.fn((translations: any, key: string) => key),
getCurrentLang: vi.fn(() => 'en'),
onLangChange: new EventEmitter(),
onTranslationChange: new EventEmitter(),
onDefaultLangChange: new EventEmitter()
};
mockTranslateService.stream.mockReturnValue(of('Test'));
const mockDialogRef = {
close: vi.fn()
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NO_ERRORS_SCHEMA , EventEmitter } from '@angular/core';
import { MockTranslatePipe } from 'src/test-helpers';
import { TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs';
Expand All @@ -17,12 +17,22 @@ describe('EmailRecipientTagEditComponent', () => {
const mockTranslateService = {
instant: vi.fn((key: string) => key),
get: vi.fn((key: string) => of(key)),
use: vi.fn(),
use: vi.fn(() => of(null)),
setDefaultLang: vi.fn(),
getDefaultLang: vi.fn(() => 'en'),
addLangs: vi.fn(),
getLangs: vi.fn(() => ['en']),
getBrowserLang: vi.fn(() => 'en'),
getBrowserCultureLang: vi.fn(() => 'en'),
currentLang: 'en',
stream: vi.fn()
defaultLang: 'en',
stream: vi.fn((key: string) => of(key)),
getParsedResult: vi.fn((translations: any, key: string) => key),
getCurrentLang: vi.fn(() => 'en'),
onLangChange: new EventEmitter(),
onTranslationChange: new EventEmitter(),
onDefaultLangChange: new EventEmitter()
};
mockTranslateService.stream.mockReturnValue(of('Test'));
const mockDialogRef = {
close: vi.fn(),
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core';
import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild, inject, ChangeDetectorRef } from '@angular/core';
import {
EntityGroupEditModel,
EntityItemModel,
Expand Down Expand Up @@ -37,6 +37,7 @@ export class EntityEditCreateComponent implements OnInit, OnDestroy{
private location = inject(Location);
dialog = inject(MatDialog);
private overlay = inject(Overlay);
private cdr = inject(ChangeDetectorRef);

entityGroupEditModel: EntityGroupEditModel = new EntityGroupEditModel();
@ViewChild('frame', { static: true }) frame;
Expand Down Expand Up @@ -91,6 +92,8 @@ export class EntityEditCreateComponent implements OnInit, OnDestroy{
description: data.model.description,
}
this.actualizeEntityItemPositions();
// Trigger change detection after async update
this.cdr.detectChanges();
}
});
} else if (this.header === 'selectable'){
Expand All @@ -108,6 +111,8 @@ export class EntityEditCreateComponent implements OnInit, OnDestroy{
description: data.model.description,
}
this.actualizeEntityItemPositions();
// Trigger change detection after async update
this.cdr.detectChanges();
}
});
}
Expand Down
21 changes: 17 additions & 4 deletions eform-client/src/app/components/footer/footer.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, inject, OnInit, DestroyRef } from '@angular/core';
import { Component, inject, OnInit, DestroyRef, ChangeDetectorRef } from '@angular/core';
import { Store } from '@ngrx/store';
import {
selectCurrentUserFullName,
Expand Down Expand Up @@ -36,6 +36,7 @@ export class FooterComponent implements OnInit {
private authStore = inject(Store);
private destroyRef = inject(DestroyRef);
private selectCurrentUserClaims$ = this.authStore.select(selectCurrentUserClaims);
private cdr = inject(ChangeDetectorRef);

fullName = '';
userName = '';
Expand All @@ -46,15 +47,27 @@ export class FooterComponent implements OnInit {
ngOnInit() {
this.store.select(selectCurrentUserFullName)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(name => this.fullName = name);
.subscribe(name => {
this.fullName = name;
// Trigger change detection after async update
this.cdr.detectChanges();
});

this.store.select(selectCurrentUserName)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(user => this.userName = user);
.subscribe(user => {
this.userName = user;
// Trigger change detection after async update
this.cdr.detectChanges();
});

this.store.select(selectCurrentUserAvatarUrl)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(url => this.avatarUrl = url);
.subscribe(url => {
this.avatarUrl = url;
// Trigger change detection after async update
this.cdr.detectChanges();
});
}

checkGuards(guards: string[]): Observable<boolean> {
Expand Down
5 changes: 4 additions & 1 deletion eform-client/src/app/components/header/header.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit, inject } from '@angular/core';
import { Component, OnInit, inject, ChangeDetectorRef } from '@angular/core';
import {Router} from '@angular/router';
import {EventBrokerService} from 'src/app/common/helpers';
import {HeaderSettingsModel} from 'src/app/common/models/settings';
Expand All @@ -16,6 +16,7 @@ export class HeaderComponent implements OnInit {
private eventBrokerService = inject(EventBrokerService);
private settingsService = inject(AppSettingsService);
private router = inject(Router);
private cdr = inject(ChangeDetectorRef);

headerSettingsModel: HeaderSettingsModel = new HeaderSettingsModel;
logoImage: any;
Expand Down Expand Up @@ -46,6 +47,8 @@ export class HeaderComponent implements OnInit {
} else if (!this.headerSettingsModel.imageLink) {
this.logoImage = '../../../assets/images/logo.png';
}
// Trigger change detection after async update
this.cdr.detectChanges();
}
}));
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, HostListener, OnDestroy, OnInit, Renderer2, ViewChild, inject } from '@angular/core';
import { AfterViewInit, Component, HostListener, OnDestroy, OnInit, Renderer2, ViewChild, inject, ChangeDetectorRef } from '@angular/core';
import {AuthStateService} from 'src/app/common/store';
import {AutoUnsubscribe} from 'ngx-auto-unsubscribe';
import {Observable, of, Subscription, take, tap} from 'rxjs';
Expand Down Expand Up @@ -46,6 +46,7 @@ export class FullLayoutComponent implements OnInit, OnDestroy, AfterViewInit {
private eventBrokerService = inject(EventBrokerService);
private settingsService = inject(AppSettingsService);
private loaderService = inject(LoaderService);
private cdr = inject(ChangeDetectorRef);

@ViewChild('drawer') drawer: MatDrawer;
isDarkThemeAsync$: Subscription;
Expand Down Expand Up @@ -136,6 +137,8 @@ export class FullLayoutComponent implements OnInit, OnDestroy, AfterViewInit {
} else if (!this.headerSettingsModel.imageLink) {
this.logoImage = '../../../assets/images/logo.png';
}
// Trigger change detection after async update
this.cdr.detectChanges();
}
}));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, EventEmitter, OnDestroy, OnInit, Output, inject } from '@angular/core';
import { Component, EventEmitter, OnDestroy, OnInit, Output, inject, ChangeDetectorRef } from '@angular/core';
import {AutoUnsubscribe} from 'ngx-auto-unsubscribe';
import {Observable, Subscription} from 'rxjs';
import { MatTreeNestedDataSource, MatTree, MatTreeNodeDef, MatTreeNode, MatTreeNodePadding, MatNestedTreeNode, MatTreeNodeToggle, MatTreeNodeOutlet } from '@angular/material/tree';
Expand Down Expand Up @@ -34,6 +34,7 @@ interface MenuNode {
export class NavigationComponent implements OnInit, OnDestroy {
router = inject(Router);
private authStore = inject(Store);
private cdr = inject(ChangeDetectorRef);

@Output() clickOnLink: EventEmitter<void> = new EventEmitter<void>();

Expand All @@ -58,6 +59,8 @@ export class NavigationComponent implements OnInit, OnDestroy {
if (x.length > 0) {
this.menu.data = [...x];
this.restoreOpenedMenu();
// Trigger change detection after async update
this.cdr.detectChanges();
}
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
import { Component, OnDestroy, OnInit, inject, ChangeDetectorRef } from '@angular/core';
import {
EntityGroupModel,
Paged,
Expand Down Expand Up @@ -48,6 +48,7 @@ export class EntitySearchComponent implements OnInit, OnDestroy {
private dialog = inject(MatDialog);
private overlay = inject(Overlay);
private translateService = inject(TranslateService);
private cdr = inject(ChangeDetectorRef);

advEntitySearchableGroupListModel: Paged<EntityGroupModel> = new Paged<EntityGroupModel>();
entitySearchRemoveComponentAfterClosedSub$: Subscription;
Expand Down Expand Up @@ -84,6 +85,8 @@ export class EntitySearchComponent implements OnInit, OnDestroy {
if (data && data.model) {
this.advEntitySearchableGroupListModel = data.model;
this.store.dispatch(updateEntitySearchTotal({total: data.model.total}));
// Trigger change detection after async update
this.cdr.detectChanges();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
import { Component, OnDestroy, OnInit, inject, ChangeDetectorRef } from '@angular/core';
import {
Paged,
EntityGroupModel,
Expand Down Expand Up @@ -50,6 +50,7 @@ export class EntitySelectComponent implements OnInit, OnDestroy {
private dialog = inject(MatDialog);
private overlay = inject(Overlay);
private translateService = inject(TranslateService);
private cdr = inject(ChangeDetectorRef);

advEntitySelectableGroupListModel: Paged<EntityGroupModel> = new Paged<EntityGroupModel>();
entitySelectRemoveComponentAfterClosedSub$: Subscription;
Expand Down Expand Up @@ -87,6 +88,8 @@ export class EntitySelectComponent implements OnInit, OnDestroy {
if (data && data.model) {
this.advEntitySelectableGroupListModel = data.model;
this.store.dispatch(updateEntitySelectTotal({total: data.model.total}));
// Trigger change detection after async update
this.cdr.detectChanges();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NO_ERRORS_SCHEMA , EventEmitter } from '@angular/core';
import { FolderDeleteComponent } from './folder-delete.component';
import { FoldersService } from 'src/app/common/services';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
Expand All @@ -21,12 +21,22 @@ describe('FolderDeleteComponent', () => {
const mockTranslateService = {
instant: vi.fn((key: string) => key),
get: vi.fn((key: string) => of(key)),
use: vi.fn(),
use: vi.fn(() => of(null)),
setDefaultLang: vi.fn(),
getDefaultLang: vi.fn(() => 'en'),
addLangs: vi.fn(),
getLangs: vi.fn(() => ['en']),
getBrowserLang: vi.fn(() => 'en'),
getBrowserCultureLang: vi.fn(() => 'en'),
currentLang: 'en',
stream: vi.fn()
defaultLang: 'en',
stream: vi.fn((key: string) => of(key)),
getParsedResult: vi.fn((translations: any, key: string) => key),
getCurrentLang: vi.fn(() => 'en'),
onLangChange: new EventEmitter(),
onTranslationChange: new EventEmitter(),
onDefaultLangChange: new EventEmitter()
};
mockTranslateService.stream.mockReturnValue(of('Test'));
mockFoldersService = {
deleteSingleFolder: vi.fn(),
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NO_ERRORS_SCHEMA, ChangeDetectorRef, EventEmitter } from '@angular/core';
import { FoldersComponent } from './folders.component';
import { FoldersService } from 'src/app/common/services';
import { MatDialog } from '@angular/material/dialog';
Expand All @@ -23,16 +23,33 @@ describe('FoldersComponent', () => {
const mockTranslateService = {
instant: vi.fn((key: string) => key),
get: vi.fn((key: string) => of(key)),
use: vi.fn(),
use: vi.fn(() => of(null)),
setDefaultLang: vi.fn(),
getDefaultLang: vi.fn(() => 'en'),
addLangs: vi.fn(),
getLangs: vi.fn(() => ['en']),
getBrowserLang: vi.fn(() => 'en'),
getBrowserCultureLang: vi.fn(() => 'en'),
currentLang: 'en',
stream: vi.fn()
defaultLang: 'en',
stream: vi.fn((key: string) => of(key)),
getParsedResult: vi.fn((translations: any, key: string) => key),
getCurrentLang: vi.fn(() => 'en'),
onLangChange: new EventEmitter(),
onTranslationChange: new EventEmitter(),
onDefaultLangChange: new EventEmitter()
};
mockTranslateService.stream.mockReturnValue(of('Test'));
const mockAppMenuStateService = {
updateState: vi.fn(),
loadMobileMenuItems: vi.fn()
};
const mockChangeDetectorRef = {
markForCheck: vi.fn(),
detectChanges: vi.fn(),
checkNoChanges: vi.fn(),
detach: vi.fn(),
reattach: vi.fn()
};
mockFoldersService = {
getAllFolders: vi.fn(),
getAllFoldersList: vi.fn(),
Expand All @@ -54,6 +71,7 @@ describe('FoldersComponent', () => {
{ provide: Store, useValue: mockStore },
{ provide: TranslateService, useValue: mockTranslateService },
{ provide: AppMenuStateService, useValue: mockAppMenuStateService },
{ provide: ChangeDetectorRef, useValue: mockChangeDetectorRef },
{ provide: Overlay, useValue: { scrollStrategies: { reposition: () => ({}) } } }
],
schemas: [NO_ERRORS_SCHEMA]
Expand Down
Loading
Loading