Angular Material Stepper - 如何动态创建组件以加载到步骤中 [英] Angular Material Stepper - how to dynamically create components to load into steps

查看:33
本文介绍了Angular Material Stepper - 如何动态创建组件以加载到步骤中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看过很多类似的帖子,但没有一个能帮到我.

我希望能够使用 @ngFor 创建组件,然后加载到

所以,这个字符串step"应该是 ngFor 变量...

但它只是作为一个字符串传入,而不是组件.

有没有人知道我在这里做错了什么,或者即使有更好的方法来做到这一点?

有关此示例的更多详细信息,可以从

所以,我现在的问题是在执行行 const componentFactory=this.componentFactoryResolver.resolveComponentFactory(this.item)

时抛出以下错误

ngComponent: StepPage1Component {}消息:找不到 [object Object] 的组件工厂.您是否将其添加到 @NgModule.entryComponents 中?"堆栈:错误:未找到 [object Object] 的组件工厂.您是否将其添加到 @NgModule.entryComponents?↵ at noComponentFactoryError (http://localhost:4200/vendor.js:71338:17)↵ at CodegenComponentFactoryResolver.push../node_modules/@angular/core/fesm5/core.js.CodegenComponentFactoryResolver.resolveComponentFactory (http://localhost:4200/vendor.js:71376:19)↵ 在 StepPageWrapperComponent.push../src/app/step-page-wrapper/step-page-wrapper.component.ts.StepPageWrapperComponent.ngOnInit (http://localhost:4200/main.js:244:66)↵ 在 checkAndUpdateDirectiveInline (http://localhost:4200/vendor.js:83560:19)↵ at checkAndUpdateNodeInline (http://localhost:4200/vendor.js:84824:20)↵ at checkAndUpdateNode (http://localhost:4200/vendor.js:84786:16)↵ at debugCheckAndUpdateNode (http://localhost:4200/vendor.js:85420:38)↵ at debugCheckDirectivesFn (http://localhost:4200/vendor.js:85380:13)↵ at Object.eval [as updateDirectives] (ng:///AppModule/WizardStepperComponent.ngfactory.js:16:5)↵ 在 Object.debugUpdateDirectives [as updateDirectives] (http://localhost:4200/vendor.js:85372:21)"__proto__:对象

确实将此作为 entryComponent

@NgModule({声明: [应用组件,向导步进组件,StepPage1组件,StepPage2组件,StepPage3组件,页面指​​令,StepPageWrapper 组件],进口:[浏览器模块,浏览器动画模块,垫步进模块,垫输入模块,垫按钮模块,自动完成模块],提供者:[],引导程序:[AppComponent],入口组件:[StepPage1组件,StepPage2组件,StepPage3组件,StepPageWrapper 组件]

[UPDATE2 8 月 19 日 30 日]

进入this.componentFactoryResolver.resolveComponentFactory,看到如下...

所以组件似乎在地图中,但 map.get 返回未定义?

解决方案

所以组件似乎在地图中,但 map.get 正在返回未定义?

是的,这是误导.您传递的是类的实例而不是类本身.

考虑以下示例:

class 测试 {}控制台日志(测试);//类测试{}控制台日志(新测试());//测试 {}

如您所见,输出非常相似,但它们是两种不同的东西.

所以解决方案是传递组件类型而不是组件实例:

this.steps = [StepPage1组件,StepPage2组件,StepPage3组件]

I have seen a number of similar posts, but none help me.

I wish to be able to use @ngFor to create components to then load into steps of the Material Stepper

I have been following an example of dynamic loading, and some other posts (which are incomplete), and come up with the following.

First I have a number of components I want to load into the steps. They are just empty apart from the string (eg "component is working"

eg

export class StepPage1Component implements OnInit, StepPage {
      constructor() { }
      ngOnInit() {
      }
}

So I have 3 of these.

The "root" component is as follows.

export class WizardStepperComponent implements OnInit {
  @ViewChild('stepper') private myStepper: MatStepper;
  totalStepsCount: number;

  public steps: StepPage[];

  constructor(private stepsService: StepPagesService) { 
    this.steps = [
      new StepPage1Component,
      new StepPage2Component,
      new StepPage3Component
    ]
  }

Markup:

<mat-horizontal-stepper #stepper>
  <mat-step *ngFor='let step of steps'>
    <app-step-page-wrapper item='step'></app-step-page-wrapper>
  </mat-step>     
</mat-horizontal-stepper>

<!-- second option -->
<div>
  <button (click)="goBack(stepper)" type="button" [disabled]="stepper.selectedIndex === 0">Back</button>
  <button (click)="goForward(stepper)" type="button"
    [disabled]="stepper.selectedIndex === stepper._steps.length-1">Next</button>

  <!-- using totalStepsCount -->
  <!-- <button (click)="goForward(stepper)" type="button" [disabled]="stepper.selectedIndex === totalStepsCount-1">Next</button> -->
</div>

So in markup above, I have the ngFor that I am having problems with.

The wrapper is as follows...

import { Component, OnInit, Input, ViewChild, ComponentFactoryResolver } from '@angular/core';
import { directiveDef } from '@angular/core/src/view';
import { PageDirective } from '../page.directive';

@Component({
  selector: 'app-step-page-wrapper',
  template: '<ng-template pageDirective></ng-template>',
})
export class StepPageWrapperComponent implements OnInit {
  @Input() item?: any;
  @ViewChild(PageDirective) pageHost: PageDirective;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngOnInit() {
    if (this.item == undefined) {
      console.error('Item undefined');
      return 
    }

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.item);
    const viewContainerRef = this.pageHost.viewContainerRef;
    viewContainerRef.clear();
    const componentRef = viewContainerRef.createComponent(componentFactory);
  }
}

and finally the directive it is using is..

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[pageDirective]'
})
export class PageDirective {

  constructor(public viewContainerRef: ViewContainerRef) { }
}

So, I am trying to use the StepPageWrapperComponent to take each component as an input (item), and then use the componentFactoryResolver to create the conponent and then "attach" it to the directive in the template, ie pageDirective in <ng-template pageDirective></ng-template>

However, when I run this, I get the following error..

        ERROR Error: No component factory found for step. Did you add it to @NgModule.entryComponents?
        at noComponentFactoryError (core.js:9877)
        at CodegenComponentFactoryResolver.push../node_modules/@angular/core/fesm5/core.js.CodegenComponentFactoryResolver.resolveComponentFactory (core.js:9915)
        at StepPageWrapperComponent.push../src/app/step-page-wrapper/step-page-wrapper.component.ts.StepPageWrapperComponent.ngOnInit (step-page-wrapper.component.ts:21)
        at checkAndUpdateDirectiveInline (core.js:22099)
        at checkAndUpdateNodeInline (core.js:23363)
        at checkAndUpdateNode (core.js:23325)
        at debugCheckAndUpdateNode (core.js:23959)
        at debugCheckDirectivesFn (core.js:23919)
        at Object.eval [as updateDirectives] (WizardStepperComponent.html:3)
        at Object.debugUpdateDirectives [as updateDirectives] (core.js:23911)

If I put a break point in StepPageWrapperComponent, this.item just seems to be a string!

So, this string "step" is meant to be the ngFor variable...

But it is just coming in as a string, rather than the component.

Does anyone have any ideas on what I am doing wrong here, or even if there is a better way of doing this?

For any more details the source for this sample is can be cloned from https://github.com/pjc2007/wizard-stepper1.git

Thanks in advance for any help!

[UPDATE 30 Aug 19]

The reason the input was not working was I forgot the square brackets when passing the variable to the input.. ie I had

<app-step-page-wrapper item='step'></app-step-page-wrapper>

instead of

    <app-step-page-wrapper [item]='step'></app-step-page-wrapper>

I now have the component as the input for StepPageWrapperComponent as expected.

So, my problem now is the following error that is thrown when executing the line const componentFactory=this.componentFactoryResolver.resolveComponentFactory(this.item)

ngComponent: StepPage1Component {}
message: "No component factory found for [object Object]. Did you add it to @NgModule.entryComponents?"
stack: "Error: No component factory found for [object Object]. Did you add it to @NgModule.entryComponents?↵    at noComponentFactoryError (http://localhost:4200/vendor.js:71338:17)↵    at CodegenComponentFactoryResolver.push../node_modules/@angular/core/fesm5/core.js.CodegenComponentFactoryResolver.resolveComponentFactory (http://localhost:4200/vendor.js:71376:19)↵    at StepPageWrapperComponent.push../src/app/step-page-wrapper/step-page-wrapper.component.ts.StepPageWrapperComponent.ngOnInit (http://localhost:4200/main.js:244:66)↵    at checkAndUpdateDirectiveInline (http://localhost:4200/vendor.js:83560:19)↵    at checkAndUpdateNodeInline (http://localhost:4200/vendor.js:84824:20)↵    at checkAndUpdateNode (http://localhost:4200/vendor.js:84786:16)↵    at debugCheckAndUpdateNode (http://localhost:4200/vendor.js:85420:38)↵    at debugCheckDirectivesFn (http://localhost:4200/vendor.js:85380:13)↵    at Object.eval [as updateDirectives] (ng:///AppModule/WizardStepperComponent.ngfactory.js:16:5)↵    at Object.debugUpdateDirectives [as updateDirectives] (http://localhost:4200/vendor.js:85372:21)"
__proto__: Object

I do have this as an entryComponent

ie

@NgModule({
  declarations: [
    AppComponent,
    WizardStepperComponent,
    StepPage1Component,
    StepPage2Component,
    StepPage3Component,
    PageDirective,
    StepPageWrapperComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MatStepperModule,
    MatInputModule,
    MatButtonModule,
    MatAutocompleteModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [
    StepPage1Component,
    StepPage2Component,
    StepPage3Component,
    StepPageWrapperComponent
  ]

[UPDATE2 30 Aug 19]

Stepping into this.componentFactoryResolver.resolveComponentFactory, I see the following...

So the component seems to be in the map, but the map.get is returning undefined?

解决方案

So the component seems to be in the map, but the map.get is returning undefined?

Yes, it's misleading. You're passing instance of class not class itself.

Consider the following example:

class Test {}
console.log(Test);       // class Test {}
console.log(new Test()); // Test {}

As you can see the output is quite similar but they are two different things.

So the solution is to pass component Type instead of component instance:

this.steps = [
  StepPage1Component,
  StepPage2Component,
  StepPage3Component
]

这篇关于Angular Material Stepper - 如何动态创建组件以加载到步骤中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆