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

Dúvida com Observables (Angular8 & Ionic 4)

Boa tarde,

Gostaria de uma orientação quanto aos Observables.

Tenho acompanhado as aulas de Angular e Ionic e adaptando ao meu projeto.

Estou fazendo um aplicativo para aprendizado, onde eu faço a inserção de produtos em um carrinho de compras, esses produtos no caso seriam verduras.

Vou tentar ser breve na explicação da estrutura do meu projeto para ficar claro.

Os meus produtos estão em um banco de dados (MongoDB) e estão da seguinte forma:

{imagemUrl: "assets/Agriao.jpg", nome: "Agrião", preco: 2, quantidade: 9, _id: 12}
{imagemUrl: "assets/Rucula.jpg", nome: "Rúcula", preco: 2, quantidade: 15, _id: 31}

Quando eu adiciono um produto ao carrinho, cada produto vai como um objeto dessa forma:

{nomeProduto: "Agrião", compraQtd: 1, precoUnitario: 2, _idProduto: [12, 12]}
{nomeProduto: "Agrião&Rúcula", compraQtd: 1, precoUnitario: 2, _idProduto: [12, 31]}

Vocês podem se perguntar porque o _idProduto tem um array do ID do Produto, isso é porque cada pacote de determinado produto contém dois pés do mesmo, a compra no aplicativo não é por quantidade de cada produto e sim por pacote, esses produtos podem ser de um tipo somente (Agrião) ou podem ser mistos (Agrião & Rúcula), por isso eu coloquei em um array os ID's dos respectivos produtos que estão sendo comprados, porque é através desse ID que vou posteriormente debitar da quantidade do estoque dele no banco de dados.

Agora eu vou realmente explicar a minha dificuldade:

Vamos dizer que eu somente adiciono o produto Agrião no carrinho, vou comprar um pacote do mesmo, lembrando que cada pacote contém dois pés do produto, logo, deveria ser debitado dois agriões do estoque da sua quantidade total, que no momento ele possui a quantidade de 9. No meu carrinho, ao finalizar a compra existe o seguinte método:

this.itensDoCarrinho.forEach(pedidoCompra => { //Aqui eu faço um forEach para cada produto que está no meu carrinho.
        console.log("PedidoCompraModel", pedidoCompra);
        pedidoCompra._idProduto.forEach(idProduto => { //Aqui eu faço um forEach para cada ID que está dentro do meu pedidoCompra.
          this.produtoService.getProduto(idProduto) //Aqui eu faço uma requisição para o meu banco através do HttpClient para me trazer informações do produto que estou querendo debitar do estoque.
            .subscribe(produtoDados => {
              console.log("Produto Dados do GetProduto", produtoDados);
              this.produtoService.updateQtdProduto(produtoDados._id, { "quantidade": produtoDados.quantidade - pedidoCompra.compraQtd }) //Aqui é onde eu faço o debito da quantidade comprada relativa ao ID informado e dou um patch atualizando o banco de dados.
                .subscribe(produtoDadosAtualizado => {
                  console.log("Produto Dados do UpdateQtdProduto", produtoDadosAtualizado);
                });
            });
        });
      });

Porém, o que acontece é que ele somente faz o debito de UMA QUANTIDADE do produto em questão, e não de duas quantidades, como eu gostaria que fosse. Eu tenho a impressão que isso tem algo a ver com o assincronismo dos Observables.

Vou colocar abaixo uma foto do console.log da operação acima para vocês entenderem com mais clareza o que aconteceu.

Imagem do Console.log do exemplo citado

Alguém saberia me orientar o do porque isso está acontecendo? O que eu poderia fazer para que fosse debitado corretamente a quantidade do produto de acordo com os ID's que estão informados no PedidoCompraModel?

Desculpe se eu não consegui ser muito claro na minha explicação, qualquer outra informação, por favor, somente me solicitar.

Obrigado pelo tempo de todos!

5 respostas
solução!

Fala ai Dennis, tudo bem? Isso pode estar ocorrendo por N motivos, o que eu gostaria de sugerir, é você criar uma função que vai receber um array de id's e devolver um array de Observable.

Onde para cada item do array será uma chamada para a API, logo, será um Observable.

Feito isso, depois você pode concatená-los em um único Observable e aguardar o processamento dos dois:

// simulando chamada para a API
const updateStock = productId => timer(3000).pipe(mapTo(productId))

// função que converte o id do produto para um Observable
const processProduct = productId => updateStock(productId)

// exemplo de array
const productIds = [1, 2]

// convertando o id do produto para Observable com .map
// e executando todos os Observable em ordem sequencial
// com a função concat
concat(...productIds.map(p => processProduct(p)))
    .subscribe(id => {
        console.log(id)
        console.log(`Produto ${id} processado!!!`)
    })

A ideia seria mais ou menos essa.

Espero ter ajudado.

Olá Matheus!

Muito obrigado pela sua orientação! Consegui uma solução seguindo seus passos e realizando algumas modificações.

Sobre o timer, onde existe o tempo de 100ms, existe alguma forma se não existir esse tempo para execução de cada procedimento?

De qualquer forma, muito obrigado pela sua resposta e sua ajuda, já me ajudou muito com esse problema que estava enfrentando!

let arrayIds: Number[] = [];
      this.itensDoCarrinho.forEach(pedidoCompra => {
          arrayIds.push(...pedidoCompra._idProduto);
      });

      const updateStock = produto => timer(100).pipe(mapTo(produto));

      const processProduct = idProduto => updateStock(idProduto);

      concat(...arrayIds.map(idProduto => processProduct(idProduto)))
        .subscribe(id => {
            this.produtoService.getProduto(id)
              .subscribe(produtoDados => {
                console.log(produtoDados);
                this.produtoService.updateQtdProduto(produtoDados._id, { "quantidade": produtoDados.quantidade - 1 })
                  .subscribe(() => {});
              });
        });

Boa Dennis, fico feliz que tenha dado certo.

Sobre o timer, você não vai precisar dele, a função updateStock estava apenas simulando uma requisição HTTP que demorava três segundos.

No seu caso, troque a função updateStock pela sua função que de fato realiza a chamada para a API.

Falha minha não ter explicado esse ponto.

Espero ter ajudado.

Ajudou muito!

Obrigado pelo seu tempo! Muito agradecido!

Magina, sempre que precisar não deixe de criar suas dúvidas.

Abraços e bons estudos.