Reputation: 1636
I'm making an Angular component that will be used to display a file icon depending on an input string. The input string will be checked against an array of some possible values for each file type.
For example,
And so on, for each of the file types our app expects.
I was planning on doing a simple list of <i>
elements with appropriate classes depending on which array the input string is in.
export class FileIconComponent {
@Input() fileType: string;
private readonly FILE_WRD: string[] = ["word", "doc", "docx"];
private readonly FILE_PDF: string[] = ["pdf", "application/pdf"];
private readonly FILE_EXL: string[] = ["excel", "xls", "xlsx"];
constructor() {}
}
<i *ngIf="FILE_EXL.includes(fileType?.toLowerCase())" class="icon document-excel-o"></i>
<i *ngIf="FILE_WRD.includes(fileType?.toLowerCase())" class="icon document-word-o"></i>
<i *ngIf="FILE_PPT.includes(fileType?.toLowerCase())" class="icon document-powerpoint-o"></i>
However, since change detection runs many times per second, and I'll be displaying probably a hundred lines of this per page, I'm concerned about performance when I'm putting a function call in my template. Would making one <i>
and use ngClass to determine the class be more performant?
Is there a more efficient way of doing this? Or am I over-concerned about performance in this case?
Upvotes: 0
Views: 746
Reputation: 29335
use a setter input, so you run the functions when needed....
export class FileIconComponent {
private _fileType: string;
@Input() set fileType(fileType: string) {
this._fileType = fileType;
this.isWord = this.FILE_WRD.includes(fileType.toLowerCase());
this.isExcel = this.FILE_EXL.includes(fileType.toLowerCase());
this.isPdf = this.FILE_PDF.includes(fileType.toLowerCase());
};
get fileType() { return this._fileType; }
isWord = false;
isExcel = false;
isPdf = false;
private readonly FILE_WRD: string[] = ["word", "doc", "docx"];
private readonly FILE_PDF: string[] = ["pdf", "application/pdf"];
private readonly FILE_EXL: string[] = ["excel", "xls", "xlsx"];
constructor() {}
}
then simple and clean usage in template:
<i *ngIf="isExcel" class="icon document-excel-o"></i>
<i *ngIf="isWord" class="icon document-word-o"></i>
<i *ngIf="isPdf" class="icon document-powerpoint-o"></i>
setters only run when the input changes, so only as needed.
Upvotes: 2
Reputation: 6811
Endeed, function in the HTML is not recommended with the angular lifecycle.
In your case, you can't use a getter function because the result will depend of the value of the index.
For performance, you can:
OnPush
strategy for your component.trackBy
pipe to not reload everytime all your list.ngOnChanges
where you specify for each item, the wanted class to avoid the use of function.ngOnChanges
, a setter
method for your input.Upvotes: 2
Reputation: 3110
You can add an Enum, and use it in the ngIf
import { Component, Input, OnInit } from "@angular/core";
enum FileTypeEnum {
FILE_WRD,
FILE_PDF,
FILE_EXL
}
@Component({
selector: "hello",
template: `
<i *ngIf="type === fileTypeEnum.FILE_EXL" class="icon document-excel-o"></i>
<i *ngIf="type === fileTypeEnum.FILE_WRD" class="icon document-word-o"></i>
<i *ngIf="type === fileTypeEnum.FILE_PDF" class="icon document-pdf-o"></i>
`,
styles: [
`
h1 {
font-family: Lato;
}
`
]
})
export class HelloComponent implements OnInit {
@Input() name: string;
type: FileTypeEnum;
fileTypeEnum = FileTypeEnum;
private readonly fileTypes = {
FILE_WRD: ["word", "doc", "docx"],
FILE_PDF: ["pdf", "application/pdf"],
FILE_EXL: ["excel", "xls", "xlsx"]
};
ngOnInit() {
const fileType = Object.keys(this.fileTypes).find(key => this.fileTypes[key].includes(this.name));
this.type = FileTypeEnum[fileType];
}
}
But if this logic is only to know which icon to show, then you can use ngClass instead with only one i
tag:
import { Component, Input, OnInit } from "@angular/core";
@Component({
selector: "hello",
template: `
<i [ngClass]="className"></i>
`,
styles: [
`
h1 {
font-family: Lato;
}
`
]
})
export class HelloComponent implements OnInit {
@Input() name: string;
className;
private readonly fileTypes = {
word: ["word", "doc", "docx"],
pdf: ["pdf", "application/pdf"],
excel: ["excel", "xls", "xlsx"]
};
ngOnInit() {
const fileType = Object.keys(this.fileTypes).find(key =>
this.fileTypes[key].includes(this.name)
);
this.className = `icon document-${fileType}-o`;
}
}
Upvotes: 1