Angular基于HTTP动态生成组件:TypeScript代码加载问题求助
__annotations__未定义的错误 Hey, I see exactly what's going wrong here. That TypeError: Cannot read property '__annotations__' of undefined error pops up because you're passing a raw TypeScript string directly to Angular's Component decorator—and that decorator expects a real class constructor, not plain text.
Let's break down the problematic line in your code:
const tmpCmp = Component({ template: "<h1>HEAD</h1>", selector: 'abcd' })(txt);
Here, txt is just a string of TS code you fetched from the server (like class MyDynamicComponent { ... }). It's not a parsed, executable class object yet. Angular's decorator tries to attach metadata to this string, but since strings don't have properties like __annotations__, it throws that error.
Fixing the Issue
To load a remote TS component successfully, you need to turn that string into a usable class first. Here's a step-by-step solution:
- Compile TS to JS (skip this if you fetch pre-compiled JS directly)
- Execute the JS code to get the component class constructor
- Apply Angular's decorators and compile the component normally
Here's the updated code that works:
import { HttpClient, HttpClientModule } from "@angular/common/http"; import { Component, ViewChild, ViewContainerRef, Compiler, Injector, NgModule, NgModuleRef, OnInit } from "@angular/core"; // Import TypeScript library if you need to compile TS in the browser import * as ts from 'typescript'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { @ViewChild('vc', { read: ViewContainerRef }) _container: ViewContainerRef; constructor( private http: HttpClient, private _compiler: Compiler, private _injector: Injector, private _m: NgModuleRef<any> ) { } ngOnInit() { this.http.get("https://raw.githubusercontent.com/ketan-gote/stackblitz-demo/master/ts.txt", { responseType: 'text' }) .subscribe((tsCode: string) => { // Step 1: Transpile TS to JS (skip if fetching pre-compiled JS) const jsCode = ts.transpileModule(tsCode, { compilerOptions: { module: ts.ModuleKind.ES2020, target: ts.ScriptTarget.ES2020, emitDecoratorMetadata: true, experimentalDecorators: true } }).outputText; // Step 2: Execute the JS to get the component class // Ensure your remote code defines a class named DynamicComponent const getComponentClass = new Function(`${jsCode}; return DynamicComponent;`); const dynamicComponentClass = getComponentClass(); // Step 3: Apply Angular's Component decorator const tmpCmp = Component({ // You can also fetch the template from remote here if needed template: "<h1>HEAD</h1>", selector: 'abcd' })(dynamicComponentClass); // Create a temporary module for the component const tmpModule = NgModule({ imports: [HttpClientModule], declarations: [tmpCmp], entryComponents: [tmpCmp] // Optional for Angular 9+, but safe to keep for compatibility })(class { }); // Compile and render the component this._compiler.compileModuleAsync(tmpModule) .then((moduleFactory) => { const moduleRef = moduleFactory.create(this._injector); const resolver = moduleRef.componentFactoryResolver; const cmpFactory = resolver.resolveComponentFactory(tmpCmp); const cmpRef = cmpFactory.create(this._injector, [], null, this._m); this._container.insert(cmpRef.hostView); }) .catch(err => console.error("Failed to load dynamic component:", err)); }); } }
Important Notes
- Security Warning: Using
new Functionto execute remote code carries XSS risks. Only load code from trusted sources to avoid malicious injections. - Angular Production Mode: JIT compilation is disabled by default in production. If you need this to work in prod, you'll need to enable JIT or adjust your build configuration to allow dynamic compilation.
- Remote Code Structure: Make sure your remote TS/JS code defines a class (like
DynamicComponent) that matches what you're trying to return in thenew Functionblock.
内容的提问来源于stack exchange,提问作者ketan gote

