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

View não atualiza

Já postei aqui um tempo atrás um problema com um ngFor que não atualizava a view, que foi resolvido com a anotação @ViewChild. Agora estou com um problema semelhante, mas um pouco diferente...

Estou desenvolvendo um app que cria planilha de treinos e tenho a seguinte estrutura:

ExerciciosComponent SerieComponent TreinoComponent CadastroTreinoComponent.

Um treino, é composto por várias séries e uma série é composta por vários exercícios.

O componente de cadastro tem um layout em colunas, sendo que a primeira coluna o usuário imputa os dados do exercício, e por meio de um botão adiciona esse exercício na série (coluna ao lado). Ao finalizar de editar a série, ele clica em um outro botão, que adiciona a série ao treino. Ao fazer isso, a coluna de séries deve ser limpa, e a série deve aparecer formatada na coluna de treino. O problema é que a coluna de série não limpa, e fica travada como se a view não estivesse atualizada.

ExercicioComponent

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

@Component({
  selector: 'exercicio',
  templateUrl: './exercicio.component.html',
  styleUrls: ['./exercicio.component.css']
})
export class ExercicioComponent {

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


}

exercicio.component.html

<div class="row">
  <div class="col-xs-3">
    <div [hidden]="repeticoes < 2">
      {{repeticoes}} X
    </div>
  </div>
  <div class="col-xs-4">
    {{metragem}} M
  </div>
  <div class="col-xs-5">
    {{descritivo}}
  </div>
</div>

SerieComponente

import { ExercicioComponent } from './../exercicio/exercicio.component';
import { Component, Input, ViewChildren } from '@angular/core';

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

  @Input()
  repsSerie: number = 1;
  @Input()
  exercicios: ExercicioComponent[] = [];
  metragemTotalSerie: number = 0;

  constructor() {
    this.repsSerie = 1;

    this.exercicios = [];
    this.metragemTotalSerie = 0;
  }

  adicionaExercicio(exercicio: ExercicioComponent) {
    var nrReps = exercicio.repeticoes;
    var metragemExercicio = exercicio.metragem;
    var totalExercicio = nrReps * metragemExercicio;
    this.metragemTotalSerie += totalExercicio;
    this.exercicios.push(exercicio);
  }
}

serie.component.html

<div class="row row-list">
  <div class="col-xs-2">
    <div [hidden]="repsSerie < 2" class="bg-success">   
     {{repsSerie}}X
    </div>
  </div>
  <div class="col-xs-9 bg-info">
    <div *ngFor="let exercicio of exercicios"  >
       <exercicio metragem={{exercicio.metragem}} descritivo={{exercicio.descritivo}} repeticoes={{exercicio.repeticoes}} ></exercicio>
    </div>
  </div>
</div>

TreinoComponent

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

@Component({
  selector: 'treino',
  templateUrl: './treino.component.html',
  styleUrls: ['./treino.component.css'],

})
export class TreinoComponent {


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

  adicionaSerie(serie: SerieComponent) {
    var reps = serie.repsSerie;
    this.metragemTotal += (reps * serie.metragemTotalSerie);
    this.series.push(serie);
  }
}

treino.componente.html


<div>
  <div>
   Tipo de treino: {{tipoTreino}}
  </div>
  <div *ngFor="let serie of series">
      <serie repsSerie={{serie.repsSerie}} [exercicios]="serie.exercicios"></serie>
  </div>
  <div>
    Total: {{metragemTotal}}
  </div>  
</div>

CadastroTreinoComponent

import { ExercicioComponent } from './../exercicio/exercicio.component';
import { TreinoComponent } from './../treino/treino.component';
import { SerieComponent } from './../serie/serie.component';
import { Component, ViewChild, Output, } from '@angular/core';

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

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

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

  exercicio: ExercicioComponent = new ExercicioComponent();

  adicionaExercicio() {
    this.serie.adicionaExercicio(this.exercicio);
    this.exercicio = new ExercicioComponent();
  }
  adicionaSerieNoTreino() {
    this.treino.adicionaSerie(this.serie);
    //Verificar porque que não limpa o formulário
    this.serie = new SerieComponent();
  }


  cadastraTreino(event) {
    event.preventDefault();
    console.log(JSON.stringify(this.treino));
    this.treino = new TreinoComponent();
  }
}

cadastro-treino.component.html

<div class="container center-block" >
<section id="Exercicio" class="col-md-4">
  <div class="form-group">
    <h2 class="text-center">Exercício</h2>
    <div class="input-group">
      <input type="number" name="repeticoes" [(ngModel)]="exercicio.repeticoes" class="form-control" placeholder="Repetições">
      <div class="input-group-addon">Vezes</div>
        <input type="number" class="form-control" [(ngModel)]="exercicio.metragem" placeholder="Metragem">
      <div class="input-group-addon">Metros</div>
    </div>
    <div class="input-group">
      <input type="text" class="form-control" [(ngModel)]="exercicio.descritivo" placeholder="Braço c/Palmar">
    </div>
    <button type="button" (click)="adicionaExercicio()" class="btn btn-primary">Adicionar na série</button>
  </div>
</section>
<section class="col-md-4">
  <div class="form-group">
    <h2 class="text-center">Série</h2>
    <div class="input-group">
      <input type="number" name="repsSerie" [(ngModel)]="serie.repsSerie" class="form-control" placeholder="Repetir série">
      <div class="input-group-addon">Vezes</div>
    </div>
    <serie></serie>
    <button  class="btn btn-primary" (click)="adicionaSerieNoTreino()">Adicionar no treino</button>
  </div>
</section>
<section class="col-md-4">
  <form (submit)="cadastraTreino($event)">
    <div class="form-group">
      <h2 class="text-center">Treino</h2>
      <treino></treino>
      <button  type="submit" class="btn btn-primary">Salvar treino</button>
    </div>
  </form>
</section>
</div>

Ao logar no console, verifica-se que o código está funcionando, o objeto Serie é limpo, porém a view não é atualizada.

Se preferir, tem o código deste problema no github, basta baixar e executar pra ver o bug em ação: https://github.com/HildePedroni/Angular.git

nenhum erro no console é apresentado. Alguma sugestão?

4 respostas

Rapaz, muita gente tendo problema com Angular 2, também, é tão novinho. Mas tipo, não sei a causa do seu problema olhando rapidamente seus arquivos.

No entanto, você pode fazer um teste para saber se o zones esta funcionando corretamente. Você pode forçar uma renderização da sua view após executar a operação que limpa a série.

Isso pode ser feito com ngZone. Segue um exemplo da documentação. Você injeta o artefato e dispara a atualização.

https://angular.io/docs/ts/latest/api/core/index/NgZone-class.html#!#run-anchor

Isso não resolve a causa do problema, mas lança uma luz para sabermos se é problema no change detection do Angular ou alguma coisa no seu código que não enxergamos.

Pelo que eu entendi do ngZone, para disparar um renderização eu tenho que chamar o metodo run() do ngZone

implementei basicamente como o exemplo da documentação. dentro do cadastro-treino.componente.ts

adicionaSerieNoTreino() {
    this._ngZone.runOutsideAngular(() => {
      this._adicionaELimpa(() => {
        this._ngZone.run(() => {

          console.log("Limpou");
        });
      });
    });


    //Verificar porque que não limpa o formulário
  }

  _adicionaELimpa(doneCallback: () => void) {
    this.treino.adicionaSerie(this.serie);
    this.treino = new TreinoComponent();
    doneCallback();
  }

O resultado foi exatamente o mesmo.

Pode ser que eu tenha implementado errado. Não sei se entendi muito bem esse Zone.

solução!

É assim, mas eu vi algo no seu código estranho:

 adicionaSerieNoTreino() {
    this.treino.adicionaSerie(this.serie);
    //Verificar porque que não limpa o formulário
    this.serie = new SerieComponent();
  }

O this.serie é um objeto, certo? Você esta passando a referência para dentro de this.treino.adicioanSerie. Quando você faz this.serie = new SerieComponent esta alterando a mesma referência que você passou para seu método this.treino.adicionaSerie.

Por isso sua lista esta ficando limpa.

Faça um teste, comente a linha this.serie = new SerieComponent();.

Cara, eu estava achando que ao colocar ele dentro do array do Treino ele criaria uma nova referencia. E ao zerar o objeto original ele iria limpar normalmente.

Fiz o seguinte:

adicionaSerieNoTreino() {

    var novaSerie = new SerieComponent();
    novaSerie.exercicios = this.serie.exercicios;
    novaSerie.metragemTotalSerie = this.serie.metragemTotalSerie;
    novaSerie.repsSerie = this.serie.repsSerie;
    this.treino.adicionaSerie(novaSerie);
    //Verificar porque que não limpa o formulário
    this.serie.clear();
  }

O metodo this.série.clear() apenas reinicia as variaveis para seus estados originais.

Assim, tudo funcionou.

Muito obrigado.