Help Me Understand Angular *ngFor Directive Please

I thought I had a good grasp upon this directive but maybe I’m going about it the wrong way. I have a parent component that renders a child component and on that child component I have an ngFor directive that loops over my customers and creates a card for each instance. I keep getting this error though:

Here is my code:
Parent HTML (customer-section.component.html):

<app-customer-card *ngFor="let card of customerCards"></app-customer-card>

Parent TS (customer-section.component.ts):

import { Customer } from "./customer model/customer.model";

@Component({
  selector: "app-customer-section",
  templateUrl: "./customer-section.component.html",
  styleUrls: ["./customer-section.component.css"]
})
export class CustomerSectionComponent implements OnInit {
  customerCards: Customer[] = [
    new Customer(
      `Hardol Doe`,
      `../../../../assets/images/tyler-nix-ZGa9d1a_4tA-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a human smiling`
    ),
    new Customer(
      `Jane Schmidt`,
      `../../../../assets/images/marivi-pazos-cvpk5Y4ZWUs-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a human woman`
    ),
    new Customer(
      `John Smith`,
      `../../../../assets/images/marius-ciocirlan-vMV6r4VRhJ8-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a man smiling`
    )
  ];
  constructor() {}

  ngOnInit() {}
}

Child HTML (customer-card.component.html):

  <div class="card-img-container">
    <img
      class="customer-card-img"
      src="{{ card.image }}"
      alt="{{ card.imageAlt }}"
    />
  </div>
  <div class="customer-card-title">
    <h1>{{ card.name }}</h1>
  </div>
  <div class="customer-card-content">{{ card.content }}</div>
</div>

Child TS (customer-card.component.ts):

import { Customer } from "../customer model/customer.model";

@Component({
  selector: "app-customer-card",
  templateUrl: "./customer-card.component.html",
  styleUrls: ["./customer-card.component.css"]
})
export class CustomerCardComponent implements OnInit {
  constructor() {}

  ngOnInit() {}
}

And here is my model TS (customer.model.ts):

import { CommonModule } from "@angular/common";

@NgModule({
  declarations: [],
  imports: [CommonModule]
})
export class Customer {
  public name: string;
  public image: string;
  public content: string;
  public imageAlt: string;

  constructor(name: string, image: string, content: string, imageAlt: string) {
    this.name = name;
    this.image = image;
    this.content = content;
    this.imageAlt = imageAlt;
  }
}

I’m sure there is something small I’m missing but I cannot figure it out for the life of me. Any and all help is greatly appreciated :slight_smile:

Your customer-card.component.ts does not have a card object, but your customer-child.component.html references card.image, card.imageAlt, card.name, and card.content.

If you are going to use a child component for your card, then it needs to be passed the relevant data.

1 Like

Thank you for your response @ArielLeslie :slight_smile:

Ahh ok, I thought there was a way to have the loop on the containing element with the object on the parent TS file.

I moved the object to the child TS file with the loop still in the parent container but none of the info is rendered. Here is my current update:

Parent HTML (customer-section.component.html):

<app-customer-card *ngFor="let card of customerCards"></app-customer-card>

Parent TS (customer-section.component.ts):

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-customer-section",
  templateUrl: "./customer-section.component.html",
  styleUrls: ["./customer-section.component.css"]
})
export class CustomerSectionComponent implements OnInit {
  constructor() {}

  ngOnInit() {}
}

Child HTML (customer-card.component.html):

  <div class="card-img-container">
    <img
      class="customer-card-img"
      src="{{ card.image }}"
      alt="{{ card.imageAlt }}"
    />
  </div>
  <div class="customer-card-title">
    <h1>{{ card.name }}</h1>
  </div>
  <div class="customer-card-content">{{ card.content }}</div>
</div>

Child TS (customer-card.component.ts):

import { Component, OnInit, Input } from "@angular/core";
import { Customer } from "../customer model/customer.model";

@Component({
  selector: "app-customer-card",
  templateUrl: "./customer-card.component.html",
  styleUrls: ["./customer-card.component.css"]
})
export class CustomerCardComponent implements OnInit {
  customerCards: Customer[] = [
    new Customer(
      `Hardol Doe`,
      `../../../../assets/images/tyler-nix-ZGa9d1a_4tA-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a human smiling`
    ),
    new Customer(
      `Jane Schmidt`,
      `../../../../assets/images/marivi-pazos-cvpk5Y4ZWUs-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a human woman`
    ),
    new Customer(
      `John Smith`,
      `../../../../assets/images/marius-ciocirlan-vMV6r4VRhJ8-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a man smiling`
    )
  ];
  constructor() {}

  ngOnInit() {}
}

The previous error is gone but nothing renders on the page now. I’m confused as to why the ngFor isn’t looping over my customer object and rendering the information?

After a few days of thinking about my problem, I came up with a solution. It’s not exactly what I want but it works and I’m on a crunch to meet a deadline :smiley:

I essentially moved all of the HTML from the card (child) into the customer-section (parent) and used ng-content to render in the child. Below is my solution that creates 3 card components within the customer section:

Parent HTML (customer-section.component.html):

<app-customer-card *ngFor="let card of customerCards">
  <div class="customer-card-container">
    <div class="card-img-container">
      <img
        class="customer-card-img"
        src="{{ card.image }}"
        alt="{{ card.imageAlt }}"
      />
    </div>
    <div class="customer-card-title">
      <h1>{{ card.name }}</h1>
    </div>
    <div class="customer-card-content">{{ card.content }}</div>
  </div>
</app-customer-card>

Parent TS

import { Component, OnInit } from "@angular/core";
import { Customer } from "./customer model/customer.model";

@Component({
  selector: "app-customer-section",
  templateUrl: "./customer-section.component.html",
  styleUrls: ["./customer-section.component.css"]
})
export class CustomerSectionComponent implements OnInit {
  customerCards: Customer[] = [
    new Customer(
      `Hardol Doe`,
      `../../../assets/images/tyler-nix-ZGa9d1a_4tA-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a human smiling`
    ),
    new Customer(
      `Jane Schmidt`,
      `../../../assets/images/marivi-pazos-cvpk5Y4ZWUs-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a human woman`
    ),
    new Customer(
      `John Smith`,
      `../../../assets/images/marius-ciocirlan-vMV6r4VRhJ8-unsplash.jpg`,
      `Swearem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam scelerisque accumsan nisl, a mattis eros vestibulum et. Vestibulum placerat purus ut nibh aliquam fringilla.`,
      `a man smiling`
    )
  ];
  constructor() {}

  ngOnInit() {}
}

Child HTML (customer-card.component.html):

<ng-content></ng-content>

Child TS (customer-card.component.ts):

import { Component, OnInit, Input } from "@angular/core";
import { Customer } from "../customer model/customer.model";

@Component({
  selector: "app-customer-card",
  templateUrl: "./customer-card.component.html",
  styleUrls: ["./customer-card.component.css"]
})
export class CustomerCardComponent implements OnInit {
  constructor() {}

  ngOnInit() {}
}