MWPodgorni
MWPodgorni

Reputation: 83

Angular KeyValuePie and ngFor strange behavior

I'm trying to figure out what is happening in my program and I really don't understand. Before I jump to the problem, for the purpose of this question I simplified the code and I conducted some tests, to make sure that I localized the problem. So, in my component.html file I have this piece of code:

<div *ngFor="let item of entityDetails | keyvalue">
   <div *ngIf="hasConflict(item.key)">text</div>
</div>

As you can see I'm using *ngFor together with Angular KeyVauePipe and inside I check a condition using *ngIf. Entity Details is a json that I get through http request using Promise and it looks like this:

{rcn: "1912330", name: "Barcelona supercomputing Center", vatNumber: "ESS090000099D", category: "Research Organisation", categoryCode: "REC"}

In component.ts, the declaration:

public entityDetails: string[] = new Array();

and the retrieving the data:

this.service.getEntityDetails().then(data => {
   this.entityDetails = data;
});

In hasConflict method, all I do is printout:

hasConflict(item): Boolean {
    let check: boolean = false;
    console.log("test");
    return check;
}

When I run it and open the console, I can already see a bunch of printouts:

printouts

but then once I click anywhere on the website or I use scroll, they intensify, after one mouse click:

click

after quick scroll:

scroll

Any help will be appreciated.

Upvotes: 0

Views: 168

Answers (2)

crystalfrost
crystalfrost

Reputation: 281

As suggested in the comments [@Chellappan] you should use a pipe. Function calling inside ngif is what causes the problem due to angular's change detection triggering.

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

@Pipe({ name: 'hasConflict' })
export class HasConflictPipe implements PipeTransform {
 transform (value: any, type: string, ): boolean {

   console.log('test');
   return value ? true : false;
 }
}

and then inside your template

<div *ngFor="let item of entityDetails | keyvalue">
   <div *ngIf="item.key | hasConflict">text</div>
</div>

Obviously you should place your logic inside hasConflict that is specific to your case.

Upvotes: 0

Archit Garg
Archit Garg

Reputation: 3267

Why is this happening.

This is happening beacuse you are calling a function from template. So every time angular's change detection runs, it updates the UI and calls the function again.

How to fix

Instead of calling a function from template, Prepare a map inside subscribe of getEntryDetails which you could use in template directly to check for the condition. For example:

conflictMap = {};

if(condition){
  conflictMap[key] = value;
}

and then directly in html:

<div *ngFor="let item of entityDetails | keyvalue">
   <div *ngIf="conflictMap[item.key]">text</div>
</div>

Further reading - using a function in *ngIf runs several times instead of once

Upvotes: 1

Related Questions