Dynamically append component to div in Angular 5
Solution 1
Here's the way I got it working
import {
Renderer2,
Directive,
Input,
ElementRef,
OnChanges,
ViewEncapsulation
} from "@angular/core";
import { MatSpinner } from "@angular/material";
@Directive({
selector: "[myDirective]"
})
export class MyDirective {
@Input()
set myDirective(newValue: boolean) {
console.info("myDirectiveBind", newValue);
if (!!this._$matCard) {
const method = newValue ? "removeClass" : "addClass";
this.renderer[method](this._$matCard, "ng-hide");
}
this._myDirective = newValue;
}
private _myDirective: boolean;
private _$matCard;
constructor(private targetEl: ElementRef, private renderer: Renderer2) {
this._$matCard = this.renderer.createElement('mat-card');
const matCardInner = this.renderer.createText('Dynamic card!');
this.renderer.addClass(this._$matCard, "mat-card");
this.renderer.appendChild(this._$matCard, matCardInner);
const container = this.targetEl.nativeElement;
this.renderer.appendChild(container, this._$matCard);
}
}
import {
Component,
ElementRef,
AfterViewInit,
ViewEncapsulation
} from '@angular/core';
@Component({
selector: 'card-overview-example',
templateUrl: 'card-overview-example.html',
styleUrls: ['card-overview-example.css']
})
export class CardOverviewExample {
hideMyDirective = !1;
constructor(private _elementRef: ElementRef) { }
getElementRef() {
return this._elementRef;
}
ngAfterViewInit() {
let element = this._elementRef.nativeElement;
let parent = element.parentNode;
element.parentNode.className += " pippo";
}
}
.ng-hide {
display: none;
}
<mat-card>Simple card</mat-card>
<div class="text-center">
<button (click)="hideMyDirective = !hideMyDirective">
Toggle show dynamic card
</button>
</div>
<br />
<span>hideMyDirective: {{hideMyDirective}}</span>
<hr />
<div class="myDiv" [myDirective]="hideMyDirective">
<ul>
<li>My content</li>
</ul>
</div>
Solution 2
It's pretty simple. I just made an example to you.
Please, read the comments inside loader directive.
https://github.com/garapa/studying/tree/master/loader
EDIT:
You component:
export class LoaderComponent {
loading;
constructor() { }
}
Your directive
export class LoaderDirective implements OnDestroy {
private componentInstance: ComponentRef<LoaderComponent> = null;
@Input()
set appLoader(loading: boolean) {
this.toggleLoader(loading);
}
constructor(
private viewContainerRef: ViewContainerRef,
private componentFactoryResolver: ComponentFactoryResolver
) { }
toggleLoader(loading: boolean) {
if (!this.componentInstance) {
this.createLoaderComponent();
this.makeComponentAChild();
}
this.componentInstance.instance.loading = loading;
}
private createLoaderComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(LoaderComponent);
this.componentInstance = this.viewContainerRef.createComponent(componentFactory);
}
private makeComponentAChild(){
const loaderComponentElement = this.componentInstance.location.nativeElement;
const sibling: HTMLElement = loaderComponentElement.previousSibling;
sibling.insertBefore(loaderComponentElement, sibling.firstChild);
}
ngOnDestroy(): void {
if (this.componentInstance) {
this.componentInstance.destroy();
}
}
}
You module
@NgModule({
...
entryComponents: [
LoaderComponent
]
})
Solution 3
Inside the component html file to which component is to be inserted:
<div #target>
</div>
Inside the component ts file to which component is to be inserted:
'Component_to_insert' -> is the component to be inserted inside another component.
import { Component_to_insert } from 'path';
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, AfterViewInit } from '@angular/core';
@Component({
selector: 'component-name',
templateUrl: 'component.html',
styleUrls: ['component.scss'],
entryComponents: [Component_to_insert]
})
export class ManagetemplatesPanelComponent implements AfterViewInit {
@ViewChild('target', { read: ViewContainerRef }) entry: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
ngAfterViewInit() {
this.createComponent();
}
createComponent() {
this.entry.clear();
const factory = this.resolver.resolveComponentFactory(Component_to_insert);
const componentRef = this.entry.createComponent(factory);
}
}
![Sampgun](https://i.stack.imgur.com/pCtVV.jpg?s=256&g=1)
Sampgun
I'm a Web Developer in Bergamo. I'm developing with Angular6+ and Material and Ionic5+ applications at this time. I deal with frontend architecture and development of web applications. I also like programming with Java, PHP, AngularJS, Bootstrap 3+ and Vue 2+. I enjoy discovering new technologies and create plugins and reusable components. I know how to get the best from CMS like Wordpress, creating themes and plugins to extend its functionalities. I often deal with backend technologies, especially Java and NodeJS (NestJS).
Updated on July 09, 2022Comments
-
Sampgun almost 2 years
I have this
https://angular-dynamic-component-append.stackblitz.io/
I managed to dynamically append an element, but it doesn't get compiled. I saw many tutorials like this
But it's not really what I need. And often they use the hashtag notation to identify the container.
I need to append a component to any element which may have my custom directive on it.
I'd also need to use the bind value of the directive to control a [hidden] attribute on the appended element.
THE GOALS
- Override behaviour of existing component:
- adding an attribute to show/hide
- adding a class to customize appearance
- Reduce html coding
- No need to write the entire component
<my-comp></mycomp>
- No need to know the class
- Automatic behaviour if the class name is changed
- Changing the element on which the directive is applied
- The final goal will be to add a class to the contaner element
- No need to write the entire component
Expected source
<div [myDirective]="myBoolean"> <p>some content</p> </div>
Expected compiled
<div [myDirective]="myBoolean" class="myDirectiveClass1"> <p>some content</p> <someComponent [hidden]="myBoolean" class="myDirectiveClass2"></someComponent> </div>
Is there a way to achieve this?
Thank you in advance
- Override behaviour of existing component:
-
Sampgun over 6 yearsThis is nice, but I can't believe I need to create a "getElement" method on each parent component...
-
Sampgun over 6 yearsI managed to do everything I needed. As soon as I can I'll answer this question. I was inspired by your code. The problem here is that you need to know you are in the "LoaderComponent". I wanted something more "universal". Let me know if you want to be tagged when I answer. Bye and thank you
-
Sampgun over 6 yearsI just upped your answer, since it was useful ;)
-
Dhiraj Dhakal over 5 yearsWhen mat-card is returned, does this also apply the angular material css ? Because when i tried to do something same, the css is not applied. Thanks in advance...
-
Sampgun over 5 yearsI have to make a blitz with this code...honestly I don't remember. I use different approaches now to dinamically append components!
-
Sampgun about 5 yearsThis doesn't quite answer. I already came across this approach. And it works. But what I needed was something universal, which doesn't require to know the component to insert. More or less something working as the $compile worked in AngularJS. It was enough to append the HTML and compile it to make it work..
-
Kanish Mathew about 5 yearsThe approach has been good in Angular 6+ versions. It was an effective method for showing components without including components inside children with angular routing.
-
Sampgun about 5 yearsIt works even on Angular 4 and 5. I'm not saying it's not an effective method. But it doesn't do what I asked.
-
iamsar over 2 yearsThis method is only good for attaching elements to the DOM. It does not compile component. This particular instance may appear to work because the
mat-card
component does little more than add classes; which are being added here manually.