0

Our team is developing a pretty complex form engine that has many important nodes such as form, container, field and etc. Needless to say, class calculation for these nodes is pretty complex - there can be 10+ conditional classes with very complex conditions. As Angular offers us many ways to implement component logic in .ts instead of .html we used function way to do it, like this:

[ngClass]="calculateSomeDamnComplexConditionsAndReturnAHugeNGClassObject()"

But as I google the topic I see many "function calls in template is bad practice" comments like here or here. Ok, function calls in template is bad practice and can cause performance issues, but it would be pretty insane in 2023 to implement all complex classname calculations right in template. Template is not a place for complex calculations like this:

  <p [ngClass]="{
    'text-success':celeb.country === 'USA',
    'text-secondary':celeb.country === 'Canada',
    'text-danger':celeb.country === 'Puorto Rico',
    'text-info':celeb.country === 'India'
  }">{{ celeb.artist }} ({{ celeb.country }})

This example has only 4 conditional classes, but what if there are many of them and also there are many nodes that need complex conditional classes calculations

So the question is: angular guys, how do you implement complex classname calculations in component class without performance issues in 2023?

lucifer63
  • 784
  • 9
  • 32

2 Answers2

1

Alternatively, you could move the calculation to a custom pure pipe that returns a string containing all the necessary classes.

That way, you can even split your calculations in the pipe's methods.

@Pipe({name: 'complexClassPipe'})
export class ComplexClassPipe implements PipeTransform {
  transform(value: ArrayItem) {
    return this.getClassList(value);
  }

  private getClassList(item: ArrayItem) {
    return [this.getClassForCountry(item.country)/*, call other methods*/].join(' ');
  }

  private getClassForCountry(country: string) {
    switch (country) {
      case 'USA':
        return 'text-success';
      case 'Canada':
        return 'text-secondary';
      case 'Puorto Rico':
        return 'text-danger';
      case 'India':
        return 'text-info';
    }
  }
}

Then use it like this:

<p [ngClass]="celeb | complexClassPipe">{{ celeb.artist }} ({{ celeb.country }})
Askirkela
  • 1,120
  • 10
  • 20
  • Hello! I like the pipe way, but is there a way to abstract calculation logic from a pipe? Creating a unique pipe for EVERY complex classname calculation case seems a bit excessive for me – lucifer63 Aug 17 '23 at 12:34
  • Well, you could compute the classname for each item during the array creation (be it fetched from an API or whatever). Initial computation will be longer and no easy recomputation if you change some item property along the way. – Askirkela Aug 17 '23 at 12:50
  • yes, I saw when it comes to compute dynamic classes people use precomputation a lot, but I thought there are some smart mechanisms that will let me use ngClass and still have calculation logic in component class, instead of template. Thanks! I'll try precomputation way if there's no other way – lucifer63 Aug 21 '23 at 11:25
0

I think then calculate css class string not so bad for performance. But you can use variable when it just component calculateSomeDamnComplexConditionsAndReturnAHugeNGClassObject and assign method that return the value in your component ngOnInit(). When you use loop ngFor you could make additional field like celebs.forEach(item => item.style = this.getStyle(item.country))} and use in template as [ngClass]=celeb.style.

  • I agree I've done a few complex CSS classes and as long as the value is relatively stable there's really no problems with performance. – Nick Felker Aug 17 '23 at 15:19