Skip to content

Commit baf6e0f

Browse files
committed
Add SpinnerVisibilityService to handle spinner toggling manually
1 parent 05c58fb commit baf6e0f

File tree

5 files changed

+88
-10
lines changed

5 files changed

+88
-10
lines changed

src/components/spinner/spinner.component.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { PendingInterceptorService } from '../../services/pending-interceptor.se
1414
import { timer } from 'rxjs/observable/timer';
1515
import { Observable } from 'rxjs/Observable';
1616
import { debounce } from 'rxjs/operators';
17+
import { SpinnerVisibilityService } from '../../services/spinner-visibility.service';
1718

1819
@Component({
1920
selector: 'spinner',
@@ -22,9 +23,10 @@ import { debounce } from 'rxjs/operators';
2223
})
2324
export class SpinnerComponent implements OnDestroy, OnInit {
2425
public isSpinnerVisible: boolean;
25-
private subscription: Subscription;
26-
public Spinkit = Spinkit;
26+
private pendingSubscription: Subscription;
27+
private visibilitySubscription: Subscription;
2728

29+
public Spinkit = Spinkit;
2830
@Input()
2931
public backgroundColor: string;
3032
@Input()
@@ -36,13 +38,19 @@ export class SpinnerComponent implements OnDestroy, OnInit {
3638
@Input()
3739
public entryComponent: any = null;
3840

39-
constructor(private pendingRequestInterceptorService: PendingInterceptorService) {
40-
this.subscription = this.pendingRequestInterceptorService
41+
constructor(private pendingInterceptorService: PendingInterceptorService, private spinnerVisibilityService: SpinnerVisibilityService) {
42+
this.pendingSubscription = this.pendingInterceptorService
4143
.pendingRequestsStatus
4244
.pipe(debounce(this.handleDebounce.bind(this)))
43-
.subscribe(hasPendingRequests => {
44-
this.isSpinnerVisible = hasPendingRequests;
45-
});
45+
.subscribe(this.handleSpinnerVisibility().bind(this));
46+
47+
this.visibilitySubscription = this.spinnerVisibilityService
48+
.visibilitySubject
49+
.subscribe(this.handleSpinnerVisibility().bind(this));
50+
}
51+
52+
private handleSpinnerVisibility(): (v: boolean) => void {
53+
return v => this.isSpinnerVisible = v;
4654
}
4755

4856
ngOnInit(): void {
@@ -54,7 +62,7 @@ export class SpinnerComponent implements OnDestroy, OnInit {
5462

5563
if (!!this.filteredUrlPatterns.length) {
5664
this.filteredUrlPatterns.forEach(e => {
57-
this.pendingRequestInterceptorService.filteredUrlPatterns.push(new RegExp(e));
65+
this.pendingInterceptorService.filteredUrlPatterns.push(new RegExp(e));
5866
});
5967
}
6068
}
@@ -65,9 +73,9 @@ export class SpinnerComponent implements OnDestroy, OnInit {
6573
}
6674
}
6775

68-
6976
ngOnDestroy(): void {
70-
this.subscription.unsubscribe();
77+
this.pendingSubscription.unsubscribe();
78+
this.visibilitySubscription.unsubscribe();
7179
}
7280

7381
private handleDebounce(hasPendingRequests: boolean): Observable<number> {

src/services/ng-http-loader-services.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { NgModule } from '@angular/core';
1111
import { CommonModule } from '@angular/common';
1212
import { HTTP_INTERCEPTORS } from '@angular/common/http';
1313
import { PendingInterceptorService, PendingInterceptorServiceFactoryProvider } from './pending-interceptor.service';
14+
import { SpinnerVisibilityServiceFactoryProvider } from './spinner-visibility.service';
1415

1516
const PendingInterceptorServiceExistingProvider = {
1617
provide: HTTP_INTERCEPTORS,
@@ -25,6 +26,7 @@ const PendingInterceptorServiceExistingProvider = {
2526
providers: [
2627
PendingInterceptorServiceExistingProvider,
2728
PendingInterceptorServiceFactoryProvider,
29+
SpinnerVisibilityServiceFactoryProvider,
2830
],
2931
})
3032
export class NgHttpLoaderServicesModule {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
4+
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
5+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
6+
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8+
*/
9+
10+
import { Injectable } from '@angular/core';
11+
import { Subject } from 'rxjs/Subject';
12+
13+
@Injectable()
14+
export class SpinnerVisibilityService {
15+
private _visibilitySubject: Subject<boolean> = new Subject<boolean>();
16+
17+
get visibilitySubject(): Subject<boolean> {
18+
return this._visibilitySubject;
19+
}
20+
}
21+
22+
export function SpinnerVisibilityServiceFactory(): SpinnerVisibilityService {
23+
return new SpinnerVisibilityService();
24+
}
25+
26+
export let SpinnerVisibilityServiceFactoryProvider = {
27+
provide: SpinnerVisibilityService,
28+
useFactory: SpinnerVisibilityServiceFactory
29+
};

test/components/spinner/spinner.component.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { HttpClient } from '@angular/common/http';
1616
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
1717
import { NgHttpLoaderServicesModule } from '../../../src/services/ng-http-loader-services.module';
1818
import 'rxjs/add/observable/forkJoin';
19+
import { SpinnerVisibilityService } from '../../../src/services/spinner-visibility.service';
1920

2021
describe('SpinnerComponent', () => {
2122
let component: SpinnerComponent;
@@ -286,4 +287,14 @@ describe('SpinnerComponent', () => {
286287
discardPeriodicTasks();
287288
}
288289
)));
290+
291+
it('should be possible to manually show/hide the spinner', inject(
292+
[SpinnerVisibilityService], (visibilityService: SpinnerVisibilityService) => {
293+
visibilityService.visibilitySubject.next(true);
294+
expect(component.isSpinnerVisible).toBeTruthy();
295+
296+
visibilityService.visibilitySubject.next(false);
297+
expect(component.isSpinnerVisible).toBeFalsy();
298+
}
299+
));
289300
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
4+
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
5+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
6+
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8+
*/
9+
10+
import { inject, TestBed } from '@angular/core/testing';
11+
import { SpinnerVisibilityService } from '../../src/services/spinner-visibility.service';
12+
13+
describe('SpinnerVisibilityService', () => {
14+
15+
beforeEach(() => {
16+
TestBed.configureTestingModule({
17+
providers: [SpinnerVisibilityService]
18+
});
19+
});
20+
21+
it('should be created', inject([SpinnerVisibilityService], (service: SpinnerVisibilityService) => {
22+
expect(service).toBeTruthy();
23+
}));
24+
25+
it('should define a subject', inject([SpinnerVisibilityService], (service: SpinnerVisibilityService) => {
26+
expect(service.visibilitySubject).toBeTruthy();
27+
}));
28+
});

0 commit comments

Comments
 (0)