Angular2 nested *ngFor
Asked Answered
C

2

12

I use an array to store a list of group objects and an array of a list of light objects. I want to show the first group in the html and all connected lights to that group. After that the next group and the related lights and so on....

<div>
  <ul>
    <li *ngFor="let group of hueGroups">
      {{group.name}}
      <li *ngFor="let light of hueLights; let i = index">
      {{hueLights[group.lights[i]].name}}
    </li>
  </ul>
</div>



export class AppComponent implements OnInit {
  hueGroups:any[];
  hueLights:any[];

  constructor(){
    this.hueGroups = [];
    this.hueLights = [];
  }

  listAllGroups(){
   for(var g in MyHue.Groups){ //MyHue.Groups returen an array with objects
    console.log(g);
    console.log(MyHue.Groups[g].name);
    for(var id:number = 0; id < MyHue.Groups[g].lights.length; id++){
      console.log("LightID:" + MyHue.Lights[MyHue.Groups[g].lights[id]].name); // Returns the name of the Lights
    }
    this.hueGroups.push(MyHue.Groups[g]);
  }

  listAllLights(){
   for(var l in MyHue.Lights){ //MyHue.Lights returns an array with objects
    console.log(MyHue.Lights[l]);
    this.hueLights.push(MyHue.Lights[l]);
   }
  }

}

If I try to run this I get the error

Cannot read property 'lights' of undefined

So I think the syntax is wrong for the nested ngFor. It should be possible to call "group" from above.

EDIT: This is the important part of how an object of the MyHue.Groups looks like:

{action:Object
 class:"Living room"
 lights: Array(2)
   0:"3"
   1:"1"
 name:"Room1"}

In the Group object there is only the ID of the lights which are depending to this group

This is the important part of how a light object looks like:

 {state: Object, type: "Extended color light", name: "Couch1", modelid: "LCT007"…}

This is what I get if I print the hole array to the console:

Object {1: Object, 3: Object, 4: Object}

So I have to match which Light ID is in which Group, then check the light Object for the name

Cremator answered 30/5, 2017 at 22:29 Comment(6)
It seems like you're trying to use *ngFor with an object. *ngFor works only with Arrays. You could create a pipe and use *ngFor to iterate over the keys of hueLights.Sylvan
do you have a example for this? I'm pretty new to Angular 2Cremator
Include the content of hueLights and hueGroups so it'll be better to understand this.Sylvan
ok I included the important partCremator
can you include some sample objects? This should be solvable by a single map most likelyBumbailiff
I included a sample for the group and light objectsCremator
P
21

Looks like you need to create a simpler data structure where your light objects are contained in an array property of your hueGroup objects. Then you'll be able to iterate easily through your groups, and each of their lights in your template.

For example, your template should look more like this:

<ul>
    <li *ngFor="let group of hueGroups">
    {{group.name}}
    <ul>
        <li *ngFor="let light of group.lights">
        {{light.name}}
        </li>
    </ul>
    </li>
</ul>

And your component should contain a hueGroups data object or model to render.

hueGroups = [{
    name: "group 1",
    lights: [{
      name: "light 1"
    },{
      name: "light 2"
    }]
  },
  {
    name: "group 2",
    lights: [{
      name: "light 3"
    },{
      name: "light 4"
    }]
  }];

Check out this plunker for a working example of what I mean: http://plnkr.co/edit/THXdN8zyy4aQMoo4aeto?p=preview

The key here is using your template to reflect the content of the component's data that's backing it. Beyond my example, you might want to use typescript to define an interface or class for your groups and lights.

Your example is a little more complicated because your template is trying to render a synthesis of two data structures (your hueGroup.lights seem to be arrays of just light ids). I'd recommend making a function that creates a hueGroup object with embedded light objects your template can iterate over easily. Here's an example of such a function:

Updated to reflect the example data:

ngOnInit(){
    this.hueGroups = this.hueGroupSource.map((group)=>{
      let lightObjects = group.lights.map((lightID)=>{
        return this.hueLightsSource.find((light, index) => {return index === lightID});
    });
    group.lights = lightObjects;
    return group;
  });

}

Here is a plunker showing it in a working example. http://plnkr.co/edit/V309ULE8f6rCCCx1ICys?p=preview

Petrosal answered 30/5, 2017 at 23:4 Comment(7)
Thanks for your help. I included a sample for the group and light objects I receive from my service. Maybe that make more sense nowCremator
I've updated my answer to use objects from your light / group examples. Good luck!Petrosal
Sounds good, but the problem with the data object / model ist that there are not always the same number of lights in one group. A group can have one to multiple lights. Is it even possible to do this with a extra data object?Cremator
A group in my plunkr example can have any number of lights. You should be able to load it and remove a light id from a group in the app.ts file. Here's a fork showing groups with different numbers of lights: plnkr.co/edit/G6gHvmu9FzkEqtyVFiGu?p=preview. Not sure what you need to do that my plunkr isn't solving?Petrosal
It works almost. I think there is a problem in this line return index === lightID if I edit it to return index === lightID-1 it works for the first group correct but in the second group the light is undefined.Cremator
This plunker isn't working? plnkr.co/edit/G6gHvmu9FzkEqtyVFiGu?p=preview shows two working groups for me. Can you link me to a plunker that is broken then I can fix it and explain?Petrosal
Your plunker was working, but if I tried to use it with my service it did not work. I could't say what the problem was but I found a similar way to solve my problem. Your post gave me the right direction so I accept it as answer :) thanks for your helpCremator
A
1

I managed to get this working with the below nested ngFors:

<ion-card *ngFor="let exercise of exercises">
    <ion-card-header>
      <ion-card-title>
        {{exercise.name}}
      </ion-card-title>
    </ion-card-header>
    <ion-item *ngFor="let set of exercise.sets">
      set: {{set.setNo}}
      reps: {{set.reps}}
      weight:{{set.weight}}
    </ion-item>
  </ion-card>
Alexi answered 8/6, 2021 at 19:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.