如何将Bootstrap模态框动态创建为Angular2组件?

15

原标题:无法在Angular 2中初始化动态添加的(HTML)组件

我创建了一个指令,在初始化时将模态框附加到主体上。当单击一个按钮(其中注入了该指令)时,此模态框启动。但是我希望此模态框的内容是另一个组件(实际上我希望模态框本身就是组件)。似乎我无法初始化该组件。

这是我所做的 plunker:

http://plnkr.co/edit/vEFCnVjGvMiJqb2Meprr?p=preview

我试图将my-comp作为我的组件的模板

 '<div class="modal-body" #theBody>' 
            + '<my-comp></my-comp>' + 
 '</div>
1个回答

12

2.0.0正式版更新

Plunker示例>= 2.0.0

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, ModalComponent, CompComponent],
  providers: [SharedService],
  entryComponents: [CompComponent],
  bootstrap: [ App, ModalComponent ]
})
export class AppModule{}
export class ModalComponent {
  @ViewChild('theBody', {read: ViewContainerRef}) theBody;

  cmp:ComponentRef;

  constructor(
    sharedService:SharedService, 
    private componentFactoryResolver: ComponentFactoryResolver, 
    injector: Injector) {

    sharedService.showModal.subscribe(type => {
      if(this.cmp) {
        this.cmp.destroy();
      }
      let factory = this.componentFactoryResolver.resolveComponentFactory(type);
      this.cmpRef = this.theBody.createComponent(factory)
      $('#theModal').modal('show');
    });
  }

  close() {
    if(this.cmp) {
      this.cmp.destroy();
    }
    this.cmp = null;
  }
}

提示

如果一个应用程序更改了SharedService中的状态或调用导致Observable发出值并且订阅者位于不同的应用程序中,则订阅者中的代码将在发射器的NgZone中执行。

因此,在SharedService中订阅可观察对象时,请使用

class MyComponent {
  constructor(private zone:NgZone, private sharedService:SharedService) {
    private sharedService.subscribe(data => this.zone.run() => {
      // event handler code here
    });
  }
}

如何手动触发Angular2变更检测的详细信息请参见Triggering Angular2 change detection manually

翻译后

由代码动态添加的HTML不会被Angular处理,也不会实例化或添加组件或指令。

使用DynamicComponentLoader(已作废) ViewContainerRef.createComponent() (Angular 2 dynamic tabs with user-click chosen components)无法在Angular的根组件(AppComponent)之外添加组件。

我认为最好的方法是在Angular的根组件之外创建第二个组件,然后对它们分别调用bootstrap()并使用共享服务进行通信:

var sharedService = new SharedService();

bootstrap(AppComponent, [provide(SharedService, {useValue: sharedService})]);
bootstrap(ModalComponent, [provide(SharedService, {useValue: sharedService})]);

Plunker示例 beta.17
Plunker示例 beta.14

@Injectable()
export class SharedService {
  showModal:Subject = new Subject();
}

@Component({
  selector: 'comp-comp',
  template: `MyComponent`
})
export class CompComponent { }


@Component({
  selector: 'modal-comp',
  template: `
  <div class="modal fade" id="theModal" tabindex="-1" role="dialog" aria-labelledby="theModalLabel">
    <div class="modal-dialog largeWidth" role="document">
      <div class="modal-content">
        <div class="modal-header">
        <h4 class="modal-title" id="theModalLabel">The Label</h4></div>
        <div class="modal-body" #theBody>
      </div>
    <div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal" (close)="close()">Close</button>
  </div></div></div></div>
`
})
export class ModalComponent {
  cmp:ComponentRef;
  constructor(sharedService:SharedService, dcl: DynamicComponentLoader, injector: Injector, elementRef: ElementRef) {
    sharedService.showModal.subscribe(type => {
      if(this.cmp) {
        this.cmp.dispose();
      }
      dcl.loadIntoLocation(type, elementRef, 'theBody')
      .then(cmp => {
        this.cmp = cmp;
        $('#theModal').modal('show');
      });
    });
  }

  close() {
    if(this.cmp) {
      this.cmp.dispose();
    }
    this.cmp = null;
  }
}


@Component({
  selector: 'my-app',
  template: `
<h1>My First Attribute Directive</h1>
<button (click)="showDialog()">show modal</button>
<br>
<br>`,
})
export class AppComponent {
  constructor(private sharedService:SharedService) {}

  showDialog() {
    this.sharedService.showModal.next(CompComponent);
  }
}

1
如果您想在第一个实例中调用另一个模态框,会出现问题。它只会替换内容,而不是创建另一个模态框实例。示例在这里:https://plnkr.co/edit/5sjn25CV6QLbLIs2FcWp?p=preview - Maxime Lafarie
1
哎呀,我已经很久没有看过这些组件了。 - Günter Zöchbauer
1
是的,我知道,但我认为它可能非常有用,因为我陷入了类似的情况:动态创建模态框,并在其他模态框实例中调用模态框实例。 - Maxime Lafarie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接