Original title: Can't initialize dynamically appended (HTML) component in Angular 2
I've created a directive that appends a modal to the body on initialization. When a button (with the directive injected into it) is clicked this modal fires up. But I want the contents of this modal to be another component (In fact I want the modal to be the component). It seems that I can't initialize the component.
Here is a plunker of what I've done:
http://plnkr.co/edit/vEFCnVjGvMiJqb2Meprr?p=preview
I'm trying to make my-comp the template of my component
'<div class="modal-body" #theBody>'
+ '<my-comp></my-comp>' +
'</div>
Load Dynamic Content from Database in Bootstrap ModalBy clicking the Open Modal ( . openBtn ) button, the dynamic content is loaded from another PHP file ( getContent. php ) based on the ID and shows on the modal popup ( #myModal ).
Bootstrap 4 ModalChange the size of the modal by adding the . modal-sm class for small modals, . modal-lg class for large modals, or . modal-xl for extra large modals.
What dynamic components are. Dynamic means, that the components location in the application is not defined at buildtime. That means, that it is not used in any angular template. Instead, the component is instantiated and placed in the application at runtime.
update for 2.0.0 final
Plunker example >= 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;
}
}
Hint
If one application change the state in SharedService
or calls a method that causes an Observable
to emit a value and the subscriber is in a different application then the emitter, the code in the subscriber is executed in the NgZone
of the emitter.
Therefore when subscribing to an observable in SharedService
use
class MyComponent {
constructor(private zone:NgZone, private sharedService:SharedService) {
private sharedService.subscribe(data => this.zone.run() => {
// event handler code here
});
}
}
For more details how to trigger change detection see Triggering Angular2 change detection manually
original
Dynamically added HTML is not processed by Angular and doesn't result in components or directives to be instantiated or added.
You can't add components outside Angulars root component (AppComponent) using (deprecated) DynamicComponentLoader
ViewContainerRef.createComponent()
(Angular 2 dynamic tabs with user-click chosen components).
I guess the best approach is to create a 2nd component outside Angulars root component is to call bootstrap()
on each and use a shared service to communicate:
var sharedService = new SharedService();
bootstrap(AppComponent, [provide(SharedService, {useValue: sharedService})]);
bootstrap(ModalComponent, [provide(SharedService, {useValue: sharedService})]);
Plunker example beta.17
Plunker example 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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With