I am working on a generic component where the list view can be utilized by other components. But the issue is data format is different for each component. In my project, I can't use type any[] which will cause linting issues that I can't skip also.
list-view.component.html(shared component)
<div *ngFor="let list of lists">
<ng-template #defaultTemplate>
<p> {{list}}</p>
</ng-template>
<ng-container
[ngTemplateOutlet]="optionTemplate || defaultTemplate"
[ngTemplateOutletContext]="{ $implicit: list}"
>
</ng-container>
</div>
list-view.component.ts
import {Component,ContentChild,EventEmitter,Input,Output,TemplateRef} from '@angular/core';
export interface listData{
id: number;
brand: string;
model: string;
url: string;
}
@Component({
selector: 'app-my-selector',
templateUrl: './my-selector.component.html',
})
export class MySelectorComponent {
@Input() lists: listData; **// can't use any[], because of linting issue.**
@ContentChild('optionTemplate', { static: false }) optionTemplate: TemplateRef;
constructor() {}
}
test1.component.html
<div >
<app-my-selector [lists]="list">
<ng-template #optionTemplate let-list>
<img src="{{list.url}}" alt="{{list.model}}">
<p>{{list.brand}}: {{list.model}}</p>
</ng-template>
</app-my-selector>
</div>
test1.component.ts
import { Component } from '@angular/core';
export interface listData{
id: number;
brand: string;
model: string;
url: string;
}
@Component({
selector: 'app-test1',
templateUrl: './test1.component.html',
})
export class Test1Component {
list:listData[];
constructor() {}
ngOnInit() {
this.list = [
{
id: 1,
brand: 'TATA',
model: 'Indica - 2008',
url: '/indica-img.jpg'
},
{
id: 2,
brand: 'Suzuki',
model: 'Swift - 2011',
url: '/swift-img.jpg'
}
];
}
}
test2.component.html
<div >
<app-my-selector [lists]="list"></app-my-selector>
</div>
test2.component.ts
import { Component, OnInit } from "@angular/core";
@Component({
selector: "app-test2",
templateUrl: "./test2.component.html",
})
export class Test2Component {
list: string[]; **// this is where causing the issue.**
constructor() {}
ngOnInit() {
this.list = ['Harley Davidson','Bajaj','Triumph'];
}
}
If I run the above code I am getting Type 'string[]' is not assignable to type 'listData[]' in test2.component.html. Because the test1 component is an array of object data & the test2 component is an array of data. So without using any[] how can I achieve this?
CodePudding user response:
As suggested in one of the comments, try using a generic type.
Try something like this:
@Component({
selector: 'app-my-selector',
templateUrl: './my-selector.component.html',
})
export class MySelectorComponent<T> {
@Input() lists: T[]; // can't use any[], because of linting issue.
@ContentChild('optionTemplate', { static: false })
optionTemplate: TemplateRef<T>;
constructor() {}
}
PS: You may find this article helpful for future reference on creating reusable components.
CodePudding user response:
I was not able to reproduce this issue but I have identified that there were many improvements I could suggest...
import { CommonModule } from '@angular/common';
import { Component, ContentChild, Input, NgModule, TemplateRef } from '@angular/core';
// capitalize ListData instead of listData
export interface ListData {
id: number;
brand: string;
model: string;
url: string;
}
@Component({
selector: 'test-parent',
// put the container class directly to the host component (give it display: block)
host: { class: 'container' },
template: `
<!-- remove unnecessary div node to add a class -->
<test-child [lists]="list">
<ng-template #optionTemplate let-list>
<!-- no need to use interpolation {{ }} to pass attributes -->
<img [src]="list.url" [alt]="list.model" />
<p>{{ list.brand }}: {{ list.model }}</p>
</ng-template>
</test-child>
`,
})
export class TestComponentParent {
list: ListData[] = [
{
id: 1,
brand: 'TATA',
model: 'Indica - 2008',
url: '/indica-img.jpg',
},
{
id: 2,
brand: 'Suzuki',
model: 'Swift - 2011',
url: '/swift-img.jpg',
},
];
// ngOnInit() {}; no need to wait fot this lyfecycle to initialize a component property
}
@Component({
selector: 'test-child',
template: `
<!-- you can use structural directive and attribute directive in the same container, no need to create a div node -->
<ng-container
*ngFor="let list of lists"
[ngTemplateOutlet]="optionTemplate || defaultTemplate"
[ngTemplateOutletContext]="{ $implicit: list }"
>
</ng-container>
<!-- remove this template from the loop no need to redefine the template for each iteration -->
<ng-template #defaultTemplate let-list>
<p>{{ list }}</p>
</ng-template>
`,
})
export class TestComponentChild {
// lists should be an array: ListData[], not an instance of ListData
@Input() lists: ListData[];
// most angular versions have the static property as false by default, consider removing if useless
@ContentChild('optionTemplate', { static: false }) optionTemplate: TemplateRef<any>;
}
@NgModule({
declarations: [TestComponentParent, TestComponentChild],
imports: [CommonModule],
})
export class FeatureModule {}