mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Add aspnet-angular NPM package containing HttpWithStateTransfer utility
This commit is contained in:
3
src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore
vendored
Normal file
3
src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/node_modules/
|
||||||
|
**/*.js
|
||||||
|
**/*.d.ts
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
!/*.js
|
||||||
|
!/*.d.ts
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
these files except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "aspnet-angular",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Helpers for using Angular in ASP.NET Core projects",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"prepublish": "rimraf *.d.ts && tsc && echo 'Finished building NPM package \"aspnet-angular\"'",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/aspnet/JavaScriptServices.git"
|
||||||
|
},
|
||||||
|
"author": "Microsoft",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/aspnet/JavaScriptServices/issues"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular/common": "^4.3.2",
|
||||||
|
"@angular/core": "^4.3.2",
|
||||||
|
"@angular/http": "^4.3.2",
|
||||||
|
"@angular/platform-browser": "^4.3.2",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"typescript": "^2.4.2",
|
||||||
|
"rxjs": "^5.4.2",
|
||||||
|
"zone.js": "^0.8.16"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": "^4.2.5 || ^5.0.0-beta"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { Provider, NgModule, Inject } from '@angular/core';
|
||||||
|
import { Headers, Http, ResponseOptions, RequestOptionsArgs, Response } from '@angular/http';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import 'rxjs/add/observable/of';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
const globalSerializedStateKey = 'HTTP_STATE_TRANSFER';
|
||||||
|
const backingStoreDIToken = 'HTTP_STATE_BACKING_STORE';
|
||||||
|
|
||||||
|
export interface CacheOptions {
|
||||||
|
permanent: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CachedHttpResponse {
|
||||||
|
headers: { [name: string]: any } | null;
|
||||||
|
status: number;
|
||||||
|
statusText: string | null;
|
||||||
|
text: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BackingStore = { [key: string]: CachedHttpResponse };
|
||||||
|
|
||||||
|
export class HttpWithStateTransfer {
|
||||||
|
private backingStore: BackingStore;
|
||||||
|
private http: Http;
|
||||||
|
|
||||||
|
constructor(@Inject(Http) http: Http, @Inject(backingStoreDIToken) backingStore: BackingStore) {
|
||||||
|
this.http = http;
|
||||||
|
this.backingStore = backingStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public stateForTransfer(): any {
|
||||||
|
return { [globalSerializedStateKey]: this.backingStore };
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(url: string, options?: CacheOptions, requestOptions?: RequestOptionsArgs): Observable<Response> {
|
||||||
|
return this.getCachedResponse(/* cacheKey */ url, () => this.http.get(url, requestOptions), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCachedResponse(cacheKey: string, provider: () => Observable<Response>, options?: CacheOptions): Observable<Response> {
|
||||||
|
// By default, the cache is only used for the *first* client-side read. So, we're only performing
|
||||||
|
// a one-time transfer of server-side response to the client. If you want to keep and reuse cached
|
||||||
|
// responses continually during server-side and client-side execution, set 'permanent' to 'true.
|
||||||
|
const isClient = typeof window !== 'undefined';
|
||||||
|
const isPermanent = options && options.permanent;
|
||||||
|
|
||||||
|
const allowReadFromCache = isClient || isPermanent;
|
||||||
|
if (allowReadFromCache && this.backingStore.hasOwnProperty(cacheKey)) {
|
||||||
|
const cachedValue = this.backingStore[cacheKey];
|
||||||
|
if (!isPermanent) {
|
||||||
|
delete this.backingStore[cacheKey];
|
||||||
|
}
|
||||||
|
return Observable.of(new Response(new ResponseOptions({
|
||||||
|
body: cachedValue.text,
|
||||||
|
headers: new Headers(cachedValue.headers),
|
||||||
|
status: cachedValue.status,
|
||||||
|
url: cachedValue.url
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider()
|
||||||
|
.map(response => {
|
||||||
|
const allowWriteToCache = !isClient || isPermanent;
|
||||||
|
if (allowWriteToCache) {
|
||||||
|
this.backingStore[cacheKey] = {
|
||||||
|
headers: response.headers ? response.headers.toJSON() : null,
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
text: response.text(),
|
||||||
|
url: response.url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultBackingStoreFactory() {
|
||||||
|
const transferredData = typeof window !== 'undefined' ? (window as any)[globalSerializedStateKey] : null;
|
||||||
|
return transferredData || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
// The backing store is a separate DI service so you could override exactly how it gets
|
||||||
|
// transferred from server to client
|
||||||
|
{ provide: backingStoreDIToken, useFactory: defaultBackingStoreFactory },
|
||||||
|
|
||||||
|
{ provide: HttpWithStateTransfer, useClass: HttpWithStateTransfer },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class HttpWithStateTransferModule {
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './HttpWithStateTransfer';
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": ".",
|
||||||
|
"lib": ["es2015", "dom"]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/index.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user