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

ajuda

Boa tarde, estou fazendo um trabalho da faculdade com ionic 3 e o tema é um cardápio de restaurante, então eu criei uma API com um array de categorias de prato e cada catoria, dentre outros dados tem um array de produtos que são os pratos, lanches, etc da categoria... Bom preciso de uma ajuda no seguinte. essas categorias eu consigo consumir tranquilo gravo num array de categorias e basicamente o mesmo esquema do consumo da api de carros, exibido de boa no app, está ok já essa parte, e uma das coisas que tenho que utilizar no app tmb seria um "prato do dia" que tem uma combinação promocional, de inicio pensei em criar só um prato de dia na API e tentar consumir assim:( (<) ta assim pq não aparece se for < >) this._http.get(<)PratoDoDia (>)('http://localhost:8080/api/categoria/pratoDoDia') .subscribe( prato => { this.pratoDoDia = prato; } );

porém não consegui cosumir assim, só funciona quando é um array.. queria entender o motivo. Mas no fim ficou melhor como array mesmo, e na api acabei criando um array de pratosPromocionais e ficou assim o consumo:

this._http.get(<)PratoDoDia([])(>)('http://localhost:8080/api/categoria/pratoDoDia') .subscribe( pratos => { this.pratosPromocionais = pratos; } );

ai funciona de boa.

MINHA DÚVIDA: eu preciso exibir esse prato do dia só que eu não sei como exibir sem fazer um ngFor e percorrer todo o array... ex: ion-item ngFor="let pratoDoDia of pratosPromocionais" (click)="alert('oi')"

queria uma forma de exibir por exemplo, 1 item do array só e queria determinar qual...

16 respostas

Oi Armando, tudo bem? Você só precisa usar o ngFor por causa do array, quando você não tem um array, você pode acessar o atributo do componente direto com {{ pratoDoDia }} por exemplo. Não?

Teóricamente sim, mas o pratoDoDia é um prato de pratosPromocionais, então eu penso, declaro um prato: PratoDoDia, e atribuo prato = pratosPromocionais[0]. e tento usar só o prato {{ prato.nome }}. Não funciona...

o projeto ta aqui, caso possa dar uma olhada. https://github.com/DinhuX/horaBoa

Boa noite, Armando! Como vai?

Cola aqui o código que vc utiliza pra fazer a requisição e o trecho de código onde vc o chama, por favor! Mas utilize o botão "inserir código" e o insira no lugar indicado para que seu código fique formatado corretamente e facilite a análise.

A requisição eu faço assim mas eu queria mesmo era fazer um lance tipo ... prato => {} tipo sem ser em um array mas não funcionou

this._http.get<PratoDoDia[]>('http://localhost:8080/api/categoria/pratoDoDia')
              .subscribe(
                pratos => {
                  this.pratosPromocionais = pratos;
                }
              );
<ion-item *ngFor="let pratoDoDia of pratosPromocionais" (click)="selecionaPratoDoDia(pratoDoDia)">
      <img src="{{ pratoDoDia.fotoPath }}" />
      <p>{{ pratoDoDia.nome }}</p>
    </ion-item>

desse jeito está funcionando... queria só que não precisasse percorrer um for para o prato do dia e sim exibir um prato especifico, como se o "cliente" pensasse, hoje o prato X está na promoção ai ele só muda na API

Vc não consegue receber um objeto pq sua API está retornando um array.

// horaBoa_webservice/api.js

const pratosPromocionais = [
        {
            nome: 'Guisado, Salmão e Cheesecake',
            produtos: [
                {nome: 'Guisado', desc: 'Guisado de cordeiro com cuzcuz marroquino.', valor: 22.90, fotoPath: `http://${ip}:8080/img/entradas/guisado.png`},
                {nome: 'Salmão', desc: 'Salmão grelhado com arroz e legumes.', valor: 29.90, fotoPath: `http://${ip}:8080/img/principais/salmao.jpg`},
                {nome: 'Cheesecake', desc: 'Cheesecake de frutas vermelhas.', valor: 10, fotoPath: `http://${ip}:8080/img/sobremesas/cheesecake.jpg`}
            ],
            valor: 39.90,
            fotoPath: `http://${ip}:8080/img/pratododia/under_construction.png` 
        }  
    ];

app.get('/api/categoria/pratoDoDia', (req, res) => 
        res.json(pratosPromocionais)
    );

Tente fazer asim na sua API:

// horaBoa_webservice/api.js

const pratoPromocional = 
        {
            nome: 'Guisado, Salmão e Cheesecake',
            produtos: [
                {nome: 'Guisado', desc: 'Guisado de cordeiro com cuzcuz marroquino.', valor: 22.90, fotoPath: `http://${ip}:8080/img/entradas/guisado.png`},
                {nome: 'Salmão', desc: 'Salmão grelhado com arroz e legumes.', valor: 29.90, fotoPath: `http://${ip}:8080/img/principais/salmao.jpg`},
                {nome: 'Cheesecake', desc: 'Cheesecake de frutas vermelhas.', valor: 10, fotoPath: `http://${ip}:8080/img/sobremesas/cheesecake.jpg`}
            ],
            valor: 39.90,
            fotoPath: `http://${ip}:8080/img/pratododia/under_construction.png` 
        };

app.get('/api/categoria/pratoDoDia', (req, res) => 
        res.json(pratoPromocional)
    );

E assim na sua requisição:

this._http.get<PratoDoDia>('http://localhost:8080/api/categoria/pratoDoDia')
              .subscribe(
                prato => {
                  // faz o que vc quiser com o prato...
                }
              );

Eu tinha tentado isso, não funfa =/...

testei novamente. declarei: public pratoPromocional: PratoDoDia;

this._http.get<PratoDoDia>('http://localhost:8080/api/categoria/pratoDoDia')
              .subscribe(
                prato => {
                  this.pratoPromocional = prato;
                }
              );

na API do ta sem o [], igual o teu exemplo.

e chamo {{pratoPromocional.nome}} em home.html...

resposta: Runtime error _co.pratoPromocional is undifined.

ah, se eu colocar um console.log aqui:

this._http.get<PratoDoDia>('http://localhost:8080/api/categoria/pratoDoDia')
              .subscribe(
                prato => {
                  this.pratoPromocional = prato;
                  console.log(this.pratoPromocional);
                }
              );

eu consigo ver no console o objeto carregado... só que não exibe ele no home.html... o problema está la então, ta errado tentar chamar só assim {{ pratoPromocional.nome ?}}

Mto bem, então como vc mesmo constatou o problema não está na requisição e nem na API e sim em como vc está gerenciando a informação que está recebendo!

Cole o seu código da classe Typescript e do seu template HTML aqui pra eu dar uma olhada.

import { Produto } from '../models/produto';

export class PratoDoDia {
    constructor(
        public nome: string, 
        public produtos: Produto[],
        public valor: number,
        public fotoPath: string,
    ) {};
}
<ion-header>
  <ion-navbar>
    <ion-title>
      Hora Boa
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <ion-list>
    <ion-item *ngFor="let categoria of categorias" (click)="selecionaCategoria(categoria)">
      <ion-thumbnail item-left>
        <img src="{{ categoria.fotoPath }}" />
      </ion-thumbnail>
      <h2>{{ categoria.nome }}</h2>
    </ion-item>
  </ion-list>
  <ion-item-group>
    <ion-item-divider color="light">
      <ion-icon name="restaurant"></ion-icon>
        PRATO DO DIA
    </ion-item-divider>
   <!-- <ion-item *ngFor="let pratoDoDia of pratosPromocionais" (click)="selecionaPratoDoDia(pratoDoDia)">
      <img src="{{ pratoDoDia.fotoPath }}" />
      <p>{{ pratoDoDia.nome }}</p>
    </ion-item>-->
    {{ pratoPromocional.nome }}
  </ion-item-group>
</ion-content>

Armando, faltou a classe Typescript que controla o template.

export class HomePage {
  public categorias: Categoria[];
 // public pratosPromocionais: PratoDoDia[];
  public pratoPromocional: PratoDoDia;

  constructor(
    public navCtrl: NavController, 
    private _http: HttpClient,
    private _loadCtrl: LoadingController,
    private _alertCtrl: AlertController
  ) {
    let loading = this._loadCtrl.create({
      content: 'carregando as categorias...'
    });

    loading.present();

    this._http.get<Categoria[]>('http://localhost:8080/api/categoria/lista')
              .subscribe(
                categorias => {
                  this.categorias = categorias;
                  loading.dismiss();
                },
                (err: HttpErrorResponse) => {
                  console.log(err);
                  loading.dismiss();
                  this._alertCtrl.create({
                    title: 'Falha na conexão',
                    subTitle: 'Não foi possível carregar a lista de categorias, tente novamente mais tarde.',
                    buttons: [
                      {text: 'Ok'}
                    ]
                  }).present();
                }
              );
    this._http.get<PratoDoDia>('http://localhost:8080/api/categoria/pratoDoDia')
              .subscribe(
                prato => {
                  this.pratoPromocional = prato;
                  console.log(this.pratoPromocional);
                }
              );
  }

a da home?

Armando, o que deve estar acontecendo é que vc tem duas requisições sendo feitas para a sua API as quais sua página depende para que possa ser construída! No entanto, essas operações são assíncronas, ou seja, vc não tem controle de quando elas são finalizadas! Vi aqui que vc não fez os cursos avançados de Javascript e nem os cursos de Angular 2 que são pré-requisitos do curso de Ionic 3. Talvez por isso vc esteja com dúvidas com relação a isso.

A solução para o seu problema será utilizar o mergeMap() como apresentado por mim durante o curso de Ionic 3 nessa aula.

Mas por que que quando eu uso como Array, funciona perfeitamente as duas requisições na mesma API?

solução!

Eis a grande pergunta, meu aluno! Mas eu vou te explicar e te tirar das trevas em direção a luz! Aperte os cintos e vamos lá!

Na realidade, o problema das requisições assíncronas tbm acontecem quando vc trabalha com array, só não percebeu pq vc lista a informação com o *nfFor que maquia o problema!

O que acontece é que a sua tela é apresentada e se a segunda requisição (dos pratos promocionais) ainda não tiver sido respondida o array estaria vazio e o *ngFor não seria executado, escapando de executar as linhas a seguir:

<img src="{{ pratoDoDia.fotoPath }}" />
<p>{{ pratoDoDia.nome }}</p>

Com isso o problema do undefined não acontece e quando recebe a resposta da requisição a sua tela atualiza sozinha. Só que o processo acontece tão rápido que vc não percebe nada disso e acha que está tudo certo!

Por outro lado, quando vc deixa de retornar um array, no seu template vc tbm não usa mais o *ngFor e tenta apresentar diretamente o seu prato promocional, como na linha a seguir:

{{ pratoPromocional.nome }}

No entanto, agora vc dá de cara com o problema das requisições assíncronas, uma vez que ao construir a tela a resposta da segunda requisição ainda não chegou e o *ngFor não está mais lá pra te "proteger" desse problema.

Uma forma de vc resolver isso, seria somente fazer no seu template uma verificação se o pratoPromocional já foi preenchido:

<ion-item *ngIf="pratoPromocional">
    {{ pratoPromocional.nome }}
</ion-item>

No entanto, só fazer isso não é o ideal! Vc precisa usar o mergeMap() como fiz no curso para fazer a composição dos dois Observables e, antes disso, indico a vc fazer os cursos pré-requisitos que falei antes! Pq sem a base deles vc terá dificuldades de entender os conceitos que são apresentados, como foi nessa questão da assincronia.

Dessa forma, vc sairá bem mais preparado para lidar com as questões mais complexas durante a sua vida de desenvolvedor!

Grande abraço e bons estudos, meu aluno!

Muito bom professor, te agradeço demais pela atenção e pela explicação já me tirou das trevas haha agora vou me encaminhar para a luz estudando melhor o assunto e irei fazer os cursos indicados também, Abraços...

Mto bem, Armando! É isso aí, manda bala nos estudos!

E sempre que tiver qualquer dúvida é só mandar aqui no fórum!

Grande abraço e bons estudos, meu aluno!