Skip to content

Instantly share code, notes, and snippets.

@caroso1222
Last active August 18, 2023 13:34
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save caroso1222/1c99aee8c9efe873902a9c590ab7b40a to your computer and use it in GitHub Desktop.
Save caroso1222/1c99aee8c9efe873902a9c590ab7b40a to your computer and use it in GitHub Desktop.
Service to dynamically append Angular components to the body
import {
Injectable,
Injector,
ComponentFactoryResolver,
EmbeddedViewRef,
ApplicationRef
} from '@angular/core';
@Injectable()
export class DomService {
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private appRef: ApplicationRef,
private injector: Injector
) { }
appendComponentToBody(component: any) {
// 1. Create a component reference from the component
const componentRef = this.componentFactoryResolver
.resolveComponentFactory(component)
.create(this.injector);
// 2. Attach component to the appRef so that it's inside the ng component tree
this.appRef.attachView(componentRef.hostView);
// 3. Get DOM element from component
const domElem = (componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
// 4. Append DOM element to the body
document.body.appendChild(domElem);
// 5. Wait some time and remove it from the component tree and from the DOM
setTimeout(() => {
this.appRef.detachView(componentRef.hostView);
componentRef.destroy();
}, 3000);
}
}
@kaleem7832
Copy link

how to use this service?

@caroso1222
Copy link
Author

This gist is part of this tutorial: https://medium.com/hackernoon/angular-pro-tip-how-to-dynamically-create-components-in-body-ba200cc289e6. You'll see the instructions on how to use this service over there.

@reed-lawrence
Copy link

reed-lawrence commented Oct 25, 2019

Great code! I made some small modifications to allow for initializing component properties and appending to any html element!

https://gist.github.com/reed-lawrence/1f6b7c328ad3886e60dc2b0adcf75a97

@ha-rc
Copy link

ha-rc commented Jun 11, 2020

I did it and it work well,
but that does not include the styleUrls - css files.
how can I include them?

@asmaus
Copy link

asmaus commented Nov 12, 2020

It works perfectly the first time!
Thank you very much for the contribution.

@caoyuanqi
Copy link

I guess you can use this instead

const domElem = componentRef.location.nativeElement;

@Iuriy-Budnikov
Copy link

Angular sucks

@iulius-ciorica
Copy link

iulius-ciorica commented Jul 27, 2022

Verry usefull!

It also works in Angular 14 with small adjustments, I used your ideas after a lot of searching.
If it helps someone:

In constructor:

    viewContainerRef.clear();
    this.daterangepickerRef = this.viewContainerRef.createComponent(NgxDaterangepickerBootstrapComponent, {injector: this.injector});
    this.daterangepickerElement = (this.daterangepickerRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
    document.body.appendChild(this.daterangepickerElement); // add daterangepickerElement to DOM body, to fix position top left issues
    this.daterangepicker = (<NgxDaterangepickerBootstrapComponent>this.daterangepickerRef.instance);
    this.daterangepicker.inline = false; // set inline to false for all directive usage

and in ngOnDestroy:

    const reflectComponent = reflectComponentType(NgxDaterangepickerBootstrapComponent);
    const selector = document.querySelector(reflectComponent!.selector);
    if (selector !== null) document.body.removeChild(selector);
    this.applicationRef.detachView(this.daterangepickerRef.hostView);
    this.daterangepickerRef.destroy();

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment