20

Hello I am using Angular2 and wanted to fetch the server and get some values for each ID I get inside ngFor.

<div *ngFor="let item in items">
  <span> here call a function that do something with 'item' and return something new <span>
</div>
elhoucine
  • 2,356
  • 4
  • 21
  • 37

9 Answers9

15

You can make use of custom directive to call the method for each iteration:

import { Directive, Output, EventEmitter, Input, SimpleChange} from '@angular/core';

@Directive({
  selector: '[onCreate]'
})
export class OnCreate {

  @Output() onCreate: EventEmitter<any> = new EventEmitter<any>();
  constructor() {}
  ngOnInit() {      
     this.onCreate.emit('dummy'); 
  } 

}

and then you can use it in your *ngFor to call the method in your component:

<div *ngFor="let item of items">
    <div *ngIf="item" (onCreate)="yourMethod(item)"> 
    </div>
</div>

in Your component you can call this method as:

yourMethod(item){
   console.log(item);
}
Bhushan Gadekar
  • 13,485
  • 21
  • 82
  • 131
  • 1
    Is there an update needed for this code to be compatible with Angular 6? I can not get this to work as is. The function never gets called. I had to rename some things to make TSLint allow my app to compile as well. `OnCreate`/`onCreate` need to be renamed to `NgForOnCreateDirective`/`ngForOnCreateDirective` and the selector has to be prefixed with app, so I changed it to `appNgForOnCreate` – Routhinator Sep 15 '18 at 20:25
  • @Routhinator, does this sample works on Angular 5? cause the called function doesn't working. PS: I changed onCreate by CallFnDirective and the selector as '[appCallFn]'. – Atom Sep 27 '18 at 15:55
10

Doesn't sound very good , however , the simplest way :

<div *ngFor="let item of items">
  <span data-dummy="{{myFunction()}}" > here call a function that do something with 'item' and return something new <span>
</div>

Proper way :

@Directive({
  selector: '[runDummyFunc]'
})
export class runDummyFunc {

  constructor() {}
  ngOnInit() {      
     this.runDummyFunc()
  } 

}


<div *ngFor="let item in items">
    <span [runDummyFunc]="myFunction" > here call a function that do something with 'item' and return something new <span>
</div>

In your class :

      myFunction = ():void=>{

         console.log('whatever');
      }
Milad
  • 27,506
  • 11
  • 76
  • 85
  • 3
    using interpolation to call a function is not an authentic way, as it will be called multiple times due to `ngOnChanges`. @xe4me – Bhushan Gadekar Aug 07 '16 at 07:47
  • 2
    @BhushanGadekar , I know this is not a good way , that's why I've said doesn't sound good , but this is what he wants , and he can easily create a variable like fire_once and set it to true if it's called once , any way , this is not a good way – Milad Aug 07 '16 at 09:13
7

How about changing data before it comes to *ngFor in Typescript itself,

this.items.forEach((item,index)=>{
  item=this.func(item,index);
})

func(item,index):string{
    return item+ String(index); //do whatever you wish to do.
}
micronyks
  • 54,797
  • 15
  • 112
  • 146
  • 1
    I tried doing this inside a subscribe function, however I could not find a way to access `this.newArray` that I was trying to create in the forEach. Can you update this with an example? – Routhinator Sep 15 '18 at 20:29
1

Better do such function calls on each item in ngOnInit in a subscription, and then they should be displayed with *ngFor after transformation.

and change:

<div *ngFor="let item in items">

to

<div *ngFor="let item of items">
MatWaligora
  • 1,207
  • 1
  • 12
  • 15
1

The template is not the place to do that, you want to get your data in the component code. Sounds like you want to use something like flatMap of an observable, which lets you return another observable for each item in the source observable. This could be the returns of an http api call or whatever (fiddle):

var ids = ["a", "d", "c"];
var lookupValues = { "a": 123, "b": 234, "c": 345, "d": 456 };

// given an id
function fakeApiCall(id) {
  // given an id, return an observable with one entry: an object consisting
  // of an 'id' property with that id value and a 'lookup' property with the
  // value from lookupValues for that id
  return Rx.Observable.just({ id: id, lookup: lookupValues[id] });
}

var o1 = Rx.Observable.from(ids); // 'a, d, c

var o2 = o1.flatMap(x => {
  // here we get each value from o1, so we do an api call and return
  // an observable from each that will have the values in that
  // observable combined with all others to be sent to o2
  return fakeApiCall(x);
});

o2.subscribe(x => {
  document.write(JSON.stringify(x) + "<br/>");
});

// result:
// {"id":"a","lookup":123}
// {"id":"d","lookup":456}
// {"id":"c","lookup":345}
Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113
1

you can use trackBy:

@Component({
selector:'heroes',
template: `
<table>
    <thead>
        <th>Name</th>
    </thead>
    <tbody>
        <tr *ngFor="let hero of heroes; trackBy: trackHero" >
            <td>{{hero.name}}</td>
        </tr>
    </tbody>
</table>`})

export class Heroes {
    heroes = HEROES;

    trackHero(index, hero) {
        console.log(hero);
    }
}
Emir Mamashov
  • 1,108
  • 9
  • 14
1

Not sure if this is what you're asking, but you can pass in the item to a function along with an event variable like this:

<div *ngFor="let item in items">
  <span (click)="functionCall($event, item)"> <span>
</div>

And then grab that item in your class like this:

functionCall(event, item): void {
  console.log('item clicked:", item)
}
Michael Joy
  • 91
  • 1
  • 4
1

Write an another "ngFor" in between span tag in component.html

 <div *ngFor="let item in items">  
    <span *ngFor="let new of filterItems(item)"> {{new.anyAttribute}} </span>
 </div>

in component.ts if you want to filter item

filterItems(item) {
  let filterArray:any=[];
  return filterArray.filter(x => x.anyAttribute == item.anyAttribute);
}

if you want to return an array in item

filterItems(item){
   return item.Array //Array is an array in item
}
0
Component({
    selector:'heroes',
    template: `
    <table>
        <thead>
            <th>Name</th>
        </thead>
        <tbody>
            <tr *ngFor="let hero of heroes; trackBy: trackHero" >
                <td>{{hero.name}}</td>
            </tr>
        </tbody>
    </table>
`
})
export class Heroes {

    heroes = HEROES;

    trackHero(index, hero) {
        console.log(hero);
        return hero ? hero.id : undefined;

    }
}
Fredrik Widerberg
  • 3,068
  • 10
  • 30
  • 42
srk
  • 21
  • I think this may be the actual answer to the question in the title. `trackBy` caches the data in each iteration and is probably where we should work logic on that iteration. – Ben Racicot May 01 '23 at 16:28