feat(core): add new lazy load service

pull/3453/head
Arman Ozak 6 years ago
parent 4cc3a272eb
commit 221c78fa0d

@ -1,20 +1,56 @@
import { Injectable } from '@angular/core';
import { Observable, ReplaySubject, throwError } from 'rxjs';
import { concat, Observable, of, ReplaySubject, throwError } from 'rxjs';
import { delay, retryWhen, shareReplay, take, tap } from 'rxjs/operators';
import { LoadingStrategy } from '../strategies';
import { uuid } from '../utils';
@Injectable({
providedIn: 'root',
})
export class LazyLoadService {
readonly loaded = new Set();
loadedLibraries: { [url: string]: ReplaySubject<void> } = {};
load(strategy: LoadingStrategy, retryTimes?: number, retryDelay?: number): Observable<Event>;
load(
urlOrUrls: string | string[],
type: 'script' | 'style',
content: string = '',
content?: string,
targetQuery?: string,
position?: InsertPosition,
): Observable<void>;
load(
strategyOrUrl: LoadingStrategy | string | string[],
retryTimesOrType?: number | 'script' | 'style',
retryDelayOrContent?: number | string,
targetQuery: string = 'body',
position: InsertPosition = 'beforeend',
): Observable<void> {
): Observable<Event | void> {
if (strategyOrUrl instanceof LoadingStrategy) {
const strategy = strategyOrUrl;
const retryTimes = retryTimesOrType as number;
const retryDelay = retryDelayOrContent as number;
if (this.loaded.has(strategy.path)) return of(new CustomEvent('load'));
return strategy.createStream().pipe(
retryWhen(error$ =>
concat(
error$.pipe(delay(retryDelay), take(retryTimes)),
throwError(new CustomEvent('error')),
),
),
tap(() => this.loaded.add(strategy.path)),
delay(100),
shareReplay({ bufferSize: 1, refCount: true }),
);
}
let urlOrUrls = strategyOrUrl;
const content = retryDelayOrContent as string;
const type = retryTimesOrType as 'script' | 'style';
if (!urlOrUrls && !content) {
return throwError('Should pass url or content');
} else if (!urlOrUrls && content) {

@ -1,9 +1,65 @@
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
import { of, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { LazyLoadService } from '../services/lazy-load.service';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';
import { ScriptLoadingStrategy } from '../strategies';
describe('LazyLoadService', () => {
describe('#load', () => {
const service = new LazyLoadService();
const strategy = new ScriptLoadingStrategy('http://example.com/');
afterEach(() => {
jest.clearAllMocks();
});
it('should emit an error event if not loaded', done => {
const counter = jest.fn();
jest.spyOn(strategy, 'createStream').mockReturnValueOnce(
of(null).pipe(
switchMap(() => {
counter();
return throwError('THIS WILL NOT BE THE FINAL ERROR');
}),
),
);
service.load(strategy, 5, 0).subscribe({
error: errorEvent => {
expect(errorEvent).toEqual(new CustomEvent('error'));
expect(counter).toHaveBeenCalledTimes(6);
expect(service.loaded.has(strategy.path)).toBe(false);
done();
},
});
});
it('should emit a load event if loaded', done => {
const loadEvent = new CustomEvent('load');
jest.spyOn(strategy, 'createStream').mockReturnValue(of(loadEvent));
service.load(strategy).subscribe({
next: event => {
expect(event).toBe(loadEvent);
expect(service.loaded.has(strategy.path)).toBe(true);
done();
},
});
});
it('should emit a custom load event if loaded if resource is loaded before', done => {
const loadEvent = new CustomEvent('load');
service.loaded.add(strategy.path);
service.load(strategy).subscribe(event => {
expect(event).toEqual(loadEvent);
done();
});
});
});
});
describe('LazyLoadService (Deprecated)', () => {
let spectator: SpectatorService<LazyLoadService>;
let service: LazyLoadService;
const scriptElement = document.createElement('script');
@ -25,15 +81,17 @@ describe('LazyLoadService', () => {
spy.mockReturnValue(scriptElement);
service.load('https://abp.io', 'script', 'test').subscribe(res => {
expect(document.querySelector('script[src="https://abp.io"][type="text/javascript"]').textContent).toMatch(
'test',
);
expect(
document.querySelector('script[src="https://abp.io"][type="text/javascript"]').textContent,
).toMatch('test');
});
scriptElement.onload(null);
service.load('https://abp.io', 'script', 'test').subscribe(res => {
expect(document.querySelectorAll('script[src="https://abp.io"][type="text/javascript"]')).toHaveLength(1);
expect(
document.querySelectorAll('script[src="https://abp.io"][type="text/javascript"]'),
).toHaveLength(1);
done();
});
});
@ -59,7 +117,9 @@ describe('LazyLoadService', () => {
test('should load an link element', done => {
service.load('https://abp.io', 'style').subscribe(res => {
expect(document.querySelector('link[type="text/css"][rel="stylesheet"][href="https://abp.io"]')).toBeTruthy();
expect(
document.querySelector('link[type="text/css"][rel="stylesheet"][href="https://abp.io"]'),
).toBeTruthy();
done();
});

Loading…
Cancel
Save