Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Problema com renderização de *ngFor em caso particular

Iniciei um projeto novo com o intuito de estudar. Trata-se de um aplicativo onde você cadastra treinos de natação. Os treinos contém séries então criei a seguinte estrutura:

SerieComponent: Contem apenas uma série do treino.

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

@Component({
  selector: 'serie',
  templateUrl: './serie.component.html',
  styleUrls: ['./serie.component.css']
})
export class SerieComponent implements OnInit {

  @Input()
  metragem: number;
  @Input()
  repeticoes: number;
  @Input()
  descritivo: string;

  ngOnInit() {
  }

}

TreinoComponent: contem a descrição do treino, um array de séries, e a metragem total do treino

import { SerieComponent } from './../serie/serie.component';
import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'treino',
  templateUrl: './treino.component.html',
  styleUrls: ['./treino.component.css']
})
export class TreinoComponent implements OnInit {

  //Tera um array de série component
  //Percorrera esse array para exibir na planilha os dados.

  @Input()
  tipoTreino: string;
  @Input()
  metragemTotal: number = 0;
  series: SerieComponent[] = [];

  ngOnInit() {
  }
}

CadastroTreinoComponent: Responsável apenas pelo cadastro do treino

import { TreinoComponent } from './../treino/treino.component';
import { SerieComponent } from './../serie/serie.component';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-cadastro-treino',
  templateUrl: './cadastro-treino.component.html',
  styleUrls: ['./cadastro-treino.component.css']
})
export class CadastroTreinoComponent implements OnInit {

  serie: SerieComponent = new SerieComponent();
  treino: TreinoComponent = new TreinoComponent();;


  constructor() {

  }

  ngOnInit() {
  }

  adicionaSerie() {

    var reps = this.serie.repeticoes;
    var metragem = this.serie.metragem;
    this.treino.metragemTotal += (reps * metragem);
    this.treino.series.push(this.serie);
    this.serie = new SerieComponent();
  }

  cadastraTreino(event) {
    event.preventDefault();

  }
}

O template de cadastro: Não estou preocupado aqui com formatação Mas a idéia é que ele funcione "mais ou menos" como o cadastro de foto.

Coloco as informações na coluna da esquerda e as informações aparecem na coluna da direita.

<div class="container">
     <div class="col-md-6">
        <div class="form-group">  
          <label>Tipo de treino</label>
          <input type="text" (keyup)="0"  name="tipotreino" [(ngModel)]="treino.tipoTreino"  class="form-control"  autocomplete="off">   
        </div>

          <div class="row row-list">
            <label>Reps</label>
            <input type="number"  name="repeticoes" [(ngModel)]="serie.repeticoes"  class="form-control"  autocomplete="off">   
            <label>Metragem</label>
            <input type="number"  name="metragem" [(ngModel)]="serie.metragem"  class="form-control"  autocomplete="off">   
            <label>Descritivo</label>
            <input type="text"  name="descritivo" [(ngModel)]="serie.descritivo"  class="form-control"  autocomplete="off">   
        </div>
        <button type="button" class="btn btn-secondary" (click)="adicionaSerie()">adicionar</button>
     </div>
     <div class="col-md-6">
       <form (submit)="cadastraTreino($event)" class="row center-text">
          <treino tipoTreino={{treino.tipoTreino}}  metragemTotal={{treino.metragemTotal}}></treino>
          <button type="submit" class="btn btn-primary">Salvar</button>
      </form>
     </div>

</div>

Template do componente Treino:

<div>
  <div>
   Tipo de treino: {{tipoTreino}}
  </div>  
  <div>
        <serie *ngFor="let serie of series" repeticoes={{serie.repeticoes}} metragem="{{serie.metragem}}" descritivo="{{serie.descritivo }}"></serie>
  </div>
  <div>
    Total: {{metragemTotal}}
  </div>  
</div>

Template do SerieComponent:

<div class="row row-list">
  <div class="col-xs-2">
     {{repeticoes}}X
  </div>
  <div class="col-xs-2">
     {{metragem}}m
  </div>
  <div class="col-xs-8">
     {{descritivo}}
  </div>
</div>

No CadastroComponent, ao preencher os dados e clicar em salvar, eu chamo o metodo adicionaSerie(), que acessa o array de Series que está dentro de TreinoComponent e adiciona um item a ele. Pelo console, tudo é feito perfeitamente, os dados são adicionados corretamente. No html, é exibido o tipo de treino (que é preenchido no mesmo momento em que se digita) e o cálculo da metragem total também. Mas, o array de series não é renderizado. Alguma solução?

Obrigado

6 respostas

Tem mensagem de erro no Console?

Não, isso é o que mais incomoda. No console tudo funciona...

Hum, incomoda não, complica ! :)

<serie *ngFor="let serie of series" repeticoes={{serie.repeticoes}} metragem="{{serie.metragem}}" descritivo="{{serie.descritivo }}"></serie>

Você tem certeza que series é um array, que tem mais um ou mais elementos? Se não dá erro, ou o array esta chegando vazio, ou seu componente serie esta com algum problema que desconheço.

Experimenta fazer o ngFor sem usar o componente serie, só usando um <p> por exemplo.

Utilizei o p: abaixo exatamente o que eu fiz:

<div *ngFor="let serie of series">
        <p>{{serie.repeticoes}}</p> 
        <p>{{serie.metragem}}</p>
        <p>{{serie.descritivo}}</p>
  </div>

com o mesmo resultado...

Se eu fizer o seguinte no CadastroTreinoComponent , criar uma serie no ngOnInit():

ngOnInit() {
     var serieTeste: SerieComponent = new SerieComponent();
    serieTeste.repeticoes = 2;
    serieTeste.metragem = 100;
    serieTeste.descritivo = "teste";
    this.treino.series.push(serieTeste);  
}

A serie é renderizada normalmente no template, utilizando o componente , aparece como deveria.

Ao tentar adicionar uma nova serie fiz sair no console:

Mantive a série gerada pelo ngOnInit() e Utilizei como dados da nova série repetições: 10 metragem: 100 descritivo : testeDescritivo

console.log(this.serie);
//com o resultado:
SerieComponent {repeticoes: 10, metragem: 100, descritivo: "testeDescritivo"}

console.log(this.treino);
//com o resultado:
TreinoComponent {metragemTotal: 0, series: Array(2), tipoTreino: "TreinoTeste"}

MetragemTotal está dando 0 porque eu não estou fazendo o calculo mesmo. Mas como pode ver, foi adicionado o novo item série no array que agora contém 2 elementos serie, mas esse novo item não foi renderizado.

Todo o log do TreinoComponet:

TreinoComponent {
    metragemTotal: 0, 
    series: Array(2),
    tipoTreino: "TreinoTeste"
}
series: Array(2)
    0: SerieComponent
        descritivo: "teste"
        metragem: 100
        repeticoes: 2
    1:SerieComponent
        descritivo: "testeDescritivo"
        metragem: 100
        repeticoes: 10

Acredito que o problema está na detecção de mudanças, mas não encontro nenhuma solução que funcione na web.

Bom, vou atualizar aqui porque algo estranho aconteceu. No ultimo post, eu havia feito as alterações na tela de cadastro, então criei no ngOnInit() uma serie e coloquei manualmente no array e tudo havia sido renderizado normalmente.

Reiniciei o servidor, e tudo o que eu fiz no CadastroComponent parou de ser renderizado.

Então, as séries que eu crio no CadastroTreino não renderizam, nem a que eu crio na mão. Porém, no log, me mostra que o array de series no TreinoComponent está sendo populado.

E se eu crio a serie na mão, no ngOnInit do TreinoComponent e adiciono no array, ele é renderizado.

Mas qualquer elemento que eu tentar adicionar extra, é adicionado no array, mas não é renderizado.

solução!

Olá O problema foi resolvido com uma anotação

@ViewChild()

export class CadastroTreinoComponent implements OnInit {

  serie: SerieComponent = new SerieComponent();
  @ViewChild(TreinoComponent)
  treino: TreinoComponent = new TreinoComponent();

Pelo que eu entendi, essa anotação faz com que o componente pai tenha acesso ao componente filho e escute também os eventos que acontecem nele. Dessa forma, ao alterar o array que está no filho, ele renderiza novamente a tela trazendo o resultado esperado.

Se tiver uma explicação melhor ou mais didática, estou todo ouvidos. :D