2

Consider a list of all of the users in your system:

  allUsers = {
    a: {name:'Adam',email:'adam@testco.com',level:'admin',group:'Owners'},
    b: {name:'Barbra',email:'Barbra@testco.com',level:'admin',group:'Owners'},
    c: {name:'Chris',email:'Chris@otherplace.net',level:'standard',group:'Managers'},
    d: {name:'Dennis',email:'dsmolek@showman.com',level:'standard',group:'Managers'},
    e: {name:'Elizabeth',email:'eadams@testco.com',level:'standard',group:'Staff'},
    f: {name:'fred',email:'fred@testco.com',level:'visitor',group:'Visitor'},

  }

Then a list of the users on a project:

usersList = ['a','b','d','f'];

So you have a nice easy function to take the user id and lookup the rest of the user details:

  getUser(userId){
    console.log('Getting User with Id:', userId);
    if(allUsers[userId]) return allUsers[userId];
  }

Then in the template you use *ngFor to loop through the users in the list, but you want to then lookup the full set of details

<tr *ngFor="#userId in usersList" #user="getUser(userId)">
  <td>{{user.name}}</td>
</tr>

Doesn't work... Without creating custom components or other more complex stuff I can't figure out how to run the getUser function once per user. I can of course run it over and over like:

<td>{{getUser(userId).name}}</td>

but this doesn't seem like the best way. Is there an easier way to get access to the userId variable and set it as a local variable?

Here's a plunker of what I've been playing with so far

SnareChops
  • 13,175
  • 9
  • 69
  • 91
Dennis Smolek
  • 8,480
  • 7
  • 30
  • 39
  • Have you tried putting the userId in curly braces? Like this: #user="getUser({{userId}})" – Luka Jacobowitz Feb 02 '16 at 19:55
  • can you just filter the list, like this, and then iterate over the someUsers variable instead: `var someUsers = usersList.map(function(x) { return allUsers[x]; }) ` – Marc Feb 02 '16 at 20:50
  • mapping might work... I'd have to run it every time the component ran but I like the direction. I haven't tried curly braces, I'm not 100% but I don't think it's the right syntax. – Dennis Smolek Feb 03 '16 at 15:52

4 Answers4

4

Though you can create a variable on ngFor template directive, but that variable can only take value of index, even, odd & last.

Thats why I could use pipe for this case, where you need to pass usersList & allUsers to custom @Pipe getUsers (it will act same as a filter in Angular1).

Markup

<tr *ngFor="let user of usersList | getUsers: allUsers">
  <td>
    {{user.name}}
  </td>
  <td>
    {{user.email}}
  </td>
  <td>
    {{user.level}}
  </td>
  <td>
    {{user.group}}
  </td>
</tr>

Code

@Pipe({
  name: 'getUsers',
  pure: false
})
export class GetUserPipe implements PipeTransform {
  constructor(){
    this.test = 'Test';
  }
  transform(array:Array<string>, object: any) : any {
    // for..in loop
    let output = [];
    for (var num in array)
    {
      // somehow object goes inner side
      output.push(object[0][array[num]]);
    }
    console.log(output)
    return output;
  }
}

Demo Plunkr

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • This is the direction I went. I'm actually using a UserService so I was able to inject the service straight into the pipe. I still feel modifying the resulting local variable would be useful but the pipe makes it cleaner looking as well. – Dennis Smolek Feb 03 '16 at 15:58
2

I like

<td>{{getUser(userId).name}}</td>

That's what I would use. It is much easier than creating, say, a pipe/filter.

Even if you could do something with a local template variable, I believe it would get re-evaluated for every item in the list, every change detection cycle, just like the HTML shown above.

See also ng2: How to create variable in ngFor loop, where one of the suggestions is to create a child component, and pass getUser(userId) to an input property on that component.

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • But if we need to bind value of user object 10 times on UI, then we need to repeat `getUser(userId)` on HTML that many times.. will it be good practice to have it..? – Pankaj Parkar Feb 03 '16 at 07:01
  • I agree, running getUser over and over doesn't seem very efficient. – Dennis Smolek Feb 03 '16 at 15:54
  • @PankajParkar, less code is almost always better. I personally wouldn't write extra code unless there was a demonstrable performance issue. I.e., avoid premature optimization. `getUser()` is not a complicated function. – Mark Rajcok Feb 03 '16 at 16:44
  • @Mark Make sense.. Thanks +1 – Pankaj Parkar Feb 03 '16 at 21:11
0

You can have your markup as follows

<tr *ngFor="theUser in getTheUsers(usersList)" >
   <td>{{theUser.name}}</td>
</tr>

getTheUsers() function should be something like this

getTheUsers(usersList:string[]){
  return usersList.map(user=> this.allUsers[user]); 
}
Greatran
  • 180
  • 3
  • 18
0

See also my alternative approach which makes use of ng-container, *ngIf with let. Though not ideal, you don't need to create a subcomponent. Basically the expression of the *ngIf creates an object (so the *ngIf is always thruthy thus always rendering the content) and a property on the object contains the computed value. The object is assigned to local variable (using let syntax of *ngIf).

rekna
  • 5,313
  • 7
  • 45
  • 54