Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Angular 动态组件加载器-解析组件的共享服务_Angular_Typescript_Angular2 Services_Dynamic Loading - Fatal编程技术网

Angular 动态组件加载器-解析组件的共享服务

Angular 动态组件加载器-解析组件的共享服务,angular,typescript,angular2-services,dynamic-loading,Angular,Typescript,Angular2 Services,Dynamic Loading,我必须使用动态添加组件。 例如,我有一个模式窗口,在按下视图上的某个按钮后动态加载该窗口。 此操作的代码如下所示: let componentFactory = this.componentFactoryResolver.resolveComponentFactory(XYZComponent); let viewContainerRef = this.containerHost.viewContainerRef; viewContainerRef.clear(); let componentR

我必须使用动态添加组件。 例如,我有一个模式窗口,在按下视图上的某个按钮后动态加载该窗口。 此操作的代码如下所示:

let componentFactory = this.componentFactoryResolver.resolveComponentFactory(XYZComponent);
let viewContainerRef = this.containerHost.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);

let _XYZComponent = componentRef.instance as XYZComponent;

_XYZComponent.close.subscribe(() => {
    componentRef.destroy();
});
export class SomeComponentThatRendersTheModal() {
  renderFooComponent() {
    // I don't know how you call your modal, so I'll just assume there's a modal service or whatever
    this.modalService.openModal();
    this.modalService.createComponent(
      'FooComponent', { 
        inputs  : { fooInputTest  : 'kitten' },
        outputs : { fooOutputTest : handleOutput } 
      }
    );
  }

  // You can pass this method as the subscription `next` handler
  handleOutput(emittedEvent) {
    // ...
  }
}
@Component({
  selector: 'foo',
  template: `
    <h1>Foo Component, here's the input: " {{ fooInputTest }} "</h1>
    <button (click)="fooOutputTest.emit('Greetings from foo')">Foo output test</button>
  `
})
export class FooComponent {
  @Input()
  fooInputTest: any;

  @Output()
  fooOutputTest: EventEmitter<any> = new EventEmitter<any>();
}
每次我想使用这个模式窗口时,我都需要输入类似的代码,只是使用不同的组件名称

我想为此创建一个共享服务,但我找不到一个好的解决方案,我是说动态加载的组件


您知道如何创建一个好的服务来使用此代码吗?

因为您将动态加载组件,所以您必须在某个地方注册这些组件,该位置位于模态组件装饰器的
entryComponents
中。此外,由于这些组件是typescript
class
es,因此您需要从调用模态的任何组件导入它们。为了在一个地方处理这个问题,我们将把它们导入到一个文件中,然后在数组中导出它们

因此,在这里您将保留所有可能的动态组件,让我们调用此文件
dynamic components.ts

import { FooComponent } from './path/to/foo';
import { BarComponent } from './path/to/bar';
import { BazComponent } from './path/to/baz';

// export all dynamic components
export const dynamicComponents = [
  FooComponent, BarComponent, BazComponent
]
然后在模态组件中,可以将这些组件扩展到
entryComponents
属性中

import { dynamicComponents } from './path/to/dynamic-components';
@Component({
  //...
  entryComponents: [ ...dynamicComponents ]
})
export class ModalComponent {}
到目前为止,你应该都知道

现在,在模态组件内部,您可以创建一个方法,该方法将以组件名称和一些元数据作为参数呈现组件,以动态处理props,我所说的props是指
@Input()
@Output()
装饰属性。这将使模态更加灵活,因为您将能够使用不同的输入和输出渲染组件

因此,您不必像现在这样在方法中硬编码组件,而必须从
dynamicComponents
数组中提取它

由于Javascript类是函数的糖类语法,所有非匿名函数都有一个
name
属性,这样您就可以将函数提供的name参数与
dynamicComponents
中组件的名称相匹配

export class ModalComponent { 
  //...
  createComponent(name: string, metadata: any) {
    const viewContainerRef = this.entryContainer.viewContainerRef;
    const cmpClass = dynamicComponents.find(cmp => cmp.name === name);
    const cmpToCreate = new DynamicComponent(cmpClass, metadata);

    const componentFactory = this.cmpFactoryResolver.resolveComponentFactory(cmpToCreate.component)

    viewContainerRef.clear();

    const cmpRef = viewContainerRef.createComponent(componentFactory);


    // patch input values ...
    for ( let input in metadata.inputs ) {
      if ( metadata.inputs.hasOwnProperty(input) ) {
        cmpRef.instance[input] = metadata.inputs[input];
      }
    }


    // and subscribe to outputs
    for ( let output in metadata.outputs ) {
      if ( metadata.outputs.hasOwnProperty(output) ) {
        console.log('hasOuput', metadata.outputs[output]);
        cmpRef.instance[output].subscribe(metadata.outputs[output]);
      }
    }
  }
}
有几件事要提。下面是
DynamicComponent
类的定义:

export class DynamicComponent {
  constructor(public component: Type<any>, data: any) {}
}
其中
FooComponent
是这样的:

let componentFactory = this.componentFactoryResolver.resolveComponentFactory(XYZComponent);
let viewContainerRef = this.containerHost.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);

let _XYZComponent = componentRef.instance as XYZComponent;

_XYZComponent.close.subscribe(() => {
    componentRef.destroy();
});
export class SomeComponentThatRendersTheModal() {
  renderFooComponent() {
    // I don't know how you call your modal, so I'll just assume there's a modal service or whatever
    this.modalService.openModal();
    this.modalService.createComponent(
      'FooComponent', { 
        inputs  : { fooInputTest  : 'kitten' },
        outputs : { fooOutputTest : handleOutput } 
      }
    );
  }

  // You can pass this method as the subscription `next` handler
  handleOutput(emittedEvent) {
    // ...
  }
}
@Component({
  selector: 'foo',
  template: `
    <h1>Foo Component, here's the input: " {{ fooInputTest }} "</h1>
    <button (click)="fooOutputTest.emit('Greetings from foo')">Foo output test</button>
  `
})
export class FooComponent {
  @Input()
  fooInputTest: any;

  @Output()
  fooOutputTest: EventEmitter<any> = new EventEmitter<any>();
}
@组件({
选择器:“foo”,
模板:`
Foo组件,下面是输入:“{{foointest}”
输出测试
`
})
导出类组件{
@输入()
食物输入测试:任何;
@输出()
fooOutputTest:EventEmitter也是。希望有帮助

2007年12月14日编辑:


显然,访问
函数
对象的
名称
属性并不能真正用于生产(您将得到一个无组件工厂发现错误),因为在对代码进行了丑化之后,函数的名称会被破坏,并且不匹配。在解释解决此问题的一些解决方法时,出现了一个问题注释。

这是针对模态的子组件的,对吗?@OsmanCea是的,你是对的。我以前也做过类似的事情,但在尝试发布答案之前,我想知道一些事情。你感觉如何你在设计你的模态?你想在整个应用程序中有一个模态组件的实例吗?或者你想跨越多个实例,然后动态创建相应模态的内容?@OsmanCea我的应用程序中有一个模态组件的实例。好的,谢谢,我只需要知道这些。我会发布到在接下来的一两个小时内给我一个答案。