Angular 5组件中如何根据文件存在性条件加载templateUrl?
嘿,这个需求我刚好之前处理过,在Angular里分两种场景来实现会比较靠谱——构建时静态判断和运行时动态加载,我给你详细讲讲:
方案一:构建时静态判断(适合override文件在构建阶段就确定存在与否)
如果你的override/override1.html是项目里的静态文件,构建前就能确定它是否存在,那用这个方案最直接高效,因为可以在编译阶段就确定要加载的模板。
步骤:
自定义Angular CLI的Webpack配置,检测文件是否存在并注入全局变量
- 先安装
fs-extra(用来读取文件系统):npm install fs-extra --save-dev - 在项目根目录创建
webpack.config.js:const fs = require('fs-extra'); const webpack = require('webpack'); module.exports = { plugins: [ new webpack.DefinePlugin({ // 检测override文件是否存在,将结果转为JSON字符串注入全局 OVERRIDE_TEMPLATE_EXISTS: JSON.stringify(fs.existsSync('./src/app/override/override1.html')) }) ] }; - 修改
angular.json,在build配置里关联自定义Webpack:"architect": { "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./webpack.config.js" }, // 保留原有其他配置... } } }
- 先安装
修改组件代码,用函数动态返回模板路径:
import { Component } from '@angular/core'; @Component({ templateUrl: () => OVERRIDE_TEMPLATE_EXISTS ? './override/override1.html' : './test-dynamic.html', styleUrls: ['./test-dynamic.css'] }) export class TestDynamicComponent { // 类名遵循TS大驼峰规范更规范哦 constructor() { } }
方案二:运行时动态加载(适合override文件可能在运行时才出现或来自后端)
如果你的override1.html是动态生成的(比如从后端获取),或者运行时才会存在,那得用运行时加载的方式,分两种情况处理:
情况1:模板是纯静态HTML(无Angular指令/组件)
这种情况可以直接用HttpClient加载模板内容,再通过DomSanitizer安全渲染:
创建模板加载服务:
import { Injectable } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class TemplateLoaderService { constructor(private http: HttpClient) {} loadTemplate(): Observable<string> { // 先尝试加载override模板,404则 fallback 到默认模板 return this.http.get('./override/override1.html', { responseType: 'text' }).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 404) { return this.http.get('./test-dynamic.html', { responseType: 'text' }); } throw error; }) ); } }修改组件代码:
import { Component, OnInit } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TemplateLoaderService } from './template-loader.service'; @Component({ template: '<div [innerHTML]="templateContent"></div>', styleUrls: ['./test-dynamic.css'] }) export class TestDynamicComponent implements OnInit { templateContent: SafeHtml; constructor( private templateLoader: TemplateLoaderService, private sanitizer: DomSanitizer ) { } ngOnInit() { this.templateLoader.loadTemplate().subscribe(template => { this.templateContent = this.sanitizer.bypassSecurityTrustHtml(template); }); } }
情况2:模板包含Angular指令/组件(需要编译)
如果模板里有*ngIf、自定义组件等Angular逻辑,就得动态编译组件。可以用Angular CDK的工具,或者直接用Compiler(注意Angular 9+ Ivy模式下Compiler是私有API,生产环境谨慎使用):
安装Angular CDK:
npm install @angular/cdk修改组件代码:
import { Component, OnInit, ViewChild, ViewContainerRef, Compiler, Injector, NgModule } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { catchError } from 'rxjs/operators'; @Component({ template: '<ng-template #templateContainer></ng-template>', styleUrls: ['./test-dynamic.css'] }) export class TestDynamicComponent implements OnInit { @ViewChild('templateContainer', { read: ViewContainerRef }) container: ViewContainerRef; constructor( private http: HttpClient, private compiler: Compiler, private injector: Injector ) { } ngOnInit() { this.http.get('./override/override1.html', { responseType: 'text' }).pipe( catchError(() => this.http.get('./test-dynamic.html', { responseType: 'text' })) ).subscribe(template => { // 创建临时动态组件 const DynamicTemplateComponent = Component({ template })(class {}); // 创建临时模块 const DynamicTemplateModule = NgModule({ declarations: [DynamicTemplateComponent] })(class {}); // 编译模块并渲染组件 this.compiler.compileModuleAndAllComponentsAsync(DynamicTemplateModule).then(moduleFactories => { const componentFactory = moduleFactories.componentFactories.find(f => f.componentType === DynamicTemplateComponent); if (componentFactory) { this.container.createComponent(componentFactory, 0, this.injector); } }); }); } }
小提示:
- 构建时方案性能更好,适合静态文件场景;
- 运行时方案更灵活,但带Angular逻辑的模板动态编译会有一定性能开销,生产环境建议缓存编译结果。
内容的提问来源于stack exchange,提问作者ranjit redekar
相关产品推荐
相关产品推荐

