Search code examples
arrayscomponentsrenderingngforangular12

How to render multiples times the same component with diferent content in angular 12


im trying to render a component that is inside a ng-for, but the content of the component will be trigger after a click on a button, the content will be captured and then sent to the child component. After that i need to render the child component using the data that i have obtained by the click. But the child component need to be render inside the same index in ng-for and not to render in all indexes of the array.

Here we can see some code:

Parent component HTML

<div class="main-container">
  <div class="container">
    <div *ngFor="let pessoa of lista; index as i" class="media-controler ">
      <div class="director-container">
        <div class="details">
          <span class="person"> Diretor </span>
          <span class="person-name"> {{pessoa.nome}}</span>
        </div>
      </div>

      <div class="slider" *ngFor="let x of pessoa.filmes">
        <mat-card class="example-card mat-elevation-z8">
          <mat-card-header>
            <mat-card-subtitle>{{x.tipo}}</mat-card-subtitle>
            <mat-card-title>{{ x.nome}}</mat-card-title>
          </mat-card-header>
          <img mat-card-image src="{{x.imgPath}}" class="imgPath" alt="{{x.imgDesc}}">
            <mat-divider></mat-divider>
          <mat-card-actions>
            <button mat-button color="primary" (click)="openDetails(pessoa.id, x);" >Detalhes</button>
          </mat-card-actions>
        </mat-card>
      </div>

      <ng-container *ngIf="click">
        <app-teste2 [objFilme]="heranca"></app-teste2>
      </ng-container>

    </div>
  </div>
</div>

Parent component TS

import { Component } from '@angular/core';

@Component({
  selector: 'app-teste1',
  templateUrl: './teste1.component.html',
  styleUrls: ['./teste1.component.scss']
})
export class Teste1Component {

    lista = [
    {nome:'Beltrano', id:1,
    filmes:[
    { imgDesc:'community',imgPath:'../../../assets/community.jpg', nome:'Community', tipo:'Serie',  duracao:'3:20'},
    { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg', nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
    { imgDesc:'Jujutsu no Kaisen',imgPath:'../../../assets/jjk.jpg', nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
    { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg', nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},

    {nome:'Fulano', id:2,
    filmes:[
    { imgDesc:'community',imgPath:'../../../assets/community.jpg' ,nome:'Community', tipo:'Serie',  duracao:'3:20'},
    { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg' ,nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
    { imgDesc:'Jujustu no Kaisen',imgPath:'../../../assets/jjk.jpg' ,nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
    { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg' ,nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},

    {nome:'Ciclano', id:3,
    filmes:[
    { imgDesc:'community',imgPath:'../../../assets/community.jpg' ,nome:'Community', tipo:'Serie',  duracao:'3:20'},
    { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg' ,nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
    { imgDesc:'Jujutsu no Kaisen',imgPath:'../../../assets/jjk.jpg' ,nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
    { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg' ,nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},

    {nome:'Giclano', id:4,
    filmes:[
    { imgDesc:'community',imgPath:'../../../assets/community.jpg' ,nome:'Community', tipo:'Serie',  duracao:'3:20'},
    { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg' ,nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
    { imgDesc:'Jujutsu no Kaisen',imgPath:'../../../assets/jjk.jpg' ,nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
    { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg' ,nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},
  ]

  heranca: any
  click!: boolean
  array: any[] = []


  openDetails(id:number, filme:any){
    this.click = true
    this.heranca = {
      id_diretor:id,
      nome: filme.nome,
      tipo: filme.tipo,
      duracao: filme.duracao,
      descricao: "Filme ou série xpto"
    }
    this.saveIntoArray(this.heranca)
  }

  saveIntoArray(filme: any){
    this.array.push(filme)
    console.log(this.array)


  }


}

Child Component HTML

<mat-card>
  <mat-card-content>
    <div class="container">

      <span class="title">Filme:
        <span class="title-content">{{filme.nome}}</span>
      </span>
      <span class="title">Duração:
        <span class="title-content">{{filme.duracao}}</span>
      </span>
      <span class="title">Descrição:
        <span class="title-content">{{filme.descricao}}</span>
      </span>
    </div>
  </mat-card-content>
</mat-card>

Child component TS

import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-teste2',
  templateUrl: './teste2.component.html',
  styleUrls: ['./teste2.component.scss']
})
export class Teste2Component implements OnChanges{

@Input() objFilme:any

filme: any;


ngOnChanges(changes: SimpleChanges){
  for (const propname in changes){
    const chg = changes[propname]
    const cur = chg.currentValue
    console.log(cur);
    if (cur) {
      this.filme = cur
    }
  }


}

}

What i achived

When i click on button "detalhes" open a card on the right with the data of the selected card but it duplicate for all others lines too.

Actual Code:

What i want to achive When I click on the button "detalhes" i need to open a card on the right with the selected data just for that line.

Example of what I want

I'm already try to create a ng-for inside the container to match the main ng-for index to render just for the correct container.


Solution

  • Why not creating a new, additional component that represents a row in your 'card-table'? If this new CardRowComponent contained the heranca: any-property, it would mean that every card-row can display ones own herana-value.

    In my solution there would be 3 components (and possibly 1 service):

    • Teste1Component (modified)
    • CardRowComponent (new)
    • Teste2Component (unchanged)
    • Optional: A Service that contains array: any[] = [];

    The CardRowComponent HTML could look like this:

    <div class="slider" *ngFor="let x of filmes">
      <mat-card class="example-card mat-elevation-z8">
        <mat-card-header>
          <mat-card-subtitle>{{x.tipo}}</mat-card-subtitle>
          <mat-card-title>{{ x.nome}}</mat-card-title>
        </mat-card-header>
        <img mat-card-image src="{{x.imgPath}}" class="imgPath" alt="{{x.imgDesc}}">
          <mat-divider></mat-divider>
        <mat-card-actions>
          <button mat-button color="primary" (click)="openDetails(pessoa.id, x);" >Detalhes</button>
        </mat-card-actions>
      </mat-card>
    </div>
    
    <ng-container *ngIf="click">
      <app-teste2 [objFilme]="heranca"></app-teste2>
    </ng-container>
    

    The CardRowComponent TS would look something like this:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-card-row',
      templateUrl: './card-row.component.html',
      styleUrls: ['./card-row.component.scss']
    })
    export class CardRowComponent {
    
      @Input()
      filmes!: any[];
    
      heranca: any;
      click!: boolean;
      array: any[] = [];
    
      openDetails(id:number, filme:any){
        this.click = true
        this.heranca = {
          id_diretor:id,
          nome: filme.nome,
          tipo: filme.tipo,
          duracao: filme.duracao,
          descricao: "Filme ou série xpto"
        }
        this.saveIntoArray(this.heranca)
      }
    
      saveIntoArray(filme: any){
        this.array.push(filme)
        console.log(this.array)
      }
    }
    

    The Parent-Component HTML would look something like this:

    <div *ngFor="let pessoa of lista; index as i" class="media-controler">
      <div class="director-container">
        <div class="details">
          <span class="person"> Diretor </span>
          <span class="person-name"> {{pessoa.nome}}</span>
        </div>
      </div>
    
     <app-card-row [filmes]="pessoa.filmes"></app-card-row>
    
    </div>
    

    The Parent-Component TS would just contain the movies-list:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-teste1',
      templateUrl: './teste1.component.html',
      styleUrls: ['./teste1.component.scss']
    })
    export class Teste1Component {
    
        lista = [
        {nome:'Beltrano', id:1,
        filmes:[
        { imgDesc:'community',imgPath:'../../../assets/community.jpg', nome:'Community', tipo:'Serie',  duracao:'3:20'},
        { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg', nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
        { imgDesc:'Jujutsu no Kaisen',imgPath:'../../../assets/jjk.jpg', nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
        { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg', nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},
    
        {nome:'Fulano', id:2,
        filmes:[
        { imgDesc:'community',imgPath:'../../../assets/community.jpg' ,nome:'Community', tipo:'Serie',  duracao:'3:20'},
        { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg' ,nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
        { imgDesc:'Jujustu no Kaisen',imgPath:'../../../assets/jjk.jpg' ,nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
        { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg' ,nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},
    
        {nome:'Ciclano', id:3,
        filmes:[
        { imgDesc:'community',imgPath:'../../../assets/community.jpg' ,nome:'Community', tipo:'Serie',  duracao:'3:20'},
        { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg' ,nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
        { imgDesc:'Jujutsu no Kaisen',imgPath:'../../../assets/jjk.jpg' ,nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
        { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg' ,nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},
    
        {nome:'Giclano', id:4,
        filmes:[
        { imgDesc:'community',imgPath:'../../../assets/community.jpg' ,nome:'Community', tipo:'Serie',  duracao:'3:20'},
        { imgDesc:'Avatar',imgPath:'../../../assets/avatar1.jpg' ,nome:'Avatar', tipo:'Filme',  duracao:'3:20'},
        { imgDesc:'Jujutsu no Kaisen',imgPath:'../../../assets/jjk.jpg' ,nome:'Jujutsu no Kaizen', tipo:'Anime',  duracao:'3:20'},
        { imgDesc:'Kamen Rider',imgPath:'../../../assets/kmrider.jpg' ,nome:'Kamem-rider', tipo:'Dorama', duracao:'3:20'}]},
      ]
    }
    

    The current Teste2Component would remain unchanged.

    Hint: Since you possibly want to have a global array: any[] = []; that is shared among all card-rows, you could move this array to a service and then inject the service into CardRowComponent via constructor.