微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 Ivy-Compiler 在 Angular 9/11 中递归渲染动态组件

如何解决如何使用 Ivy-Compiler 在 Angular 9/11 中递归渲染动态组件

我们最近从 Angular v7 迁移到了 Angular v11。选择退出常春藤编译器后,应用程序再次开始工作。 但是,我们现在要启用 Ivy-Compiler。为此,需要对我们如何实例化动态组件进行一些重构。

从那时起,应用程序现在可以编译,但在渲染特定动态组件时会在运行时引发错误,而这些组件本身会在树的更深处渲染相同的动态组件。

动态组件的第一个实例化工作并呈现内容内容是一些其他组件,它本身应该呈现另一个动态组件。第二个动态组件的内容永远不会呈现,控制台中出现以下错误

NG0304: 'generic-component' is not a kNown element:
1. If 'generic-component' is an Angular component,then verify that it is part of this module.
2. If 'generic-component' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.

这是浏览器中当前的 HTML 输出

<generic-component>
  <div></div>
  <div class="content">
    <blocks ng-reflect-model="[object Object]" ng-reflect-blocks="[object Object]">
      <div ng-reflect-switch="genericComp">
        <generic-component>
        <!-- Content is missing and the above error (NG0304) occurs -->
    </generic-component>
      </div>
    </blocks>   
  </div>
</generic-component>

我对此进行了大量研究,但似乎很难为当前版本的 angular 找到正确答案,因为过去许多方面似乎都发生了变化。

当发生这样的错误时,我知道我应该将包含动态组件指令的模块导入到应该呈现它的模块中。但是我不能这样做,因为它会创建循环依赖(通用组件导入块-> 导入通用组件)。 在以前的 angular 版本上(没有导入),因为组件的范围似乎不同。

关于如何解决这个问题的任何想法?是否可以在中间组件中递归渲染动态组件?

我已经把代码拆下来贴在下面了。

这是动态组件:

@Component({
    selector: 'generic-component',inputs: ['block','model'],template: '<div #vcr></div>'
})
export class GenericComponent implements AfterViewInit {

    @ViewChild('vcr',{read: ViewContainerRef})
    public genericUiComponentTarget: ViewContainerRef;

    private componentRef: ComponentRef<GenericUi>;
    
    constructor(private readonly factoryProvider: DynamicUiFactoryProvider) {
    }
    
    ngAfterViewInit(): void {
      let comp: AngularComponent = someComponent; // Pseudocode; "someComponent" is fetched via rest-service
      this.createComponent(comp);
    }
    
    private createComponent(comp: AngularComponent) {
      this.factoryProvider.createComponentFactory(comp).then(
          (factory: ComponentFactory<GenericUi>) => {
              this.componentRef = this.genericUiComponentTarget.createComponent(factory);
              this.componentRef.instance.initComponent(comp);
          }
      );
    }
}

DynamicUiFactoryProvider:

@Injectable()
export class DynamicUiFactoryProvider {

  constructor(private compiler: Compiler) { }

  createComponentFactory(componentDef: AngularComponent): Promise<ComponentFactory<GenericUi>> {
  if (componentDef.componentType == 1) {
    switch(componentDef.componentId){
        case 0:
            componentType = SomeBlockComponent;
        default:
            componentType = SomeOtherComponent;
    }
  }
  
  return new Promise((resolve) => {
    this.compiler.compileModuleAndAllComponentsAsync(GenericModule).then(
        (moduleWithFactories) => {
            factory = moduleWithFactories.componentFactories.find(component => component.componentType === componentType);
            resolve(factory);
        }
    )};
  });
  
}

通用模块:

@NgModule({
    imports: [
        CommonModule,FormsModule,BlockModule,// Contains "BlocksComponent"
    ],declarations: [
        GenericComponent,SomeBlockComponent,SomeOtherComponent,],exports: [
        GenericComponent,]
})
export class GenericModule { }

SomeBlockComponent 渲染 blocks-element 本身渲染 generic-component 标签

@Component({
    moduleId: module.id,selector: 'dynamic-block-html',template: `
      <div class="content">
          <blocks
              *ngIf="model.content"
              [model]='model'
              [blocks]="model.content.getAsList()"
          ></blocks>
      </div>
    ` 
})
export class SomeBlockComponent extends AbstractBlockComponent implements GenericUi {
  
  public initComponent(componentDef: AngularComponent) {
    // some initialization-code,e.g. loading the model and its block-deFinitions
  }
  
}

BlocksComponent(呈现第二个通用组件标签):

@Component({
    moduleId: module.id,selector: 'blocks',templateUrl: 'view/blocks.html',inputs: ['blocks','model']
})
export class BlocksComponent{
}
<div *ngFor="let block of blocks">
    <div [ngSwitch]="block.type">
        <some-other-component *ngSwitchCase="'otherComp'"></some-other-component>
        <generic-component [block]="block" [model]="model" *ngSwitchCase="'genericComp'"></generic-component>
    </div>
</div>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。