如何解决在动态组件中找不到角通用异步管道
我有一个Angular 10应用程序,该应用程序将Angular Universal用于SSR。此应用程序还发出http请求以获取一些html并动态呈现它。当我使用ng serve运行应用程序时,一切正常,动态内容将通过http请求获取,并正确呈现到页面中。这些是我的主要模块和组件
app.module.ts
export function createCompiler(compilerFactory: CompilerFactory) {
return compilerFactory.createCompiler();
}
@NgModule({
declarations: [
AppComponent,],imports: [
browserModule.withServerTransition({ appId: 'serverApp' }),browserAnimationsModule,HttpClientModule,browserTransferStateModule,CoreModule
],providers: [
HttpClientModule,{ provide: COMPILER_OPTIONS,useValue: {},multi: true },{ provide: CompilerFactory,useClass: JitCompilerFactory,deps: [COMPILER_OPTIONS] },{ provide: Compiler,useFactory: createCompiler,deps: [CompilerFactory] }
],bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
@Component({
selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.scss'],})
export class AppComponent implements OnInit {
categories$: Observable<any[]>;
categories: any[]
store$: Observable<any>;
constructor() {}
ngOnInit() {
const cats = [
{ id: 1,name: 'XMAS' },{ id: 2,name: 'KITCHEN' },{ id: 3,name: 'DINNING' },{ id: 4,name: 'BATHROOM' },{ id: 5,name: 'bedROOM' },];
const store = {
countryCode : 'US',}
this.store$ = of(store);
this.categories$ = of(cats);
this.categories = [...cats];
}
}
这是该组件的基本html
<p>TEST</p>
<div *ngIf="categories?.length">
<h2>CATEGORIES</h2>
<ul *ngFor="let cat of categories">
<li>{{ cat.name }}</li>
</ul>
</div>
<footer>
<app-dynamic-content name="Footer_Sections" [parentContext]="this"></app-dynamic-content>
</footer>
app-dynamic内容组件通过http请求获取内容,创建一个新组件,将获取的内容分配为模板,创建一个新模块,并将该组件添加到声明中以允许使用
dynamic-content.component.ts
@Component({
selector: 'app-dynamic-content',templateUrl: './dynamic-content.component.html',styleUrls: ['./dynamic-content.component.scss'],changeDetection: ChangeDetectionStrategy.OnPush
})
export class DynamicContentComponent implements OnInit,OnDestroy,AfterViewInit {
@input() name: string;
@input() parentContext: any;
@ViewChild('container',{ read: ViewContainerRef }) container: ViewContainerRef;
private componentRef: ComponentRef<any>;
private unsubscribe = new Subject<void>();
parsedContent: DynamicContent;
dynamicContent$: Observable<any>;
template: string;
constructor(
private dynamicContentsService: DynamicContentsService,private compiler: Compiler,private changeDetectorRef: ChangeDetectorRef
) { }
ngOnInit(): void {
this.dynamicContent$ = this.dynamicContentsService.getDynamicContent(this.name).pipe(filter(res => !!res));
}
ngAfterViewInit() {
this.dynamicContent$.pipe(take(1))
.subscribe((response) => {
if (response && response.dynamicContents.length) {
this.parsedContent = this.dynamicContentsService.parseDinamycContent(response.dynamicContents as DynamicContent[]);
this.template = this.parsedContent.value;
this.createDynamicComponent();
}
});
}
createDynamicComponent() {
const Metadata = new Component({
selector: 'app-dynamic',template: this.template
});
const factory = this.createComponentFactory(Metadata);
this.componentRef = this.container.createComponent(factory);
this.changeDetectorRef.detectChanges();
}
createComponentFactory(Metadata: Component): ComponentFactory<any> {
const self = this;
const componentClass = class DynamicComponent { context: any = self.parentContext; };
const moduleClass = class RuntimeComponentModule {};
const decoratedComponent = Component(Metadata)(componentClass);
const modules = this.getModulesNeeded();
const decoratedngModule = NgModule({ imports: modules,declarations: [decoratedComponent] })(moduleClass);
const module: ModuleWithComponentFactories<any> = this.compiler.compileModuleAndAllComponentsSync(decoratedngModule);
return module.componentFactories.find(factory => factory.componentType === decoratedComponent);
}
getModulesNeeded() {
return [browserModule,CommonModule];
}
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
if (this.componentRef) {
this.componentRef.destroy();
}
}
}
dynamic-content.component.html
<ng-container>
<ng-template #container></ng-template>
</ng-container>
<div class="social" *ngIf="context.store$ | async as store">
<div class="networks">
<h3>Follow US</h3>
</div>
</div>
正如我所说,当应用程序与ng serve一起运行时,此组件可以正常工作,呈现动态内容。问题是当我使用ng run app-name:serve-ssr使用ssr运行应用程序时,所有内容均来自服务器,除了此动态内容组件。该内容实际上是在服务器中获取的,因为我可以看到模板存储在脚本html标签中的transferState键中,但是我在服务器控制台中遇到了此错误
ERROR Error: The pipe 'async' Could not be found!
at getPipeDef$1 (C:\app\dist\myapp\server\main.js:1:2321863)
at ɵɵpipe (C:\app\dist\myapp\server\main.js:1:2321915)
at template (ng:///.js:150:9)
at executeTemplate (C:\app\dist\myapp\server\main.js:1:2097798)
at renderView (C:\app\dist\myapp\server\main.js:1:2093138)
at renderComponent (C:\app\dist\myapp\server\main.js:1:2111597)
at renderChildComponents (C:\app\dist\myapp\server\main.js:1:2093514)
at renderView (C:\app\dist\myapp\server\main.js:1:2093555)
at ComponentFactory$1.create (C:\app\dist\myapp\server\main.js:1:2313775)
at R3ViewContainerRef.createComponent (C:\app\dist\myapp\server\main.js:1:2130074)
出现此错误后,浏览器似乎控制了控件从transferState键获取内容并呈现该内容,但ssr不适用于这种动态内容。我的通用服务器由ng add Universal命令组成,唯一的变化是代理中间件。
import 'zone.js/dist/zone-node';
import { createProxyMiddleware,Filter,Options,RequestHandler } from 'http-proxy-middleware';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
import { environment } from 'src/environments/environment';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(),'dist/myApp/browser');
const indexHtml = existsSync(join(distFolder,'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine(
'html',ngExpressEngine({
bootstrap: AppServerModule,})
);
server.set('view engine','html');
server.set('views',distFolder);
server.use('/api',createProxyMiddleware({ target: environment?.host,changeOrigin: true }));
server.get(
'*.*',express.static(distFolder,{
maxAge: '1y',})
);
// All regular routes use the Universal engine
server.get('*',(req,res) => {
console.log(req.params);
res.render(indexHtml,{ req,providers: [{ provide: APP_BASE_HREF,useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port,() => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const modulefilename = (mainModule && mainModule.filename) || '';
if (modulefilename === __filename || modulefilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
有什么办法可以使这两件事协同工作吗?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。