Reputation: 693
I have a 3rd party library component that I am trying to customize by adding some additional markup at a certain point. I am using a structural directive to do this and currently I am programmatically adding and styling nodes using the renderer like so:
const headerDiv = this.newDiv();
el = this.nativeElement.querySelector('div.ui-dropdown-items-wrapper');
this.renderer.appendChild(el, this.renderer.createText('sometext'));
private newDiv(): Element {
return this.renderer.createElement('div');
}
The markup is like so:
<div #containerDiv>
<child-component *myStructuralDirective>
</child-component>
</div>
is there a way to define an <ng-template>
directly in the parent component's markup and inject that at a certain point using the renderer? Something like this:
<div #containerDiv>
<child-component *myStructuralDirective>
</child-component>
</div>
<ng-template #footer>
<p>Some other markup</p>
</ng-template>
and then inject the content of #footer at some point of my choosing into the child-component. Note - I have no access to the child since it's a compiled 3rd party lib.
The gist of this is that I am trying to see if there is a better way to define the markup as a template variable that I can use in my structural directive, get access to that template and inject it at a certain point in the child component.
Edit -
I am looking at ViewContainerRef.insert
, unfortunately this can insert templates only based on location of other ViewRef
s. Since I cannot change the markup of the 3rd party component I can define a ng-container
to mark the point of insertion and have to only use CSS selectors. Not sure if there is a way to insert a template based on a element location. I know these are 2 discrete concepts (Angular View abstractions vs direct DOM insertion) so i'm not optimistic this can be done!
Upvotes: 6
Views: 8334
Reputation: 8269
Yes, you can inject ng-template to wherever you want it to be placed either with these following methods:
1.) Using ng-container
<div #containerDiv>
<child-component *myStructuralDirective></child-component>
// This will show your template
<ng-container [ngTemplateOutlet]="footer"></ng-container>
</div>
<ng-template #footer>
<p>Some other markup</p>
</ng-template>
2.) Or Inject it using a structural directive with a ViewContainerRef & CreateEmbeddedView
a.)Supply the footer template on your childComponent. myStructuralDirective is now both a directive and an [] input that asks for a value. <child-component [myStructuralDirective]="footer"></child-component> b.) Use default ViewContainerRef declared on the constructor which pertains to your current view/screen which the template the template is attached example for this - it is attached on ChildComponentView @Directive({ selector: 'myStructuralDirective' }) export class MyStructuralDirective implements AfterViewInit { @Input() myStructuralDirective: TemplateRef<any>; constructor(private container: ViewContainerRef) {} ngAfterViewInit() { this.container.createEmbeddedView(this.myStructuralDirective); }
}
3.) Or Supply an @Input() on your ChildComponent passing the footer template value
a.) <child-component *myStructuralDirective [footerTemplate]="footer"></child-component> b.) On the ChildComponent Class, declare its @Input() property @Component({ selector: 'child-component' }) export class ChildComponent { @Input() footerTemplate: TemplateRef<any>; } c.) On your ChildComponent Template, supply an ng-container to wherever you want it to be placed. <h1>Header</h1> <h1>Content</h1> <ng-container [ngTemplateOutlet]="footerTemplate"></ng-container>
4.) Or you can supply a container on your ChildComponent
a.) ChildComponent Sample Template
<p>Header</p> <p>Content</p> <div #container></div>
b.) Declare ViewContainerRef and TemplateRef on your ChildComponent Class with the @Input() that requires the footer template value from the component
@Component({...}) export class ChildComponent implements AfterViewInit { //From your template <div #container></div> @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef; @Input() footerTemplate: TemplateRef<any>; ngAfterViewInit() { this.container.createEmbeddedView(this.footerTemplate); }
}
Your ng-template will now appear on your desired location.
Upvotes: 9