4

I'm attempting to call a function inside my HTML interpolation, like so. This HTML chunk is inside a *ngFor loop.

<mat-list>
    <mat-list-item *ngFor="let unit of units">
    <div>
        <p>Remaining Life</p>
        <h2>{{ parseTimeRemaining(unit.daysRemaining) }}</h2>
    </div>
    </mat-list-item>
<mat-list>

parseTimeRemaining() returns a string to be displayed, using the value of unit.daysRemaining as a parameter.

public parseTimeRemaining(days: number){
    const timeString = days + " Days";
    return timeString;
}

Currently, my function returns undefined and thus, an empty <h2> element. Did I do something wrong? If so, what's the best way to display the information I need?

2
  • 1
    Note that it is not recommended to bind functions to the template for performance reasons since they are called during every change detection cycle. You should bind variables instead. Commented Oct 10, 2018 at 21:53
  • Your problem is not in the code you provided. angular-y8tngp.stackblitz.io Please check how you are setting the data and what else is going on. Commented Oct 10, 2018 at 22:04

2 Answers 2

5

This could be a great opportunity to use a Pipe instead of a function. This helps to avoid calling a function within an *ngFor which can have detrimental affects to performance. From the documentation surrounding pipes and change detection.

Angular picks a simpler, faster change detection algorithm when you use a pipe.

Angular has a built-in pipe, I18nPluralPipe which "Maps a value to a string that pluralizes the value according to locale rules.". You create an object (pluralMap) targeting specific numerical values and indicate the resulting string should be allowing to say 0 Days, 1 Day, or 99 days.

pluralMap = {
  '=0': '0 Days',
  '=1': '1 Day',
  'other': '# Days'
};

You would use it in a template as follows:

<ul>
  <li *ngFor="let unit of units">{{ unit.daysRemaining | i18nPlural:pluralMap }}</li>
</ul>

Another approach would be to create a Custom Pipe. This would allow you implement custom formatting and/or validation logic against the provided daysRemaining.

Pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'daysRemaining'})
export class DaysRemainingPipe implements PipeTransform {
  transform(days: number): string {
    const timeString = days + " Days";
    return timeString;
  }
}

Module (registration):

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; 
import { AppComponent } from './app.component';
import { DaysRemainingPipe } from './days-remaining.pipe';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, DaysRemainingPipe ]
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Template:

<ul>
  <li *ngFor="let unit of units">{{ unit.daysRemaining | daysRemaining }}</li>
</ul>

Taking it one step further, we could extend the I18nPluralPipe to pluralize the results within our custom pipe by extending the built in pipe and maybe check if the input is a valid number.

import { Pipe, PipeTransform } from '@angular/core';
import { I18nPluralPipe } from '@angular/common';

@Pipe({name: 'daysRemaining'})
export class DaysRemainingPipe extends I18nPluralPipe implements PipeTransform {
  transform(days: number): string {
    const safeDays = isNaN(days) ? 0 : days;

    const pluralMap = {
      '=0': '0 Days',
      '=1': '1 Day',
      'other': '# Days'
    };

    return super.transform(safeDays, pluralMap);
  }
}

Here is an example in action.

Hopefully that helps!

Sign up to request clarification or add additional context in comments.

1 Comment

This is a much more effective solution than what I came up with. It works really well. Thanks for the detailed explanation!
1

It seems that your function is not returning anything. Check that the function is being called (put a console.log to see if actually is being executed).

However, maybe it would be better something like this.

<h2>{{ unit.daysRemaining + ' Days' }}</h2>

If that works, maybe the problem was a typo, or some scope visibility that you are missing there (is that template the template of the component?)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.