Angular 在订阅中通用添加元标记
Angular Universal add meta tag in subscription
我在尝试动态设置元标记时遇到了一些实际问题。
我可以轻松地在 ngOnInit 方法中设置标签,但是如果我使用 Subscription addTag 方法什么都不做。
export class AppComponent implements OnInit, OnDestroy {
public page: Page;
public postSlug: string;
private pages: Page[] = [];
private url: string = '/';
private routerSubscription: Subscription;
private pagesSubscription: Subscription;
constructor(
private router: Router,
private contentfulService: ContentfulService,
private pageService: PageService,
private meta: Meta
) {}
ngOnInit(): void {
this.meta.addTag({ name: 'app', content: 'Set from app component' });
this.url = this.router.url.split('#')[0]; // For initial page load
this.contentfulService.getPages();
this.getPages();
this.onNavigationEnd();
}
ngOnDestroy(): void {
if (this.routerSubscription) this.routerSubscription.unsubscribe();
if (this.pagesSubscription) this.pagesSubscription.unsubscribe();
}
private onNavigationEnd(): void {
this.routerSubscription = this.router.events.subscribe((event: any) => {
if (!(event instanceof NavigationEnd)) return;
this.url = event.urlAfterRedirects.split('#')[0];
this.setPage();
this.setPost();
});
}
private setPost(): void {
this.postSlug = undefined; // Always reset
if (!this.page || this.url.indexOf('/live-better/') === -1) return;
this.meta.addTag({ name: 'post', content: 'Set post' });
var urlParts = this.url.split('/');
this.postSlug = urlParts[urlParts.length - 1];
}
private setPage(): void {
if (!this.pages.length || !this.url) return;
this.page = this.pages.find((page: Page) => page.slug === this.url);
if (!this.page) {
this.page = this.pages.find(
(page: Page) => this.url.indexOf(page.slug) === 0
);
}
this.meta.addTag({ name: 'page', content: 'Set page' });
console.log(this.page);
this.pageService.setTitle(this.page.title);
this.pageService.setMetadata(this.page);
}
private getPages(): void {
this.pagesSubscription = this.contentfulService.pages.subscribe(
(pages: Page[]) => {
if (!pages.length) return;
this.pages = pages;
this.meta.addTag({ name: 'pages', content: 'Get pages' });
this.setPage();
this.setPost();
}
);
}
}
其余代码执行正常。如果我查看源代码,我可以看到 { name: 'app', content: 'Set from app component' }
的标签,但我看不到任何其他标签。
有谁知道我是否遗漏了什么?
我认为这一定是视图后数据加载的问题,所以我创建了一个这样的解析器:
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { ContentfulService } from '../services/contentful.service';
import { Meta } from '@angular/platform-browser';
import { mergeMap, first } from 'rxjs/operators';
import { Observable, of, from } from 'rxjs';
import { Resolve } from '../models/resolve';
import { Page } from '../models/page';
@Injectable({ providedIn: 'root' })
export class PageResolver implements Resolve<{ page: Page; postSlug: string }> {
constructor(
private contentfulService: ContentfulService,
private meta: Meta
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<{ page: Page; postSlug: string }> {
console.log('about to parse');
return this.getData(state);
}
private getData(
state: RouterStateSnapshot
): Observable<{ page: Page; postSlug: string }> {
const currentUrl = state.url.split('#')[0];
console.log(currentUrl);
this.meta.addTag({ name: 'resolve', content: 'Resolving route' });
if (!this.contentfulService.current.length) {
console.log('first load');
return from(
this.contentfulService.getPages().then((pages: Page[]) => {
this.meta.addTag({
name: 'first',
content: 'First resolve hit',
});
return this.parseData(pages, currentUrl);
})
);
} else {
console.log('after load');
return this.contentfulService.pages.pipe(
first(),
mergeMap((pages: Page[]) => {
this.meta.addTag({
name: 'second',
content: 'Changed page',
});
return of(this.parseData(pages, currentUrl));
})
);
}
}
private parseData(
pages: Page[],
currentUrl: string
): { page: Page; postSlug: string } {
let page = this.setPage(pages, currentUrl);
let postSlug = this.setPost(page, currentUrl);
let data: { page: Page; postSlug: string } = {
page: page,
postSlug: postSlug,
};
console.log(data);
return data;
}
private setPage(pages: Page[], currentUrl: string): Page {
if (!pages.length || !currentUrl) throw 'No pages have been loaded';
let page = pages.find((page: Page) => page.slug === currentUrl);
if (!page) {
page = pages.find(
(page: Page) => currentUrl.indexOf(page.slug) === 0
);
}
return page;
}
private setPost(page: Page, currentUrl: string): string {
if (!page || currentUrl.indexOf('/live-better/') === -1) return;
let urlParts = currentUrl.split('/');
let postSlug = urlParts[urlParts.length - 1];
let queryIndex = postSlug.indexOf('?');
if (queryIndex > -1) postSlug = postSlug.substring(0, queryIndex);
return postSlug;
}
}
并像这样将其添加到我的路由中:
const routes: Routes = [
{
path: '**',
component: HomeComponent,
resolve: { content: PageResolver },
},
];
现在在我的 HomeComponent 中,我得到的数据是这样的:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Page } from '@models';
import { Meta } from '@angular/platform-browser';
@Component({
selector: 'sxp-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent implements OnInit {
public page: Page;
public postSlug: string;
constructor(private route: ActivatedRoute, private meta: Meta) {}
ngOnInit(): void {
this.meta.addTag({ name: 'home', content: 'Home component loaded' });
this.route.data.subscribe(
(data: { content: { page: Page; postSlug: string } }) => {
let content = data.content;
this.meta.addTag({ name: 'meta', content: 'Subscription hit' });
this.page = content.page;
this.postSlug = content.postSlug;
console.log(content);
}
);
}
}
如您所见,我在解析器中使用了 3 次 addTag
,在 HomeComponent 中使用了 2 次,但是当我查看源代码时,实际上只有一个已添加:
this.meta.addTag({ name: 'resolve', content: 'Resolving route' });
我似乎无法在任何类型的订阅后设置任何元标记。我希望解析器会延迟查看页面源实际尝试获取数据,直到所有内容都首先加载到解析器中。
我在每次 addTag
调用后添加了控制台日志。
您可以在这里查看它们:
页面源是静态的html,当它在浏览器上呈现时将被再次评估。元标记会在那时插入。
要使它们在页面源代码中可见,您必须启用服务器端呈现 -
https://angular.io/guide/universal
因此,未正确设置元数据的原因是未使用 TransferState
。我创建了一个如下所示的新服务 (TransferHttpService):
import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import {
TransferState,
StateKey,
makeStateKey,
} from '@angular/platform-browser';
import { Observable, from } from 'rxjs';
import { tap } from 'rxjs/operators';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
@Injectable({ providedIn: 'root' })
export class TransferHttpService {
constructor(
protected transferState: TransferState,
private httpClient: HttpClient,
@Inject(PLATFORM_ID) private platformId: Object
) {}
request<T>(
method: string,
uri: string | Request,
options?: {
body?: any;
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
reportProgress?: boolean;
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
method,
uri,
options,
(method: string, url: string, options: any) => {
return this.httpClient.request<T>(method, url, options);
}
);
}
/**
* Performs a request with `get` http method.
*/
get<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'get',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.get<T>(url, options);
}
);
}
/**
* Performs a request with `post` http method.
*/
post<T>(
url: string,
body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'post',
url,
body,
options,
// tslint:disable-next-line:no-shadowed-variable
(_method: string, url: string, body: any, options: any) => {
return this.httpClient.post<T>(url, body, options);
}
);
}
/**
* Performs a request with `put` http method.
*/
put<T>(
url: string,
_body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'body';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'put',
url,
_body,
options,
(_method: string, url: string, _body: any, options: any) => {
return this.httpClient.put<T>(url, _body, options);
}
);
}
/**
* Performs a request with `delete` http method.
*/
delete<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'delete',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.delete<T>(url, options);
}
);
}
/**
* Performs a request with `patch` http method.
*/
patch<T>(
url: string,
body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'patch',
url,
body,
options,
// tslint:disable-next-line:no-shadowed-variable
(
_method: string,
url: string,
body: any,
options: any
): Observable<any> => {
return this.httpClient.patch<T>(url, body, options);
}
);
}
/**
* Performs a request with `head` http method.
*/
head<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'head',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.head<T>(url, options);
}
);
}
/**
* Performs a request with `options` http method.
*/
options<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'options',
url,
options,
// tslint:disable-next-line:no-shadowed-variable
(_method: string, url: string, options: any) => {
return this.httpClient.options<T>(url, options);
}
);
}
// tslint:disable-next-line:max-line-length
getData<T>(
method: string,
uri: string | Request,
options: any,
callback: (
method: string,
uri: string | Request,
options: any
) => Observable<any>
): Observable<T> {
let url = uri;
if (typeof uri !== 'string') {
url = uri.url;
}
const tempKey = url + (options ? JSON.stringify(options) : '');
const key = makeStateKey<T>(tempKey);
try {
return this.resolveData<T>(key);
} catch (e) {
//console.log('in catch', key);
return callback(method, uri, options).pipe(
tap((data: T) => {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
// nothing;
}
if (isPlatformServer(this.platformId)) {
//console.log('set cache', key);
this.setCache<T>(key, data);
}
})
);
}
}
private getPostData<T>(
_method: string,
uri: string | Request,
body: any,
options: any,
callback: (
method: string,
uri: string | Request,
body: any,
options: any
) => Observable<any>
): Observable<T> {
let url = uri;
if (typeof uri !== 'string') {
url = uri.url;
}
const tempKey =
url +
(body ? JSON.stringify(body) : '') +
(options ? JSON.stringify(options) : '');
const key = makeStateKey<T>(tempKey);
try {
return this.resolveData<T>(key);
} catch (e) {
return callback(_method, uri, body, options).pipe(
tap((data: T) => {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
// nothing;
}
if (isPlatformServer(this.platformId)) {
this.setCache<T>(key, data);
}
})
);
}
}
private resolveData<T>(key: StateKey<T>): Observable<T> {
const data = this.getFromCache<T>(key);
if (!data) {
throw new Error();
}
if (isPlatformBrowser(this.platformId)) {
//console.log('get cache', key);
// Client only code.
this.transferState.remove(key);
}
if (isPlatformServer(this.platformId)) {
//console.log('we are the server');
// Server only code.
}
return from(Promise.resolve<T>(data));
}
private setCache<T>(key: StateKey<T>, data: T): void {
return this.transferState.set<T>(key, data);
}
private getFromCache<T>(key: StateKey<T>): T {
return this.transferState.get<T>(key, null);
}
}
然后我替换了任何看起来像这样的构造函数:
constructor(private http: HttpClient) {}
对此:
constructor(private http: TransferHttpService) {}
然后一切正常
我在尝试动态设置元标记时遇到了一些实际问题。 我可以轻松地在 ngOnInit 方法中设置标签,但是如果我使用 Subscription addTag 方法什么都不做。
export class AppComponent implements OnInit, OnDestroy {
public page: Page;
public postSlug: string;
private pages: Page[] = [];
private url: string = '/';
private routerSubscription: Subscription;
private pagesSubscription: Subscription;
constructor(
private router: Router,
private contentfulService: ContentfulService,
private pageService: PageService,
private meta: Meta
) {}
ngOnInit(): void {
this.meta.addTag({ name: 'app', content: 'Set from app component' });
this.url = this.router.url.split('#')[0]; // For initial page load
this.contentfulService.getPages();
this.getPages();
this.onNavigationEnd();
}
ngOnDestroy(): void {
if (this.routerSubscription) this.routerSubscription.unsubscribe();
if (this.pagesSubscription) this.pagesSubscription.unsubscribe();
}
private onNavigationEnd(): void {
this.routerSubscription = this.router.events.subscribe((event: any) => {
if (!(event instanceof NavigationEnd)) return;
this.url = event.urlAfterRedirects.split('#')[0];
this.setPage();
this.setPost();
});
}
private setPost(): void {
this.postSlug = undefined; // Always reset
if (!this.page || this.url.indexOf('/live-better/') === -1) return;
this.meta.addTag({ name: 'post', content: 'Set post' });
var urlParts = this.url.split('/');
this.postSlug = urlParts[urlParts.length - 1];
}
private setPage(): void {
if (!this.pages.length || !this.url) return;
this.page = this.pages.find((page: Page) => page.slug === this.url);
if (!this.page) {
this.page = this.pages.find(
(page: Page) => this.url.indexOf(page.slug) === 0
);
}
this.meta.addTag({ name: 'page', content: 'Set page' });
console.log(this.page);
this.pageService.setTitle(this.page.title);
this.pageService.setMetadata(this.page);
}
private getPages(): void {
this.pagesSubscription = this.contentfulService.pages.subscribe(
(pages: Page[]) => {
if (!pages.length) return;
this.pages = pages;
this.meta.addTag({ name: 'pages', content: 'Get pages' });
this.setPage();
this.setPost();
}
);
}
}
其余代码执行正常。如果我查看源代码,我可以看到 { name: 'app', content: 'Set from app component' }
的标签,但我看不到任何其他标签。
有谁知道我是否遗漏了什么?
我认为这一定是视图后数据加载的问题,所以我创建了一个这样的解析器:
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { ContentfulService } from '../services/contentful.service';
import { Meta } from '@angular/platform-browser';
import { mergeMap, first } from 'rxjs/operators';
import { Observable, of, from } from 'rxjs';
import { Resolve } from '../models/resolve';
import { Page } from '../models/page';
@Injectable({ providedIn: 'root' })
export class PageResolver implements Resolve<{ page: Page; postSlug: string }> {
constructor(
private contentfulService: ContentfulService,
private meta: Meta
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<{ page: Page; postSlug: string }> {
console.log('about to parse');
return this.getData(state);
}
private getData(
state: RouterStateSnapshot
): Observable<{ page: Page; postSlug: string }> {
const currentUrl = state.url.split('#')[0];
console.log(currentUrl);
this.meta.addTag({ name: 'resolve', content: 'Resolving route' });
if (!this.contentfulService.current.length) {
console.log('first load');
return from(
this.contentfulService.getPages().then((pages: Page[]) => {
this.meta.addTag({
name: 'first',
content: 'First resolve hit',
});
return this.parseData(pages, currentUrl);
})
);
} else {
console.log('after load');
return this.contentfulService.pages.pipe(
first(),
mergeMap((pages: Page[]) => {
this.meta.addTag({
name: 'second',
content: 'Changed page',
});
return of(this.parseData(pages, currentUrl));
})
);
}
}
private parseData(
pages: Page[],
currentUrl: string
): { page: Page; postSlug: string } {
let page = this.setPage(pages, currentUrl);
let postSlug = this.setPost(page, currentUrl);
let data: { page: Page; postSlug: string } = {
page: page,
postSlug: postSlug,
};
console.log(data);
return data;
}
private setPage(pages: Page[], currentUrl: string): Page {
if (!pages.length || !currentUrl) throw 'No pages have been loaded';
let page = pages.find((page: Page) => page.slug === currentUrl);
if (!page) {
page = pages.find(
(page: Page) => currentUrl.indexOf(page.slug) === 0
);
}
return page;
}
private setPost(page: Page, currentUrl: string): string {
if (!page || currentUrl.indexOf('/live-better/') === -1) return;
let urlParts = currentUrl.split('/');
let postSlug = urlParts[urlParts.length - 1];
let queryIndex = postSlug.indexOf('?');
if (queryIndex > -1) postSlug = postSlug.substring(0, queryIndex);
return postSlug;
}
}
并像这样将其添加到我的路由中:
const routes: Routes = [
{
path: '**',
component: HomeComponent,
resolve: { content: PageResolver },
},
];
现在在我的 HomeComponent 中,我得到的数据是这样的:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Page } from '@models';
import { Meta } from '@angular/platform-browser';
@Component({
selector: 'sxp-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent implements OnInit {
public page: Page;
public postSlug: string;
constructor(private route: ActivatedRoute, private meta: Meta) {}
ngOnInit(): void {
this.meta.addTag({ name: 'home', content: 'Home component loaded' });
this.route.data.subscribe(
(data: { content: { page: Page; postSlug: string } }) => {
let content = data.content;
this.meta.addTag({ name: 'meta', content: 'Subscription hit' });
this.page = content.page;
this.postSlug = content.postSlug;
console.log(content);
}
);
}
}
如您所见,我在解析器中使用了 3 次 addTag
,在 HomeComponent 中使用了 2 次,但是当我查看源代码时,实际上只有一个已添加:
this.meta.addTag({ name: 'resolve', content: 'Resolving route' });
我似乎无法在任何类型的订阅后设置任何元标记。我希望解析器会延迟查看页面源实际尝试获取数据,直到所有内容都首先加载到解析器中。
我在每次 addTag
调用后添加了控制台日志。
您可以在这里查看它们:
页面源是静态的html,当它在浏览器上呈现时将被再次评估。元标记会在那时插入。
要使它们在页面源代码中可见,您必须启用服务器端呈现 - https://angular.io/guide/universal
因此,未正确设置元数据的原因是未使用 TransferState
。我创建了一个如下所示的新服务 (TransferHttpService):
import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import {
TransferState,
StateKey,
makeStateKey,
} from '@angular/platform-browser';
import { Observable, from } from 'rxjs';
import { tap } from 'rxjs/operators';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
@Injectable({ providedIn: 'root' })
export class TransferHttpService {
constructor(
protected transferState: TransferState,
private httpClient: HttpClient,
@Inject(PLATFORM_ID) private platformId: Object
) {}
request<T>(
method: string,
uri: string | Request,
options?: {
body?: any;
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
reportProgress?: boolean;
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
method,
uri,
options,
(method: string, url: string, options: any) => {
return this.httpClient.request<T>(method, url, options);
}
);
}
/**
* Performs a request with `get` http method.
*/
get<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'get',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.get<T>(url, options);
}
);
}
/**
* Performs a request with `post` http method.
*/
post<T>(
url: string,
body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'post',
url,
body,
options,
// tslint:disable-next-line:no-shadowed-variable
(_method: string, url: string, body: any, options: any) => {
return this.httpClient.post<T>(url, body, options);
}
);
}
/**
* Performs a request with `put` http method.
*/
put<T>(
url: string,
_body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'body';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'put',
url,
_body,
options,
(_method: string, url: string, _body: any, options: any) => {
return this.httpClient.put<T>(url, _body, options);
}
);
}
/**
* Performs a request with `delete` http method.
*/
delete<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'delete',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.delete<T>(url, options);
}
);
}
/**
* Performs a request with `patch` http method.
*/
patch<T>(
url: string,
body: any,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getPostData<T>(
'patch',
url,
body,
options,
// tslint:disable-next-line:no-shadowed-variable
(
_method: string,
url: string,
body: any,
options: any
): Observable<any> => {
return this.httpClient.patch<T>(url, body, options);
}
);
}
/**
* Performs a request with `head` http method.
*/
head<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'head',
url,
options,
(_method: string, url: string, options: any) => {
return this.httpClient.head<T>(url, options);
}
);
}
/**
* Performs a request with `options` http method.
*/
options<T>(
url: string,
options?: {
headers?:
| HttpHeaders
| {
[header: string]: string | string[];
};
observe?: 'response';
params?:
| HttpParams
| {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}
): Observable<T> {
// tslint:disable-next-line:no-shadowed-variable
return this.getData<T>(
'options',
url,
options,
// tslint:disable-next-line:no-shadowed-variable
(_method: string, url: string, options: any) => {
return this.httpClient.options<T>(url, options);
}
);
}
// tslint:disable-next-line:max-line-length
getData<T>(
method: string,
uri: string | Request,
options: any,
callback: (
method: string,
uri: string | Request,
options: any
) => Observable<any>
): Observable<T> {
let url = uri;
if (typeof uri !== 'string') {
url = uri.url;
}
const tempKey = url + (options ? JSON.stringify(options) : '');
const key = makeStateKey<T>(tempKey);
try {
return this.resolveData<T>(key);
} catch (e) {
//console.log('in catch', key);
return callback(method, uri, options).pipe(
tap((data: T) => {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
// nothing;
}
if (isPlatformServer(this.platformId)) {
//console.log('set cache', key);
this.setCache<T>(key, data);
}
})
);
}
}
private getPostData<T>(
_method: string,
uri: string | Request,
body: any,
options: any,
callback: (
method: string,
uri: string | Request,
body: any,
options: any
) => Observable<any>
): Observable<T> {
let url = uri;
if (typeof uri !== 'string') {
url = uri.url;
}
const tempKey =
url +
(body ? JSON.stringify(body) : '') +
(options ? JSON.stringify(options) : '');
const key = makeStateKey<T>(tempKey);
try {
return this.resolveData<T>(key);
} catch (e) {
return callback(_method, uri, body, options).pipe(
tap((data: T) => {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
// nothing;
}
if (isPlatformServer(this.platformId)) {
this.setCache<T>(key, data);
}
})
);
}
}
private resolveData<T>(key: StateKey<T>): Observable<T> {
const data = this.getFromCache<T>(key);
if (!data) {
throw new Error();
}
if (isPlatformBrowser(this.platformId)) {
//console.log('get cache', key);
// Client only code.
this.transferState.remove(key);
}
if (isPlatformServer(this.platformId)) {
//console.log('we are the server');
// Server only code.
}
return from(Promise.resolve<T>(data));
}
private setCache<T>(key: StateKey<T>, data: T): void {
return this.transferState.set<T>(key, data);
}
private getFromCache<T>(key: StateKey<T>): T {
return this.transferState.get<T>(key, null);
}
}
然后我替换了任何看起来像这样的构造函数:
constructor(private http: HttpClient) {}
对此:
constructor(private http: TransferHttpService) {}
然后一切正常