-
Notifications
You must be signed in to change notification settings - Fork 483
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(common): add a common module with TransferHttpCacheModule (#823)
- Loading branch information
Showing
7 changed files
with
193 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Angular Universal Common Module | ||
|
||
This is the common Angular Universal module that is common across server-side rendering app | ||
irrespective of the rendering engine. | ||
|
||
The package can be installed using: | ||
|
||
`npm install @nguniversal/common --save` | ||
|
||
## TransferHttpCacheModule | ||
|
||
`TransferHttpCacheModule` installs a Http interceptor that avoids duplicate `HttpClient` requests | ||
on the client, for requests that were already made when the application was rendered on the server | ||
side. | ||
|
||
When the module is installed in the application `NgModule`, it will intercept `HttpClient` requests | ||
on the server and store the response in the `TransferState` key-value store. This is transferred to the client, which then uses it to respond to the same `HttpClient` requests on the client. | ||
|
||
### Usage | ||
|
||
To use the `TransferHttpCacheModule` just install it as part of the top-level App module. | ||
|
||
That's it! | ||
|
||
```ts | ||
import {TransferHttpCacheModule} from ‘@nguniversal/common’; | ||
|
||
@NgModule({ | ||
imports: [ | ||
BrowserModule.withServerTransition({appId: ‘my-app’}), | ||
TransferHttpCacheModule, | ||
], | ||
bootstrap: [MyApp] | ||
}) | ||
export class AppBrowserModule() {} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { TransferHttpCacheModule } from './src/transfer_http'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"name": "@nguniversal/common", | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"version": "5.0.0-beta.1", | ||
"description": "Angular Universal common ", | ||
"homepage": "https://github.com/angular/universal", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/angular/universal" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/angular/universal/issues" | ||
}, | ||
"config": { | ||
"engine-strict": true | ||
}, | ||
"engines": { | ||
"node": ">= 6.9.0", | ||
"npm": ">= 3" | ||
}, | ||
"peerDependencies": { | ||
"@angular/common": "^5.0.0-beta.7", | ||
"@angular/core": "^5.0.0-beta.7" | ||
}, | ||
"devDependencies": { | ||
"@angular/animations": "^5.0.0-beta.7", | ||
"@angular/common": "^5.0.0-beta.7", | ||
"@angular/core": "^5.0.0-beta.7", | ||
"@angular/platform-browser": "^5.0.0-beta.7", | ||
"@angular/platform-server": "^5.0.0-beta.7", | ||
"rxjs": "^5.2.0", | ||
"typescript": "2.4.2", | ||
"zone.js": "^0.8.12" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import {HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; | ||
import {ApplicationRef, Injectable, NgModule} from '@angular/core'; | ||
import {Observable} from 'rxjs/Observable'; | ||
import {of} from 'rxjs/observable/of'; | ||
import {filter} from 'rxjs/operator/filter'; | ||
import {first} from 'rxjs/operator/first'; | ||
import {toPromise} from 'rxjs/operator/toPromise'; | ||
import {_do} from 'rxjs/operator/do'; | ||
|
||
import {BrowserTransferStateModule, TransferState, makeStateKey} from '@angular/platform-browser'; | ||
|
||
export interface TransferHttpResponse { | ||
body?: any | null; | ||
headers?: {[k: string]: string[]}; | ||
status?: number; | ||
statusText?: string; | ||
url?: string; | ||
} | ||
|
||
function getHeadersMap(headers: HttpHeaders) { | ||
const headersMap: {[name: string]: string[]} = {}; | ||
for (const key of headers.keys()) { | ||
headersMap[key] = headers.getAll(key)!; | ||
} | ||
return headersMap; | ||
} | ||
|
||
@Injectable() | ||
export class TransferHttpCacheInterceptor implements HttpInterceptor { | ||
|
||
private isCacheActive = true; | ||
|
||
private invalidateCacheEntry(url: string) { | ||
this.transferState.remove(makeStateKey<TransferHttpResponse>('G.' + url)); | ||
this.transferState.remove(makeStateKey<TransferHttpResponse>('H.' + url)); | ||
} | ||
|
||
constructor(appRef: ApplicationRef, private transferState: TransferState) { | ||
// Stop using the cache if the application has stabilized, indicating initial rendering is | ||
// complete. | ||
toPromise | ||
.call(first.call(filter.call(appRef.isStable, (isStable: boolean) => isStable))) | ||
.then(() => { this.isCacheActive = false; }); | ||
} | ||
|
||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | ||
// Stop using the cache if there is a mutating call. | ||
if (req.method !== 'GET' && req.method !== 'HEAD') { | ||
this.isCacheActive = false; | ||
this.invalidateCacheEntry(req.url); | ||
} | ||
|
||
if (!this.isCacheActive) { | ||
// Cache is no longer active. Pass the request through. | ||
return next.handle(req); | ||
} | ||
|
||
const key = (req.method === 'GET' ? 'G.' : 'H.') + req.url; | ||
const storeKey = makeStateKey<TransferHttpResponse>(key); | ||
|
||
if (this.transferState.hasKey(storeKey)) { | ||
// Request found in cache. Respond using it. | ||
const response = this.transferState.get(storeKey, {} as TransferHttpResponse); | ||
return of(new HttpResponse<any>({ | ||
body: response.body, | ||
headers: new HttpHeaders(response.headers), | ||
status: response.status, | ||
statusText: response.statusText, | ||
url: response.url, | ||
})); | ||
} else { | ||
// Request not found in cache. Make the request and cache it. | ||
const httpEvent = next.handle(req); | ||
return _do.call(httpEvent, (event: HttpEvent<any>) => { | ||
if (event instanceof HttpResponse) { | ||
this.transferState.set(storeKey, { | ||
body: event.body, | ||
headers: getHeadersMap(event.headers), | ||
status: event.status, | ||
statusText: event.statusText, | ||
url: event.url!, | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* An NgModule used in conjunction with `ServerTransferHttpCacheModule` to transfer cached HTTP | ||
* calls from the server to the client application. | ||
*/ | ||
@NgModule({ | ||
imports: [BrowserTransferStateModule], | ||
providers: [ | ||
TransferHttpCacheInterceptor, | ||
{provide: HTTP_INTERCEPTORS, useExisting: TransferHttpCacheInterceptor, multi: true}, | ||
], | ||
}) | ||
export class TransferHttpCacheModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "../../dist/common", | ||
"rootDir": "./" | ||
}, | ||
"angularCompilerOptions": { | ||
"skipTemplateCodegen": true | ||
}, | ||
"files": [ | ||
"index.ts" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters