i wanted to create common list component like below
<ul>
<li *ngFor="let item of items">{{item.text}}</li>
</ul>
with interface
export interface Items{
id:string
text:string
}
and in component.ts
@input items:any
now i wanted to reuse above component with any data source.
for example,
<app-list-view [items]="ProductList"></app-list-view>
& products contains.
this.ProductList =[PRODUCTSITEMS...]
export interface Products{
produceId
productText
}
may be <app-list-view [items]="OrderList"></app-list-view>
this.OrderList=[ORDERITEMS...]
export interface Orders{
orderId
orderText
}
how can we use typescript generics or something like that here?, i do not want to use map function to create 2 more extra property on ProductList and OrderList.
what is best way to achieve above functionality.
Nice question; it's good that you want to make this component reusable for different types of lists.
Unfortunately, a generic won't help you with how your code works, it would only help you keep track of types while writing code. However, what you want to do can be implemented with an additional input.
Instead of writing {{item.text}}
in the template and setting .text
in stone, what if the name of this key is also an input? Let's call it key
.
Then the template would look like this:
{{item[key]}}
Now we just need to make key
an input in the class. We can give it a default value text
so that, if you don't give the name of the key, it falls back to item.text
.
@Input() key: string = 'text'
Now you can use your component like this:
<app-list-view
[items]="productList"
[key]="'productText'"
></app-list-view>
<app-list-view
[items]="ordersList"
[key]="'orderText'"
></app-list-view>
In both cases, the correct thing will be chosen: in the first case, it will be the same as writing {{item.productText}}
and in the second it would be like saying {{item.orderText}}
.
If you want to be able to reuse your component with any datasource you will simply need to define a display template for your component. As a base example your component definition could look like :
For product :
<app-list-view [items]="ProductList">
<ng-template #option let-data="data">{{data?.productText + ' - ' + data?.productId}}</ng-template>
</app-list-view>
For order :
<app-list-view [items]="OrderList">
<ng-template #option let-data="data">{{data?.orderText}}</ng-template>
</app-list-view>
How do I use the template ?
Inside your component you will need to get the template by using the decorator @ContentChild. Here we select the template thanks to the #option
@ContentChild("option") optionTemplate: TemplateRef<any>;
Then in your component html you can now use this template :
<ul>
<li *ngFor="let item of items">
<ng-template [ngTemplateOutlet]="optionTemplate" [ngTemplateOutletContext]="{$implicit : item}"></ng-template>
</li>
</ul>
Bonus : to make it more clear to use for the user you can create a (directive + component) to manage your template and make the component use look like this :
<app-list-view [items]="OrderList">
<app-list-option *optionDef="let-data">{{data?.orderText}}</app-list-option>
</app-list-view>
The app-list-option component :
@Component({
selector: 'app-list-option',
template: '<ng-content></ng-content>'
})
export class AppListOption {
constructor() {
}
}
The *optionDef structural directive who will capture the template :
@Directive({
selector: '[optionDef]',
})
export class AppListOptionDef {
constructor(public template: TemplateRef<any>) {
}
}
To finish you just need to change the selector in your component :
@ContentChild(AppListOptionDef ) optionDef: AppListOptionDef;
And use optionDef.template in the HTML :
<ng-template [ngTemplateOutlet]="optionDef.template" [ngTemplateOutletContext]="{$implicit : item }"></ng-template>
For more information
You will find here some of my code working on the way I described
Reusable "select" code : https://github.com/xrobert35/asi-ngtools/blob/master/src/components/asi-select/asi-select.component.ts
The bonus part : https://github.com/xrobert35/asi-ngtools/blob/master/src/components/common/asi-component-template.ts
Working exemple : https://ng-tools.asi.fr/views/showroom/asi-ngtools/components/asi-select
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