首页 > 解决方案 > 动态加载的 Angular 10 组件无法访问 CommonModule

问题描述

我目前正在开发一个 Angular 应用程序。在我的一种方法中,我动态创建了一个组件,但是我无法使用ngClass,ngIf以及来自组件中的其他此类指令CommonModule

以下是错误示例

在动态加载的徽标组件中使用 ngIf 或 ngClass 时出错

我已经做了什么:

快速查看组件工厂 createcomponent 的角度文档

我已经为此花费了几个小时,并相信这与我使用该createComponent方法有关。

请帮忙!

这是我的 app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule, HammerGestureConfig, HammerModule, 
 HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';
import { GoogleAnalyticsService } from 
 './shared/services/googleanalytics'; // import our Google Analytics 
 service
import { BrowserAnimationsModule } from '@angular/platform- 
browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';


@NgModule({
  declarations: [AppComponent],
  imports: [
   CommonModule,
   BrowserModule,
   AppRoutingModule,
   HttpClientModule,
   BrowserAnimationsModule,
   ],
  providers: [GoogleAnalyticsService], 
  bootstrap: [AppComponent],
 })
 export class AppModule {}

这是我的显示组件 (Bdoc-a.component.ts)

import {
  Component, OnInit, Input, Output, Renderer2, AfterViewInit, 
 AfterContentChecked, ViewChild, ElementRef,
  ComponentFactoryResolver, ViewContainerRef, ViewChildren, QueryList
  } from '@angular/core';
 import { StorageService } from 
 '../../../../shared/services/storage.service';
import { AuthService } from '../../../../auth/auth.service';

 @Component({
  selector: 'app-bdoc-a',
  templateUrl: './bdoc-a.component.html',
  styleUrls: ['./bdoc-a.component.scss'],
 })
 export class BdocAComponent implements OnInit, AfterViewInit, 
 AfterContentChecked {
  @Input() docConfig = { aspectRatio: '4:3', width: 800, height: 500 };
  @Input() bgStyles = { shadow: true, bgClr: '#ffcc00' };
  @Input() showBtns = false;

  fWidth = this.docConfig.width;
  fMaxWidth = this.docConfig.width;
  fHeight = this.docConfig.height;
  fMaxHeight = this.docConfig.height;

  @Input() url = '';
  @Input() settings = { ...  };


  ...

  @ViewChildren('loadDynAssetElEditItms', { read: ViewContainerRef }) 
  biEditEls: QueryList<ViewContainerRef>;
  @ViewChildren('loadDynAssetElViewItms', { read: ViewContainerRef }) 
  biViewEls: QueryList<ViewContainerRef>;
  loadDynAssetEl: any;


  @ViewChild('bieditorFloat', { read: ElementRef }) bieditorFloat: 
  ElementRef;
  @ViewChild('bieditorFloatViewer', { read: ElementRef }) 
  bieditorFloatViewer: ElementRef;

  // variable to hold all document page elements
  allDocPages: any;
  allDocPageComp = [];


  constructor(private storage: StorageService,
          private resolver: ComponentFactoryResolver,
          private authService: AuthService,
          private elmRef: ElementRef) {
   this.url = 'templates/logo/1/logo1a/logo1a.component';
   }


   ngAfterViewInit(): void {
    this.initBrandAsset();
   }

  initBrandAsset(): void {
    this.allDocPageComp = [];

    setTimeout(() => {
      for (let i = 0; i < this.pages; i++) {
        this.loadDynAsset(this.url, i);
      }
      this.setDocPageVar();
    }, 200);
  }

  async loadDynAsset(url, pgIndex) {
    const impEl = await import( 'src/app/' + url);
    const allKeys = Object.keys(impEl);


    this.biEditEls.forEach((itm, i) => {
      if (i === pgIndex) {
      itm.clear();
      const newComp = itm.createComponent(
      this.resolver.resolveComponentFactory(impEl[allKeys[0]]));
      newComp.instance['bol']['test'] = 'LOGO TEST TEXT HERE... ' + pgIndex;

      this.allDocPageComp.push(newComp.instance);
    }
    });

    }
   }

这是我的 Logo1a.component.html

   <div class="baItem biLogo logo1a edit">
    <div class="null bilNull">
      <ng-container>
        <div class="bilSymb">
          <div class="null">
           <div #forTxtLogo class="forTxtLogo" *ngIf="config.symb.mode === 
         'txt'">
        <div class="symbItm"><div class="nl">B</div></div><div 
     class="symbItm"><div class="nl">S</div></div>
      </div>
      <div #forImgSvgLogo class="forImgSvgLogo" *ngIf="config.symb.mode 
     === 'svg' || config.symb.mode === 'img'">
        <div class="symbItm"><div class="nl"></div></div>
      </div>
    </div>
   </div><!-- end of bilSymb -->
   </ng-container>
   <ng-container *ngIf="config.body.show">
    <div class="bilBody">
    <div class="null">
    <ng-container *ngIf="config.body.txt.show">
      <div #forTxtArea class="forTxtArea">
        <div class="null">Logo Body Text Area</div>
      </div>
    </ng-container>
    <ng-container *ngIf="config.body.tag.show">
      <div #forTagArea class="forTagArea"><div class="null">Logo Tag 
   Area...</div></div>
    </ng-container>
  </div>
   </div><!-- end of bilBody -->
   </ng-container>
   </div><!-- end of biNull -->
   </div><!-- end of biLogo-->

这是我的 Logo1a.component.ts

import { NgModule, Component, OnInit, Input, Output, Renderer2, 
AfterViewInit, AfterContentChecked, ViewChild, ElementRef,
 ComponentFactoryResolver, ViewContainerRef, ViewChildren, QueryList
} from '@angular/core';
 import { StorageService } from 
'../../../../shared/services/storage.service';
 import { AuthService } from '../../../../auth/auth.service';

 @Component({
  selector: "app-logo1a",
   templateUrl: "./logo1a.component.html",
  styleUrls: ["./logo1a.component.scss"],
})


export class Logo1aComponent implements OnInit, AfterViewInit {
  @ViewChild('loadExtra', { read: ViewContainerRef }) loadExtra: 
 ViewContainerRef;

 @Input() bol = { test: 'LOGO 1A LOADED!' }; // breadth of life

 public config = {
   symb: {
   show: true,
   mode: 'txt', // 'txt', 'symb', 'img'
  },
 body: {
  show: true,
  txt: { show: true },
  tag: { show: true }
  }
 }

  constructor(private storage: StorageService,
          private resolver: ComponentFactoryResolver,
          private vcRef: ViewContainerRef,
          private authService: AuthService,
          private elmRef: ElementRef) { }

     ngOnInit(): void {}

     ngAfterViewInit() {}
  }

这是我的 Logo1a.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Logo1aComponent } from './logo1a.component';

@NgModule({
  imports: [CommonModule],
  declarations: [Logo1aComponent],
  exports: [Logo1aComponent]
})
export class Logo1aModule {}

但这是我在我的 Angular 10 应用程序中动态导入模块而不是组件后引发的错误,正如@jburtondev 建议的那样:

错误错误:未捕获(在承诺中):错误:断言错误:传入的类型不是 ComponentType,它没有“ɵcmp”属性。……

标签: javascriptangulartypescript

解决方案


所以我终于有了一个不太干净的工作方法来解决我的问题。

  1. 我创建了一个新模块 LogoModules,我在其中导入了将在显示组件中显示的所有徽标模块

    从'@angular/core'导入{ NgModule };从'../templates/logo/1/logo1a/logo1a.module'导入{ Logo1aModule };从'../templates/logo/2/logo2a/logo2a.module'导入{ Logo2aModule };从'../templates/logo/3/logo3a/logo3a.module'导入{ Logo3aModule };

    @NgModule({ // 导入:[], // 导出:[], })

    导出类 LogoModules {}

这从@jburtondev 的建议中进行了一些修改,以预先导入所有模块,但我仍然认为应该有一种更简洁的方法来完成这部分。在不预先导入模块的情况下动态加载模块和组件仍然有效,但有时也会失败。我认为这是由于 tsconfig 中的编译器和 webpack 的一些设置。我需要更多的研究来澄清这一点。

  1. 我在显示组件中导入了 LogoModules 模块

    从'../templates/logo/logos.module'导入{ LogoModules };...

但是......所以我能够将每个徽标模板的相对路径存储在我的数据库中并根据用户点击动态显示它们,我在我的显示组件(Bdoc-a.component.ts)中更新了我的动态组件加载器,如图所示以下:

....
async loadDynAsset(url, pgIndex) {
  const mObj = await import('src/app/' + url2);
  const mKeys = Object.keys(mObj);
  const mName = mKeys[0];

  this.compiler.compileModuleAndAllComponentsAsync(mObj[mName])
    .then((factories) => {
      const f = factories.componentFactories[0];
      const newComp = this.testDynComp.createComponent(f);
      newComp.instance['bol']['test'] = 'LOGO TEST TEXT HERE... ' + pgIndex;
    });

  .... 

}
....

然后我能够得到一致的结果而没有错误。再次感谢@jburtondev 的一些建议。一旦我发现更好的方法来解决我的问题,我会立即更新我的答案。


推荐阅读