Skip to content

Commit 0879e02

Browse files
committed
feat: be zoneless!
1 parent bd81362 commit 0879e02

19 files changed

+139
-113
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## v17.3.0
4+
- Be Zoneless compatible before angular 20.
5+
36
## v17.2.0
47
- Drop `model()` in favor of `input()` where possible.
58

angular.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@
3232
"builder": "@angular-builders/jest:run",
3333
"options": {
3434
"configPath": "./jest.config.ts",
35-
"polyfills": [
36-
"zone.js",
37-
"zone.js/testing"
38-
],
3935
"tsConfig": "./tsconfig.spec.json",
4036
"watch": false
4137
}

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
"ng-packagr": "^19.0.0",
6262
"rxjs": "^7.8.0",
6363
"typescript": "~5.7.2",
64-
"typescript-eslint": "^8.18.0",
65-
"zone.js": "~0.15.0"
64+
"typescript-eslint": "^8.18.0"
6665
}
6766
}

src/lib/components/ng-http-loader.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div id="spinner"
2-
*ngIf="isVisible$ | async"
2+
*ngIf="isVisible()"
33
[class.backdrop]="backdrop()"
44
[style.opacity]="opacity()"
55
[ngStyle]="{'background-color': backdrop() ? backdropBackgroundColor() : 'transparent'}">

src/lib/components/ng-http-loader.component.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,27 @@
77
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
88
*/
99

10-
import { AsyncPipe, NgComponentOutlet, NgIf, NgStyle } from '@angular/common';
11-
import { Component, input, model, OnInit, Type } from '@angular/core';
10+
import { NgComponentOutlet, NgIf, NgStyle } from '@angular/common';
11+
import { Component, input, model, OnInit, Signal, Type } from '@angular/core';
1212
import { merge, Observable, partition, timer } from 'rxjs';
1313
import { debounce, distinctUntilChanged, switchMap, tap } from 'rxjs/operators';
1414
import { PendingRequestsInterceptorConfigurer } from '../services/pending-requests-interceptor-configurer.service';
1515
import { SpinnerVisibilityService } from '../services/spinner-visibility.service';
1616
import { Spinkit, SPINKIT_COMPONENTS } from '../spinkits';
17+
import { toSignal } from "@angular/core/rxjs-interop";
1718

1819
@Component({
1920
selector: 'ng-http-loader',
2021
standalone: true,
2122
templateUrl: './ng-http-loader.component.html',
2223
styleUrls: ['./ng-http-loader.component.scss'],
23-
imports: [SPINKIT_COMPONENTS, NgStyle, NgComponentOutlet, NgIf, AsyncPipe]
24+
imports: [SPINKIT_COMPONENTS, NgStyle, NgComponentOutlet, NgIf]
2425
})
2526
export class NgHttpLoaderComponent implements OnInit {
2627

2728
spinkit = Spinkit;
2829
isVisible$!: Observable<boolean>;
30+
isVisible: Signal<boolean | undefined>;
2931
visibleUntil = Date.now();
3032

3133
readonly backdrop = input<boolean>(true);
@@ -42,15 +44,6 @@ export class NgHttpLoaderComponent implements OnInit {
4244
readonly spinner = model<string | null>(Spinkit.skWave);
4345

4446
constructor(private pendingRequestsInterceptorConfigurer: PendingRequestsInterceptorConfigurer, private spinnerVisibility: SpinnerVisibilityService) {
45-
}
46-
47-
ngOnInit(): void {
48-
this.initIsvisibleObservable();
49-
this.nullifySpinnerIfEntryComponentIsDefined();
50-
this.initFilters();
51-
}
52-
53-
private initIsvisibleObservable(): void {
5447
const [showSpinner$, hideSpinner$] = partition(this.pendingRequestsInterceptorConfigurer.pendingRequestsStatus$, h => h);
5548

5649
this.isVisible$ = merge(
@@ -63,6 +56,12 @@ export class NgHttpLoaderComponent implements OnInit {
6356
distinctUntilChanged(),
6457
tap(h => this.updateExpirationDelay(h))
6558
);
59+
this.isVisible = toSignal(this.isVisible$);
60+
}
61+
62+
ngOnInit(): void {
63+
this.nullifySpinnerIfEntryComponentIsDefined();
64+
this.initFilters();
6665
}
6766

6867
private nullifySpinnerIfEntryComponentIsDefined(): void {

src/test/components/ng-http-loader.component.on-push.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import { HttpClient, provideHttpClient, withInterceptors } from '@angular/common/http';
1111
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
12-
import { ChangeDetectionStrategy, Component } from '@angular/core';
12+
import { ChangeDetectionStrategy, Component, provideExperimentalZonelessChangeDetection } from '@angular/core';
1313
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
1414
import { By } from '@angular/platform-browser';
1515
import { NgHttpLoaderComponent } from '../../lib/components/ng-http-loader.component';
@@ -32,16 +32,17 @@ describe('NgHttpLoaderComponent OnPush', () => {
3232
beforeEach(async () => {
3333
await TestBed.configureTestingModule({
3434
imports: [HostComponent],
35-
providers: [provideHttpClient(withInterceptors([pendingRequestsInterceptor$])), provideHttpClientTesting()]
35+
providers: [
36+
provideHttpClient(withInterceptors([pendingRequestsInterceptor$])),
37+
provideHttpClientTesting(),
38+
provideExperimentalZonelessChangeDetection(),
39+
]
3640
})
3741
.compileComponents();
38-
});
3942

40-
beforeEach(() => {
4143
fixture = TestBed.createComponent(HostComponent);
4244
http = TestBed.inject(HttpClient);
4345
httpMock = TestBed.inject(HttpTestingController);
44-
fixture.detectChanges();
4546
});
4647

4748
it('should work as expected when the host component has ChangeDetectionStrategy.OnPush', fakeAsync(() => {

src/test/components/ng-http-loader.component.outlet.spec.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,31 @@
99

1010
import { ComponentFixture, TestBed } from '@angular/core/testing';
1111
import { By } from '@angular/platform-browser';
12-
import { of } from 'rxjs';
1312
import { NgHttpLoaderComponent } from '../../lib/components/ng-http-loader.component';
1413
import { SkThreeBounceComponent } from '../../lib/components/sk-three-bounce/sk-three-bounce.component';
14+
import { provideExperimentalZonelessChangeDetection, signal } from "@angular/core";
1515

1616
describe('NgHttpLoaderComponentOutlet', () => {
1717
let component: NgHttpLoaderComponent;
1818
let fixture: ComponentFixture<NgHttpLoaderComponent>;
1919

2020
beforeEach(async () => {
2121
await TestBed.configureTestingModule({
22-
imports: [NgHttpLoaderComponent]
22+
imports: [NgHttpLoaderComponent],
23+
providers: [
24+
provideExperimentalZonelessChangeDetection(),
25+
]
2326
})
2427
.compileComponents();
25-
});
2628

27-
beforeEach(() => {
2829
fixture = TestBed.createComponent(NgHttpLoaderComponent);
2930
component = fixture.componentInstance;
30-
fixture.detectChanges();
3131
});
3232

33-
it('should be possible to specify an entryComponent', () => {
34-
component.isVisible$ = of(true);
33+
it('should be possible to specify an entryComponent', async () => {
34+
component.isVisible = signal(true);
3535
fixture.componentRef.setInput('entryComponent', SkThreeBounceComponent);
36-
fixture.detectChanges();
36+
await fixture.whenStable();
3737

3838
const element = fixture
3939
.debugElement

src/test/components/ng-http-loader.component.spec.ts

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import { HttpClient, provideHttpClient, withInterceptors } from '@angular/common/http';
1111
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
1212
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
13+
import { provideExperimentalZonelessChangeDetection, signal } from '@angular/core';
1314
import { By } from '@angular/platform-browser';
1415
import { forkJoin, Observable, of, Subscription } from 'rxjs';
1516
import { NgHttpLoaderComponent } from '../../lib/components/ng-http-loader.component';
@@ -29,19 +30,20 @@ describe('NgHttpLoaderComponent', () => {
2930
beforeEach(async () => {
3031
await TestBed.configureTestingModule({
3132
imports: [NgHttpLoaderComponent],
32-
providers: [provideHttpClient(withInterceptors([pendingRequestsInterceptor$])), provideHttpClientTesting()]
33+
providers: [
34+
provideHttpClient(withInterceptors([pendingRequestsInterceptor$])),
35+
provideHttpClientTesting(),
36+
provideExperimentalZonelessChangeDetection(),
37+
]
3338
})
3439
.compileComponents();
35-
});
3640

37-
beforeEach(() => {
3841
fixture = TestBed.createComponent(NgHttpLoaderComponent);
3942
component = fixture.componentInstance;
4043
http = TestBed.inject(HttpClient);
4144
httpMock = TestBed.inject(HttpTestingController);
4245
spinner = TestBed.inject(SpinnerVisibilityService);
4346
isVisible = false;
44-
fixture.detectChanges();
4547
isVisibleSubscription = component.isVisible$.subscribe(v => isVisible = v);
4648
});
4749

@@ -53,9 +55,9 @@ describe('NgHttpLoaderComponent', () => {
5355
expect(component).toBeTruthy();
5456
});
5557

56-
it('should create the ng-http-loader component with default values', () => {
57-
component.isVisible$ = of(true);
58-
fixture.detectChanges();
58+
it('should create the ng-http-loader component with default values', async () => {
59+
component.isVisible = signal(true);
60+
await fixture.whenStable();
5961

6062
const element = fixture
6163
.debugElement
@@ -65,10 +67,10 @@ describe('NgHttpLoaderComponent', () => {
6567
expect(element.className).toBe('sk-wave colored');
6668
});
6769

68-
it('should not set the colored class if background-color is defined', () => {
69-
component.isVisible$ = of(true);
70+
it('should not set the colored class if background-color is defined', async () => {
71+
component.isVisible = signal(true);
7072
fixture.componentRef.setInput('backgroundColor', '#ff0000');
71-
fixture.detectChanges();
73+
await fixture.whenStable();
7274

7375
const element = fixture
7476
.debugElement
@@ -86,10 +88,10 @@ describe('NgHttpLoaderComponent', () => {
8688
expect(element).toBeNull();
8789
});
8890

89-
it('should be able to specify another known spinner', () => {
90-
component.isVisible$ = of(true);
91+
it('should be able to specify another known spinner', async () => {
92+
component.isVisible = signal(true);
9193
component.spinner.set(Spinkit.skRotatingPlane);
92-
fixture.detectChanges();
94+
await fixture.whenStable();
9395

9496
const element = fixture
9597
.debugElement
@@ -99,10 +101,10 @@ describe('NgHttpLoaderComponent', () => {
99101
expect(element.className).toBe('sk-rotating-plane colored-parent');
100102
});
101103

102-
it('should allow us to specify a custom background-color', () => {
103-
component.isVisible$ = of(true);
104+
it('should allow us to specify a custom background-color', async () => {
105+
component.isVisible = signal(true);
104106
fixture.componentRef.setInput('backgroundColor', '#ff0000');
105-
fixture.detectChanges();
107+
await fixture.whenStable();
106108

107109
const element = fixture
108110
.debugElement
@@ -749,9 +751,9 @@ describe('NgHttpLoaderComponent', () => {
749751
expect(isVisible).toBeFalsy();
750752
}));
751753

752-
it('should set the backdrop CSS class by default', () => {
753-
component.isVisible$ = of(true);
754-
fixture.detectChanges();
754+
it('should set the backdrop CSS class by default', async () => {
755+
component.isVisible = signal(true);
756+
await fixture.whenStable();
755757

756758
const element = fixture
757759
.debugElement
@@ -761,10 +763,10 @@ describe('NgHttpLoaderComponent', () => {
761763
expect(element).toBeTruthy();
762764
});
763765

764-
it('should be possible to remove the backdrop CSS class', () => {
766+
it('should be possible to remove the backdrop CSS class', async () => {
765767
component.isVisible$ = of(true);
766768
fixture.componentRef.setInput('backdrop', false);
767-
fixture.detectChanges();
769+
await fixture.whenStable();
768770

769771
const element = fixture
770772
.debugElement
@@ -773,9 +775,9 @@ describe('NgHttpLoaderComponent', () => {
773775
expect(element).toBeNull();
774776
});
775777

776-
it('should have a default opacity', () => {
777-
component.isVisible$ = of(true);
778-
fixture.detectChanges();
778+
it('should have a default opacity', async () => {
779+
component.isVisible = signal(true);
780+
await fixture.whenStable();
779781

780782
const element: HTMLElement = fixture
781783
.debugElement
@@ -785,10 +787,10 @@ describe('NgHttpLoaderComponent', () => {
785787
expect(element.style.opacity).toBe(`0${component.opacity()}`);
786788
});
787789

788-
it('should be possible to override opacity', () => {
789-
component.isVisible$ = of(true);
790+
it('should be possible to override opacity', async () => {
791+
component.isVisible = signal(true);
790792
fixture.componentRef.setInput('opacity', '.3');
791-
fixture.detectChanges();
793+
await fixture.whenStable();
792794

793795
const element: HTMLElement = fixture
794796
.debugElement
@@ -798,9 +800,9 @@ describe('NgHttpLoaderComponent', () => {
798800
expect(element.style.opacity).toBe(`0${component.opacity()}`);
799801
});
800802

801-
it('should have a default backdrop background color if backdrop is true', () => {
802-
component.isVisible$ = of(true);
803-
fixture.detectChanges();
803+
it('should have a default backdrop background color if backdrop is true', async() => {
804+
component.isVisible = signal(true);
805+
await fixture.whenStable();
804806

805807
const element: HTMLElement = fixture
806808
.debugElement
@@ -810,10 +812,10 @@ describe('NgHttpLoaderComponent', () => {
810812
expect(element.style.backgroundColor).toBe('rgb(241, 241, 241)');
811813
});
812814

813-
it('should be possible to override backdrop background color when backdrop is true', () => {
814-
component.isVisible$ = of(true);
815+
it('should be possible to override backdrop background color when backdrop is true', async () => {
816+
component.isVisible = signal(true);
815817
fixture.componentRef.setInput('backdropBackgroundColor', '#777777');
816-
fixture.detectChanges();
818+
await fixture.whenStable();
817819

818820
const element: HTMLElement = fixture
819821
.debugElement
@@ -823,10 +825,10 @@ describe('NgHttpLoaderComponent', () => {
823825
expect(element.style.backgroundColor).toBe('rgb(119, 119, 119)');
824826
});
825827

826-
it('should not have a transparent backdrop background color if backdrop is false', () => {
827-
component.isVisible$ = of(true);
828+
it('should not have a transparent backdrop background color if backdrop is false', async () => {
829+
component.isVisible = signal(true);
828830
fixture.componentRef.setInput('backdrop', false);
829-
fixture.detectChanges();
831+
await fixture.whenStable();
830832

831833
const element: HTMLElement = fixture
832834
.debugElement

0 commit comments

Comments
 (0)