在Modal组件中动态创建组件 - Angular 4

时间:2018-05-21 22:23:10

标签: javascript angular typescript modal-dialog

到目前为止,我正在创建自己的模态组件,它可以动态创建我的初始模态组件,但后来我尝试在模态组件中创建另一个组件。

使用下面的代码,对于模态容器组件,我在模板变量上有一个viewCotainerRef,然后在该容器div中创建组件。当我尝试在DynamicContentComponent内部渲染我的ModalComponent时,我的问题就到了,我在第一次点击时无法获得viewContainerRef,因为它是undefined,但是在第二次单击模态触发器时它是定义。

所以当我生成DynamicContentComponent时,我会在ModalComponent上设置'viewContainerRef'。将它发送到渲染服务,当我可以清楚地看到它控制台登出时很好。但是当我使用相同的DynamicContentComponent渲染实际的viewContainerRef时,它是未定义的。

很抱歉,如果这有点令人困惑,代码在下面,但如果这还不够,我会在下班后发布plunkr。

由于

ModalComponentService

@Injectable()
export class ModalService {
  public showModal: Subject<any> = new Subject();
  public viewContainerRef: ViewContainerRef;
  public injector: Injector;
  private resolver: ComponentFactoryResolver;
  private compiler: Compiler;
  private render: RenderService;

  constructor(resolver: ComponentFactoryResolver, compiler: Compiler, renderer: RenderService) {
    this.render = renderer;
    this.resolver = resolver;
    this.compiler = compiler;
  }

  public setViewContainerRef(vcRef: ViewContainerRef): void {
    this.viewContainerRef = vcRef;
  }

  public setInjector(injector: Injector): void {
    this.injector = injector;
  }

  public createDialog(module: any, component: any, paramsModal?: Object, paramsComponent?: Object) {

    const componentRef$ = new ReplaySubject();

    this.compiler.compileModuleAndAllComponentsAsync(module).then((data) => {
      const standardModalFactory = data.componentFactories.filter( (singleComponents) => {
        return singleComponents.componentType === StandardModalComponent;
      })[0];

      const modalRef = this.viewContainerRef.createComponent(standardModalFactory);

      modalRef.instance['destroy'] = () => {
        modalRef.destroy();
      };

      Object.assign(modalRef.instance, paramsModal);

      componentRef$.next(modalRef);
      componentRef$.complete();
    });

    this.render.renderComponent(module, component, paramsComponent).subscribe((data) => {
      console.log(data); 
    });

    return componentRef$;
  }
}

ModalComponent

export class ModalComponent implements OnInit {
  @ViewChild('modalPlaceholder', {read: ViewContainerRef}) container;

  @Output()
  public appModalCloseModal: EventEmitter<any> = new EventEmitter<void>();

  public showModal = true;
  private modalService: ModalService;
  public injector: Injector;

  constructor(modalService: ModalService, injector: Injector) {
    this.modalService = modalService;
    this.injector = injector;
  }

  public ngOnInit(): void {
    this.modalService.setViewContainerRef(this.container);
    this.modalService.setInjector(this.injector);
  }
}

ModalComponent模板

<div #modalPlaceholder class="appModal-placeholer"></div>

StandardModalComponent - 注入上面的容器

export class StandardModalComponent extends StandardModal implements OnInit {
  @ViewChild('standardModalContent')
  public standardModalContent;

  @Input()
  public appStandardModalHeaderLabel: string;

  private renderService: RenderService;

  constructor(render: RenderService) {
    super();
    this.renderService = render;
  }

  ngOnInit() {
    this.renderService.setModalReference(this.standardModalContent);
  }
}

在上面的Init上,它是为内容组件设置第二个viewContainerRef的地方。

StandardModalComponent - Template

因此,需要在模态组件中显示的组件将被注入standardModalComponent的引用

<div class="appStandardModal-container">
  <div class="appStandardModal-overlay">
    <div class="appStandardModal-modalDialog">

      <div class="appStandardModal-header">
        <div class="appStandardModal-titleContainer">
          <h3>{{appStandardModalHeaderLabel}}</h3>
        </div>

        <div class="appStandardModal-closeModal">
          <button (click)="closeModal()">
            <fa name="times"></fa>
          </button>
        </div>
      </div>

      <div #standardModalContent class="appStandardModal-content">

      </div>

    </div>
  </div>
</div>

这是我的RenderService,可以在#standardModalContent引用中动态创建所需的组件。

@Injectable()
export class RenderService {
  public viewContainer: ViewContainerRef;
  public injectors: Injector;
  private compiler: Compiler;
  public modalComponentReference$: ReplaySubject<any>;

  constructor(compiler: Compiler) {
    this.compiler = compiler;
  }

  public setModalReference(viewContainer: ViewContainerRef): void {
    this.viewContainer = viewContainer;
    console.log('setModalReference', this.viewContainer);
  }

  public setModalInjectorReference(injector: Injector): void {
    this.injectors = injector;
  }

  public renderComponent(module: any, component: any, componentParams?: Object) {
    this.modalComponentReference$ = new ReplaySubject();
    console.log('setModalReference in render component', this.viewContainer);

    const factory = component.componentFactory;
    const createdComponent = this.viewContainer.createComponent(factory);

    Object.assign(createdComponent.instance, componentParams);

    this.modalComponentReference$.next(createdComponent);

    return this.modalComponentReference$;
  }

} 

最初在service.setModalReference上调用standardModalComponent时,它会被正确记录,但是当我来调用renderComponent方法时,在第一次单击模态触发器时未定义但是,之后的任何点击都没有。

可能没用,但提供了更多的想法,这里是我的组件下面的点击触发器

OpenModalComponent

export class OrderedNamesComponent implements IOrderedNames, OnInit {
  @Input()
  public appOrderedNamesName: boolean;

  @Input()
  public appOrderedNamesIndex: number;

  private modalService: ModalService;
  private params: { appStandardModalHeaderLabel: string; onSave: () => void };

  constructor(modalService: ModalService) {
    this.modalService = modalService;
  }

  public ngOnInit(): void {
    this.setParams();
  }

  public emitToParent() {
    this.modalService.createDialog(AppModule, TestComponentComponent, this.params);
  }

  private setParams(): void {
    this.params = {
      appStandardModalHeaderLabel: 'Select Teams',
      onSave: () => alert('save me')
    };
  }
}

0 个答案:

没有答案