Search code examples
angularperformancetypescriptperformance-testing

Angular: preconstructed object vs template getters


I've long been wondering whether to use a template full of getters, or just preconstruct the entire object with a resolver, and I can't really seem to find a clear answer anywhere.

I've created two simplified scenarios below.

Currently, I'm always using scenario 1, but as soon as the app/program becomes more complex, I notice (huge) performance drops, therefore I'm thinking about using scenario 2 from now on.


Scenario 1: Getter functions

  • Pro: You only get/use whatever you need in your current template. Adding new getters does not require you to update your interfaces.
  • Con: Impact on performance when having hundreds/thousands of getters in a single template (I think).

Code:

var devices = ['desktop', 'tablet', 'mobile'];

var icons = {
    desktop: 'icon-desktop',
    tablet: 'icon-tablet',
    mobile: 'icon-mobile'
}

var translations = {
    desktop: 'Desktop',
    tablet: 'Tablet',
    mobile: 'Mobile'
}

getIcon(item: string): string {
    return icons(type);
}

getTranslation(item: string): string {
    return translations(type);
}

Template:

<div *ngFor="device in devices">
    <i [class]="getIcon(device)"></i>
    <span>{{ getTranslation(device) }}</span>
</div>

Scenario 2: Preconstruct the object

  • Pro: With resolve, it's easy to preconstruct the entire object, thus skipping all the getters in my template.
  • Pro: The object never changes, therefore Angular does not have to 'watch' it.
  • Con: The object can become huge, and a lot of properties may never be used.

Code:

var devices = [
    {
        item: 'desktop',
        translation: 'Desktop',
        icon: 'icon-desktop'
    },
    {
        item: 'tablet',
        translation: 'Tablet',
        icon: 'icon-tablet'
    },
    {
        item: 'mobile',
        translation: 'Mobile',
        icon: 'icon-mobile'
    }
]

Template:

<div *ngFor="device in devices">
    <i [class]="device.icon"></i>
    <span>{{ device.translation }}</span>
</div>

Can anyone tell me the pros and cons of each scenario, and why? What about performance (my main question)? Personal preference or experience?


Solution

  • getTranslation(device) will be called on each change detection cycle. This provides performance overhead but it is insignificant, as long as getTranslation itself isn't costly.

    A more conventional way is to use a pipe like {{ device | translation }}. This can be pure pipe which is more efficient. But if the language is supposed to be changed dynamically, the pipe should be impure, because the expression evaluates to new value even if device value is unchanged.