Skip to content
This repository was archived by the owner on Dec 18, 2024. It is now read-only.

Commit 4894877

Browse files
authored
fix: cache fetched docs (#1001)
Currently we fetch a doc every time we need to display it which means that user navigating between two pages will have to request the same information multiple times. These changes add a caching layer since the information isn't going to change.
1 parent 0b39011 commit 4894877

File tree

2 files changed

+28
-18
lines changed

2 files changed

+28
-18
lines changed

src/app/shared/doc-viewer/doc-viewer.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ describe('DocViewer', () => {
118118
});
119119

120120
it('should show error message when doc not found', () => {
121-
spyOn(console, 'log');
121+
spyOn(console, 'error');
122122

123123
const fixture = TestBed.createComponent(DocViewerTestComponent);
124124
const docViewer = fixture.debugElement.query(By.directive(DocViewer));
@@ -138,7 +138,7 @@ describe('DocViewer', () => {
138138
expect(docViewer).not.toBeNull();
139139
expect(docViewer.nativeElement.innerHTML).toContain(
140140
'Failed to load document: http://material.angular.io/error-doc.html');
141-
expect(console.log).toHaveBeenCalledTimes(1);
141+
expect(console.error).toHaveBeenCalledTimes(1);
142142
});
143143

144144
// TODO(mmalerba): Add test that example-viewer is instantiated.

src/app/shared/doc-viewer/doc-viewer.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ComponentFactoryResolver,
88
ElementRef,
99
EventEmitter,
10+
Injectable,
1011
Injector,
1112
Input,
1213
NgZone,
@@ -15,18 +16,34 @@ import {
1516
SecurityContext,
1617
ViewContainerRef,
1718
} from '@angular/core';
18-
import {Subscription} from 'rxjs';
19-
import {take} from 'rxjs/operators';
19+
import {Observable, Subscription} from 'rxjs';
20+
import {shareReplay, take, tap} from 'rxjs/operators';
2021
import {ExampleViewer} from '../example-viewer/example-viewer';
2122
import {HeaderLink} from './header-link';
2223

24+
@Injectable({providedIn: 'root'})
25+
class DocFetcher {
26+
private _cache: Record<string, Observable<string>> = {};
27+
28+
constructor(private _http: HttpClient) {}
29+
30+
fetchDocument(url: string): Observable<string> {
31+
if (this._cache[url]) {
32+
return this._cache[url];
33+
}
34+
35+
const stream = this._http.get(url, {responseType: 'text'}).pipe(shareReplay(1));
36+
return stream.pipe(tap(() => this._cache[url] = stream));
37+
}
38+
}
39+
2340
@Component({
2441
selector: 'doc-viewer',
2542
template: 'Loading document...',
2643
})
2744
export class DocViewer implements OnDestroy {
2845
private _portalHosts: DomPortalOutlet[] = [];
29-
private _documentFetchSubscription: Subscription = new Subscription();
46+
private _documentFetchSubscription: Subscription | undefined;
3047

3148
@Input() name: string | undefined;
3249

@@ -69,21 +86,17 @@ export class DocViewer implements OnDestroy {
6986
constructor(private _appRef: ApplicationRef,
7087
private _componentFactoryResolver: ComponentFactoryResolver,
7188
public _elementRef: ElementRef,
72-
private _http: HttpClient,
7389
private _injector: Injector,
7490
private _viewContainerRef: ViewContainerRef,
7591
private _ngZone: NgZone,
76-
private _domSanitizer: DomSanitizer) {
92+
private _domSanitizer: DomSanitizer,
93+
private _docFetcher: DocFetcher) {
7794
}
7895

7996
/** Fetch a document by URL. */
8097
private _fetchDocument(url: string) {
81-
// Cancel previous pending request
82-
if (this._documentFetchSubscription) {
83-
this._documentFetchSubscription.unsubscribe();
84-
}
85-
86-
this._documentFetchSubscription = this._http.get(url, {responseType: 'text'}).subscribe(
98+
this._documentFetchSubscription?.unsubscribe();
99+
this._documentFetchSubscription = this._docFetcher.fetchDocument(url).subscribe(
87100
document => this.updateDocument(document),
88101
error => this.showError(url, error)
89102
);
@@ -116,7 +129,7 @@ export class DocViewer implements OnDestroy {
116129

117130
/** Show an error that occurred when fetching a document. */
118131
private showError(url: string, error: HttpErrorResponse) {
119-
console.log(error);
132+
console.error(error);
120133
this._elementRef.nativeElement.innerText =
121134
`Failed to load document: ${url}. Error: ${error.statusText}`;
122135
}
@@ -149,9 +162,6 @@ export class DocViewer implements OnDestroy {
149162

150163
ngOnDestroy() {
151164
this._clearLiveExamples();
152-
153-
if (this._documentFetchSubscription) {
154-
this._documentFetchSubscription.unsubscribe();
155-
}
165+
this._documentFetchSubscription?.unsubscribe();
156166
}
157167
}

0 commit comments

Comments
 (0)