I am facing the problem, that I need to pass multiple ng-template into another component as a template, example:
<app-datatable>
<ng-template tdDataTableTemplate="description" let-value="value" let-row="row" let-column="column">
<p matTooltip="{{ row[column ]}}">{{ row[column] }}</p>
</ng-template>
<ng-template tdDataTableTemplate="vendorName" let-value="value">
<p matTooltip="{{ value }}">{{ value }}</p>
</ng-template>
</app-datatable>
app-datatable is my component and I need to pass these templates to it as a template.
td-data-table is Teradata covalent DataTable component. It has functionality, that I can template each column with ng-template. But I want to pass ng-template through my component into td-data-table - hope it makes sence
Solution:
I didn't manage to find Covalent way solution. But I've managed to rewrite Teradata grid to custom grid, which allows me more control over it. Here is Stackblitz: Demo
I encountered the same problem and managed to fix it with the following:
In datatable.component.ts:
#Component({
selector: 'app-data-table',
templateUrl: './data-table.component.html',
styleUrls: ['./data-table.component.scss']
})
export class DataTableComponent implements OnChanges, AfterContentInit {
ngAfterContentInit(): void {
for (let template of this.templates.toArray()) {
if (!template.tdDataTableTemplate) {
continue
}
this.tdDataTable._templateMap.set(template.tdDataTableTemplate, template.templateRef)
}
}
#ViewChild(TdDataTableComponent) tdDataTable;
#ContentChildren(TdDataTableTemplateDirective) templates;
}
This mimics how TdDataTableCompponent handles the templates under the hood.
Also, for completeness, I spoke to the OP on the covalent gitter and they said that they solved it by rewriting the data table from the individual covalent components. They shared a stackblitz here.
Related
In a context of a micro-front-end application I have a projected content in the app.module.ts that I want to put in a component that was instantiated via the router.
Here is a solution that does not work:
I referenced the projected content as follow
<ng-template #projected>
<ng-content></ng-content>
</ng-template>
<router-outlet></router-outlet>
I set the reference in a service
#ViewChild('projected', { static: true }) templateRef: TemplateRef<unknown>;
// ...
this.service.projectedRef = templateRef;
use it in the component that was created by the router
<ng-container [ngTemplateOutlet]="service.projectedRef"></ng-container>
Any idea why doesn't that work and how I can solve this problem?
Note: when I use <ng-container [ngTemplateOutlet]="service.projectedRef"></ng-container> inside the app.component.html, it works fine, but not inside the routed component.
I am using primeng TurboTable where for templates a pTemplate directive is added. And then accordingly DOM gets rendered I am trying to implement a very same approach in my project to create a reusable(DUMB) component. Tried searching for a solution but couldn't find a proper solution. Thought about using ng-container but when passing ng-template from Smart component to child component nothing is happening. PFB a sample of the solution I tried
Smart Component Template
<dumb-component>
<ng-template #content> Content is placed here .... </ng-template>
</dumb-component>
Dumb Component Template
<ng-container *ngTemplateOutlet="content">
</ng-container>
Link to primeng documentation : primeng docs
Since your component Dumb Component receives a template. It needs to access the template with #ContentChild
Dump Component
<ng-container *ngTemplateOutlet="content">
</ng-container>
#ContentChild(TemplateRef) content: TemplateRef<any>;
Example:https://stackblitz.com/edit/angular-ephusu
I have been trying to find the solution of this problem from two days. Unfortunately, I can not get what I want. I am using Angular5.
<div class="form-group col-md-12" [innerHTML]="GetItemsOfHolder(item.children[1],1,
'UserConfigGroupsLabelHolder') | keepHtml"></div>
This is what my function looks like:
GetItemsOfHolder(item: any,divName:string, recursive: boolean = false,typeName:string="")
{
return html;
}
Everything works fine, unless The html which I am returning contains one package named Select2
This is what I use to add the html into this div it works very fine. Until I wanted to add the dynamic package.
What I mean is return html contains the package component like this:
itemhtml +="<select2 data-type='"+holderItem.itemType+"'
[data]='this.dropdownData."+holderItem.name+"'></select2>"
This just returns the plain text to the browser and doesn't work as expected.
What I want is the string to be turned into component or any other way which works and generates the select2 dropdown.
I have been trying to search so many things.But it doesn't works
This is good but I can not understand this And dynamiccomponentloading is deprecated.
Can anyone please give me an idea How can I resolve this problem? Any example would be a great.
As commented by #Devcon
Angular will sanitize pretty much everything so that is why you are
getting plain text. What you want to look into is ReflectiveInjector
and mainly ComponentFactoryResolver. The main idea is that components
need some other info(services, other components, etc) to be rendered,
so you use the Injector to get Dependency Injection refs then the
Component factory builds your component. You then insert this to a
ViewChild reference. There is a more complicated way of dynamically
making components that uses the compiler and requires a
ModuleWithComponentFactories, this is what angular actually uses.
And searching on the angular, I accept that angular should not be done this way.
As I have to create the fully dynamic page which must be rendered in html. I changed my json little bit and using the
ng-container and ng-template and using ngswitch
I made recursive call in the template it self and found its working very fine.
I get many advantages using this:
The HTML (I render dynamically) itself is in HTML, Code is clean and readable, easily maitainable.
The example given here is pretty much the same I have done.
https://stackoverflow.com/a/40530244/2630817
A small example is here:
<ng-template #itemsList let-itemsList>
<div *ngFor="let item of itemsList;let i = index">
<div [ngSwitch]="item.itemType">
<div class="form-group" *ngSwitchCase="'TEXT'">
<label>
{{item.label}}
</label>
<input id="{{item.name}}" value="{{item.value}}" type='text' class='form-control txtbox ui-autocomplete-input'/>
</div>
<div class="form-group" *ngSwitchCase="'PASSWORD'">
<label>
{{item.label}}
</label>
<input id="{{item.name}}" value="{{item.value}}" type='password' class='form-control txtbox ui-autocomplete-input'/>
</div>
<div class="form-group" *ngSwitchCase="'BOOLEAN'">
<label style='width:40%'>{{item.label}}</label>
<div class="form-group"><input id="{{item.name}}" type='checkbox' /></div>
</div>
<div class="form-group" *ngSwitchCase="'LABEL'">
<label class="form-control">{{item.label}}</label>
</div>
<div class="form-group" *ngSwitchDefault>
<label>
{{item.label}}
</label>
<select2 class="form-control" [data]="GetDropDowndata(item.holderId)" [cssImport]="false" [width]="300" [options]="GetOptions(item.type)"></select2>
</div>
</div>
</div>
You can load every you want in one div, you have to play with ng-template and ng-content.
First you have to create one directive:
import {Directive, ViewContainerRef} from '#angular/core';
#Directive({
selector: '[dynamic]',
exportAs: 'dynamicdirective'
})
export class DynamicDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
After you have to put it in some ng-template like:
<p>
page works!
</p>
<ng-template #sider=dynamicdirective dynamic></ng-template>
and use it like
import {Component, ComponentFactoryResolver, OnInit, ViewChild} from '#angular/core';
#Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.css']
})
export class PageComponent implements OnInit {
#ViewChild('sider')
sider;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(SomeComponent);
this.sider.viewContainerRef.createComponent(componentFactory);
});
}
}
and normally will see you component loaded at the place of you ng-template (you can call https://angular.io/api/core/ViewContainerRef#clear if you want to reset your view)
I already play with this, you can find some code here https://github.com/nicearma/dynamic
I thought to leave this here for anyone who encounters the same issue in the future.
If you don't want to bother manually instantiating components with ComponentFactory as the other answers suggest, you can also use a library I wrote for the explicit purpose of loading components into dynamic content: ngx-dynamic-hooks.
You can give it any string that contains the selector of a desired component and it will automatically be loaded in its place. You can even load components by other text patterns other than just their selectors! See it in action in this Stackblitz.
There's a lot more bells and whistles, if you need them. In the link above, you'll find a fairly detailed documentation that should set you up easily.
I want to render incoming text from an API as subsequent HTML and component template.
Most of the solutions I found here use #ViewChild to inject the components but that doesn't work for me since I need to iterate the same behavior for all items in the *ngFor loop.
This is how the code would look like:
The template of the component rendering the incoming messages:
<div *ngFor="let item of messages">
<compile-component [template]="item.html"></compile-component>
</div>
Incoming message structure (item.html):
<my-component></my-component><div>Some html</div>
Component to compile:
#Component({
selector: 'my-component',
template: '<div>It works</div>',
styleUrls: ['./my-component.component.css']
})
export class MyComponent{ }
Output would look like this:
<div>It works</div><div>Some html</div>
I am looking for the solution for compile-component here.
Any help is much appreciated. Thanks in advance.
You should be able to do that via ComponentFactoryResolver. See angular docs about this here:
https://angular.io/guide/dynamic-component-loader
I'm using Angular 2 with Ngrx and Angular Material.
Are all UI changes like dropdown or dialog supposed to be in store?
For example:
<button md-button [mdMenuTriggerFor]="menu">Menu</button>
<md-menu #menu="mdMenu">
<button md-menu-item>Item 1</button>
<button md-menu-item>Item 2</button>
</md-menu>
Should I support dropdown in the store?
Next example:
<md-sidenav-container class="example-container">
<md-sidenav #sidenav class="example-sidenav">
Jolly good!
</md-sidenav>
<div class="example-sidenav-content">
<button type="button" md-button (click)="sidenav.open()">
Open sidenav
</button>
</div>
</md-sidenav-container>
Can I use open method? Maybe I should create component that wrap sidenav in component that operate only on input Input() open;?
For me, there are generally 3 questions I ask.
Are the UI changes isolated to the component and do not affect other components?
Do the UI changes need to be persisted when the view returned to after have been destroyed?
For debugging purposes do you want to track this state (possibly in order to recreate the view of the end user?)
If you answered NO to these 3 questions then you probably do NOT need to put the state of that component in the store.
By not coupling something the store, you make it more modular, easier to test and reusable.
You should use the store to keep state and the UI should be bound to that state. You can then keep Observable to that state hierarchy or subscribe to those state changes and update the UI accordingly and dynamically.
This is what I had in mind:
import { Component } from "#angular/core";
import { Store } from "#ngrx/store";
// Other relevant imports ....
#Component({
selector: 'some-comp',
templateUrl: 'some-comp.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SomeComponent implements OnDestroy {
employeeObs$:Observable<EmployeeInfo>;
constructor( private store: Store<IAppState>) {
this.employeeObs$ = this.store.select('employee')
.map((next: IEmployeeState) => next.info);
}
// ...
}
And then on html template something like:
<!-- Some other regular DOM
...
...
-->
<!-- Conditional DOM based on state -->
<div *ngIf="employeeObs$ | async as employeeInfo">
<div type="text">{{employeeInfo.name}}</div>
</div>
<!-- Some other regular DOM
...
...
-->
The conditional tag will only show if Observable has data...