0

I have a DOM structure:

<div class="dropmenu_block_header" *ngIf="getLanguages(group).length">
    <div *ngFor="let language of getLanguages(group); trackBy: trackByFn"></div>
</div>

As you can see in template there are two calls:

*ngIf="getLanguages(group).length"

*ngFor="let language of getLanguages(group)"

How can I optimize this and call method once?

My method is:

getLanguages(group: IGroupLanguage) {
    console.log(group.languages);
    return group.languages.filter((item) => item.Name === 'Russian');
}
C.OG
  • 6,236
  • 3
  • 20
  • 38
POV
  • 11,293
  • 34
  • 107
  • 201

2 Answers2

4

This is totally wrong by design.

You should save your JSON data into some variable which is being returned by method getLanguages(group) and then you have to make condition on the basis of that variable.

Else, the way you are doing will call this method again and again during each change detection which cause performance issues for your application.

Pardeep Jain
  • 84,110
  • 37
  • 165
  • 215
  • Could you share an sammple? I have updated my question – POV Oct 31 '19 at 19:22
  • 1
    Why you are passing params to the method from the template? are you constructing Array dynamically from template selection? – Pardeep Jain Oct 31 '19 at 19:24
  • 1
    @OPV, Pretty self explanatory and is pretty much the standard way of doing it.You create a variable and on `OnInit` you assign that variable to the result sets that is returned by `getLanguagues()`. Then you can use that variable on your template... – penleychan Oct 31 '19 at 19:25
  • Also you can simply omit the `*ngIf` condition as if array is empty, angular's *ngFor will not return any error. – Pardeep Jain Oct 31 '19 at 19:25
  • Because I have complicated object with nested object, like this `[{"group" : {"name":: "A", "languages": [{"id": 1, "name" "en"}]}}]`, so first I need display groups names and inside languages, but languages I need filter before showing, that is why I use this approach, it is clear for you? – POV Oct 31 '19 at 19:26
  • IMO you should reconstruct your data Structure if possible in the controller side and avoid calling functions/methods in the template side. – Pardeep Jain Oct 31 '19 at 19:28
  • How to do that? I can prepare data before passing to ngFor, but I can not filter nested data and return it, because it needs deep copy objects – POV Oct 31 '19 at 19:30
  • Pardeep Jain, could we move to chat? – POV Oct 31 '19 at 19:38
  • 1
    @OPV why don't you do it like -> iterate over the group and then nested language array and then conditionally show hide language according to the group selection? this will save you method binding as well as restructuring of data. – Pardeep Jain Oct 31 '19 at 20:05
  • Because I need to save initial data object to show it if filter returns empty. – POV Oct 31 '19 at 20:27
1

Calling a method from loop is not the proper way, You can create and use custom pipe.

Html

<div class="dropmenu_block_header">
    <div *ngFor="let language of group.languages | filter : 'Russian'"></div>
</div>

filter.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
    name: 'filter'
})
export class FilterPipe implements PipeTransform {

    transform(items: any[], searchText: string): any[] {
        if(!items) return [];
        if(!searchText) return items;
        searchText = searchText.toLowerCase();
        return items.filter( it => it.Name.toLowerCase() == searchText);
    }
}

Also you can use variable instead of 'Russian'. Refer this for more How to apply filters to *ngFor?

hrdkisback
  • 898
  • 8
  • 19