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

Passar uma lista da view para o controller

Boa noite, estou migrando do VRaptor para o Spring e o list box que enviava a minha lista para o meu controller parou de enviar. Eu tenho uma classe agendamento que possui uma lista de produtos e servicos, na página em si tudo aparece bonito, eu escolho os produtos e os serviços que eu quero que aquele agendamento tenha, mas quando dou salvar para criar meu objeto Agendamento todos os atributos vem preenchido menos as duas listas. Gostaria de ajuda para conseguir fazer o bind da lista com o meu objeto agendamento, vou postar o código aqui.

Agendamento:

package br.com.poggers.barbearia.model;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;

import org.springframework.format.annotation.DateTimeFormat;

@Entity
public class Agendamento {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    @ManyToMany
    private List<Servico> servicos;
    @ManyToMany
    private List<Produto> produtos;
    @DateTimeFormat(pattern="dd/MM/yyyy")
    private LocalDate data;
    @DateTimeFormat(pattern="HH:mm")
    private LocalTime hora;
    @ManyToOne
    private Barbeiro barbeiro;
    public Integer getId(){
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public List<Servico> getServicos() {
        return servicos;
    }
    public void setServicos(List<Servico> servicos) {
        this.servicos = servicos;
    }
    public List<Produto> getProdutos() {
        return produtos;
    }
    public void setProdutos(List<Produto> produtos) {
        this.produtos = produtos;
    }
    public Barbeiro getBarbeiro() {
        return barbeiro;
    }
    public void setBarbeiro(Barbeiro barbeiro) {
        this.barbeiro = barbeiro;
    }
    public LocalDate getData(){
        return this.data;
    }
    public void setData(LocalDate data) {
        this.data = data;
    }
    public LocalTime getHora() {
        return hora;
    }
    public void setHora(LocalTime hora) {
        this.hora = hora;
    }
    public String getDataFormatada() {
        return data.format(DateTimeFormatter.ofPattern("dd/MM/yy"));
    }
    public BigDecimal getTotal(){
        BigDecimal total = new BigDecimal("0");
        for(Produto produto : produtos){
            total.add(produto.getPreco());
        }
        for(Servico servico : servicos ){
            total.add(servico.getPreco());
        }
        return total;    
    }
    public LocalTime getTermino(){
        Duration duracaoTotal = Duration.ZERO;
        for(Servico servico : servicos){
            duracaoTotal.plus(servico.getDuracao());
        }

        return this.hora.plus(duracaoTotal);
    }    
}

Serviço:

package br.com.poggers.barbearia.model;

import java.math.BigDecimal;
import java.time.Duration;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Servico{

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    private String nome;
    private BigDecimal preco;
    private Duration duracao;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getNome() {
        return nome;
    }
    public void setNome(String nome) {
        this.nome = nome;
    }
    public BigDecimal getPreco() {
        return preco;
    }
    public void setPreco(BigDecimal preco) {
        this.preco = preco;
    }
    public Duration getDuracao() {
        return duracao;
    }
    public void setDuracao(Long minutos) {
        this.duracao = Duration.ofMinutes(minutos);
    }




}

Produto

package br.com.poggers.barbearia.model;

import java.math.BigDecimal;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Produto {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    private String nome;
    private BigDecimal preco;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getNome() {
        return nome;
    }
    public void setNome(String nome) {
        this.nome = nome;
    }
    public BigDecimal getPreco() {
        return preco;
    }
    public void setPreco(BigDecimal preco) {
        this.preco = preco;
    }


}

AgendamentoController:

package br.com.poggers.barbearia.controllers;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import br.com.poggers.barbearia.model.Agendamento;
import br.com.poggers.barbearia.model.Servico;
import br.com.poggers.barbearia.repository.AgendamentoRepository;
import br.com.poggers.barbearia.repository.BarbeiroRepository;
import br.com.poggers.barbearia.repository.ProdutoRepository;
import br.com.poggers.barbearia.repository.ServicoRepository;

@RequestMapping("/agendamento")
@Controller
public class AgendamentoController {

    private AgendamentoRepository agendamentoRepository;
    private ServicoRepository servicoRepository;
    private ProdutoRepository produtoRepository;
    private BarbeiroRepository barbeiroRepository;



    @Autowired
    public AgendamentoController(AgendamentoRepository agendamentoRepository, ServicoRepository servicoRepository, ProdutoRepository produtoRepository
            , BarbeiroRepository barbeiroRepository) {    
        this.agendamentoRepository = agendamentoRepository;
        this.servicoRepository = servicoRepository;
        this.produtoRepository = produtoRepository;
        this.barbeiroRepository = barbeiroRepository;

    }

    @Deprecated
    public AgendamentoController() {

    }

    @GetMapping("agenda/{id}")
    public ModelAndView lista(@PathVariable Integer id) {
        ModelAndView mav = new ModelAndView("agendamento/lista");
        mav.addObject("agendamentos", agendamentoRepository.findByBarbeiro(barbeiroRepository.findOne(id)));
        mav.addObject("barbeiroId", id);
        return mav;
    }

    @GetMapping("listaBarbeiros")
    public ModelAndView listaBarbeiros(){
        ModelAndView mav = new ModelAndView("agendamento/listaBarbeiros");
        mav.addObject("barbeiros", barbeiroRepository.findAll());
        return mav;
    }

    @GetMapping("form/{id}")
    public ModelAndView form(@PathVariable Integer id){
        ModelAndView mav = new ModelAndView("/agendamento/form");
        mav.addObject("listaServicos", servicoRepository.findAll());
        mav.addObject("listaProdutos", produtoRepository.findAll());
        mav.addObject("barbeiroId", id);
        return mav;
    }

    @PostMapping("adiciona")
    public String adiciona(Agendamento agendamento, RedirectAttributes redirectAttributes) {
        agendamentoRepository.save(agendamento);
        redirectAttributes.addFlashAttribute("sucesso", "Agendamento adicionado com sucesso!");
        return "redirect:listaBarbeiros";
    }

    @PostMapping("remove")
    public String remove(Integer agendamentoId, RedirectAttributes redirectAttributes) {
        redirectAttributes.addFlashAttribute("sucesso", "Agendamento removido com sucesso!");
        agendamentoRepository.delete(agendamentoRepository.findOne(agendamentoId));
        return "redirect:lista";
    }
    @GetMapping("teste")
    public void teste(@PathVariable List<Servico> servicos){
        System.out.println("OI");

    }

}

Formulário de agendamento

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib tagdir="/WEB-INF/tags/template" prefix="template"%>


<template:admin>
        <jsp:attribute name="extraStyles">
        <link href="<c:url value='/assets/css/plugins/datapicker/datepicker3.css'/>" rel="stylesheet">
        <link href="<c:url value='/assets/css/plugins/clockpicker/clockpicker.css'/>" rel="stylesheet">
        <link href="<c:url value='/assets/css/select2.min.css'/>" rel="stylesheet">
    </jsp:attribute>

    <jsp:attribute name="extraScripts">
        <script src="<c:url value='/assets/js/plugins/validate/jquery.validate.min.js'/>"></script>
        <script src="<c:url value='/assets/js/plugins/mask/jquery.mask.min.js'/>"></script>
        <script src="<c:url value='/assets/js/plugins/datapicker/bootstrap-datepicker.js'/>"></script>
        <script src="<c:url value='/assets/js/plugins/clockpicker/clockpicker.js'/>"></script>
        <script src="<c:url value='/assets/js/form/consulta.js'/>"></script>
        <script src="<c:url value='/assets/js/select2.min.js'/>"></script>
        <script type="text/javascript">
            $(".js-example-basic-multiple").select2();
        </script>
    </jsp:attribute>

    <jsp:body>
        <div class="row">
            <div class="col-lg-12">
            <div class="ibox-title">
            <c:if test="${agendamento.id == null}">
                <h1>Agendar</h1>
            </c:if>
            <c:if test="${agendamento.id != null}">
                <h1>Editar Agendamento</h1>                
            </c:if>
            </div>
            <div class="ibox-content">
            <form role="form" class="form-horizontal" action="<c:url value="/agendamento/adiciona"/>" method="post">

    <div class="form-group" id="data_1">
    <label class="col-sm-2 control-label">Data</label>
    <div class="col-sm-10">
        <div class="input-group date">
            <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
            <input type="text" name="data" data-mask="99/99/99" placeholder="__/__/__" class="form-control" value="${agendamento.data}">
        </div>
    </div>
</div>

<div class="form-group">
    <label class="col-sm-2 control-label">Hora</label>
    <div class="col-sm-10">
        <div class="input-group clockpicker" data-autoclose="true">
            <span class="input-group-addon">
                <span class="fa fa-clock-o"></span>
            </span>
            <input type="text" class="form-control" name="hora" value="${agendamento.hora}">
        </div>
    </div>
</div>

<div class="form-group">
    <label class="col-sm-2 control-label">Serviços</label>
    <div class="col-sm-10">
        <select id="select-servico" class="js-example-basic-multiple" multiple="multiple" style="width: 250px;" name='servicos.id'>
            <c:forEach items='${listaServicos}' var='servico'>
                <option value='${servico.id}'
                <c:forEach items='${agendamento.servicos}' var='meuServico' >
                    <c:if test = "${servico.id == meuServico.id}">
                        selected
                    </c:if>
                </c:forEach>
                >${servico.nome}</option>
            </c:forEach>
        </select>
    </div>
</div>

<div class="form-group">
    <label class="col-sm-2 control-label">Produtos</label>
    <div class="col-sm-10">
        <select id="select-produto" class="js-example-basic-multiple" multiple="multiple" style="width: 250px;" name='produtos.id'>
            <c:forEach items='${listaProdutos}' var='produto'>
                <option value='${produto.id}'
                <c:forEach items='${agendamento.produtos}' var='meuProduto' >
                    <c:if test = "${produto.id == meuProduto.id}">
                        selected
                    </c:if>
                </c:forEach>
                >${produto.nome}</option>
            </c:forEach>
        </select>
    </div>
</div>
<input type="hidden" name="id" value="${agendamento.id}">
<input type="hidden" name="barbeiro.id" value="${barbeiroId}">

                <div class="hr-line-dashed"></div>
                            <div class="form-group">
                                <div class="col-sm-4 col-sm-offset-2">
                                <c:if test="${agendamento.id == null}">
                                    <button class="btn btn-primary" type="submit">Adicionar</button>
                                </c:if>
                                <c:if test="${agendamento.id != null}">
                                    <button class="btn btn-primary" type="submit">Editar</button>            
                                </c:if>
                                </div>
                            </div>
                            <div class="col-md-4">


                                </div>
                </form>

            </div>             

            </div>            
        </div>
    </jsp:body>
</template:admin>
9 respostas

Olá.

Cola aqui também o código da AgendamentoRepository, por favor.

Boa noite Bruno, o código do AgendamentoRepository está logo abaixo, os outros repositories que estão injetados no agendamento controller só extendem o CrudRepository, sem nenhum método extra.

AgendamentoRepository

package br.com.poggers.barbearia.repository;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

import br.com.poggers.barbearia.model.Agendamento;
import br.com.poggers.barbearia.model.Barbeiro;

public interface AgendamentoRepository extends CrudRepository<Agendamento, Integer> {


    List<Agendamento> findByBarbeiro(Barbeiro barbeiro);

}

Ah sim, desculpe, essa classe de nada tem a ver.

O mapeamento da model que o método adiciona recebe depende das propriedades recebidas pelo POST derem match com as da model. Isso ocorre com a maioria, com exceção das listas. Renomeie seus selects para servicos e produtos. Se ainda assim não funcionar, deixe as propriedades names assim também. Veja se agora as propriedades são preenchidas.

Desculpe, eu n consegui entender. No Spring para que ocorra o bind eu preciso passar apenas o nome do atributo no name do campo do form né? Não entendi a alteração que eu tenho q fazer nas listas

solução!

Mário, não tenho experiência com Spring, por isso não consigo dar certeza. Estou tentando ajudar com a experiência que trago de outras tecnologias.

Veja bem, sua model de Agendamento tem listas de produtos e serviços. O que um objeto Agendamento vai passar para o repository salvar no banco seria apenas um serviço e um produto, certo? Posso estar enganado, mas talvez a model Agendamento deveria contar com um objeto Produto e outro Servico, não lista deles. Pra popular as listas do form você usa os métodos de listar.

Não sei se o sono está me atrapalhando, mas eu testaria tirar de listas na model de Agendamento (se minha lógica fizer sentido) e mudar os ids dos selects da página que representam as listas. Deixe eles mapeados com o nome da respectiva propriedade. Tipo, muda a lista de produtos pra Produto produto; e deixa o id do select produto. Vê se o bind funciona.

Para um só funciona, mas ainda não consigo bindar a lista

Então, o que você alterou que funcionou? Mostra como tá a classe agora.

E me diga uma coisa. Produto e Serviço são obrigatórios no agendamento? Ou pode escolher só um deles?

Boa noite Bruno, ambos são opcionais. Após muito tapa na cara consegui! Usando as taglibs do spring consegui fazer o bind da lista. A sacada é que o spring precisa do objeto vazio para fazer o bind então eu enviei o objeto vazio para o formulário e com as taglibs do Spring fiz meu select2. Muito obrigado pela sua ajuda, vou postar a parte do código que eu alterei:

AgendamentoController

    @GetMapping("form/{id}")
    public ModelAndView form(@PathVariable Integer id){
        ModelAndView mav = new ModelAndView("/agendamento/form");
        mav.addObject("listaServicos", servicoRepository.findAll());
        mav.addObject("listaProdutos", produtoRepository.findAll());
        mav.addObject("barbeiroId", id);
        mav.addObject("agendamento", new Agendamento());
        return mav;
    }

Formulário de Agendamento

<form:form servletRelativeAction="/agendamento/adiciona" class="form-horizontal" role="form" method="post" modelAttribute="agendamento">

    <div class="form-group" id="data_1">
    <label class="col-sm-2 control-label">Data</label>
    <div class="col-sm-10">
        <div class="input-group date">
            <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
            <input type="text" name="data" data-mask="99/99/99" placeholder="__/__/__" class="form-control" value="${agendamento.data}">
        </div>
    </div>
</div>

<div class="form-group">
    <label class="col-sm-2 control-label">Hora</label>
    <div class="col-sm-10">
        <div class="input-group clockpicker" data-autoclose="true">
            <span class="input-group-addon">
                <span class="fa fa-clock-o"></span>
            </span>
            <input type="text" class="form-control" name="hora" value="${agendamento.hora}">
        </div>
    </div>
</div>

<div class="form-group">
    <label class="col-sm-2 control-label">Serviços</label>
    <div class="col-sm-10">
<form:select class="js-example-basic-multiple" path="servicos" multiple="true" items="${listaServicos}" itemLabel="nome" itemValue="id" style="width:600px"/>
    </div>
</div>


<div class="form-group">
    <label class="col-sm-2 control-label">Produtos</label>
    <div class="col-sm-10">
<form:select class="js-example-basic-multiple" path="produtos" multiple="true" items="${listaProdutos}" itemLabel="nome" itemValue="id" style="width:600px"/>
    </div>
</div>

<input type="hidden" name="id" value="${agendamento.id}">
<input type="hidden" name="barbeiro.id" value="${barbeiroId}">

                <div class="hr-line-dashed"></div>
                            <div class="form-group">
                                <div class="col-sm-4 col-sm-offset-2">
                                <c:if test="${agendamento.id == null}">
                                    <button class="btn btn-primary" type="submit">Adicionar</button>
                                </c:if>
                                <c:if test="${agendamento.id != null}">
                                    <button class="btn btn-primary" type="submit">Editar</button>            
                                </c:if>
                                </div>
                            </div>
                            <div class="col-md-4">


                                </div>
                </form:form>

Opa cara, bacana! Que bom que chegou lá e que produzimos com a discussão.

Obrigado por compartilhar!

Abraço!