1
resposta

Componente select não esta enviando o id na requisição

Olá estou com a seguinte duvida tenho um select no qual eu carrego algumas informações e quero passar o id para ser salvo no banco de dados estou usando uma cardinalidade de 1 para muitos, porém na requisição só esta passando dois campos. esse é o meu HTML

<form id="id" action="/dividas/salvar" method="POST">
                        <label for="id">M</label>
                           <select  form="id" id="categorias.id" name="categorias.id" th:field="${categorias}" class="form-control" aria-label="Default select example">
                              <option  th:each="item : ${categorias}" th:value="${item.id}" selected="selected" th:text="${item.categoria}"></option>
                           </select>
                     </div>
                     <div class="input-group mb-3 col-4">
                         <label for="valor" class="input-group-text ">Valor</label>
                         <input name="valor" type="text" class="form-control" />
                     </div>
                     <div class="input-group mb-3 col-4">
                         <label for="dataVencimento" class="input-group-text ">Data do vencimento</label>
                         <input name="dataVencimento" type="text" class="form-control " />
                     </div>
                     <button type="submit">Salvar</button>
                     </form>

Meu controller:

@GetMapping("formulario")
    public String cadastrarCarteira(Model model,DividasForm body) {
        List<CategoriaDividas> categorias = categoriaRepository.findAll();
        model.addAttribute("categorias", categorias);
        return "CadastrarDividas";
    }

    @PostMapping("salvar")
    public String salvar(@Valid DividasForm body) {
        Dividas divida = body.converter();
        dividasRepository.save(divida);
        return "redirect:/dividas/formulario";
    }

E o meu form:

    private Date dataVencimento;
    private BigDecimal valor;
    private List<CategoriaDividas> categorias;


    public Dividas converter() {
        return new Dividas(dataVencimento,valor,categorias);
    }
1 resposta

Olá, tudo bem? Um dos problemas é que o seu DividasForm espera três parâmetros: dataVencimento, valor e categorias, então no seu form do HTML devem existir campos com esses três nomes especificamente. No seu select, o name está como categorias.id, então não acontece a ligação entre o categorias.id do select do form HTML e o categorias do DividasForm, eles precisam ter o mesmo nome. Uma outra questão complicada é que uma dívida pode ter mais de uma categoria, enquanto o select por padrão só permite escolher uma, mas dá pra mudar isso:

<select id="categorias" name="categorias" th:field="${categorias}" class="form-control" aria-label="Default select example" multiple>
      <option  th:each="item : ${categorias}" th:value="${item.id}" selected="selected" th:text="${item.categoria}"></option>
   </select>

Repare que no fim da linha do select foi colocado a palavra multiple, que passa a permitir que mais de uma opção seja escolhida, para isso é só apertar e segurar a tecla Ctrl do teclado enquanto clicar nas opções, você pode ver mais sobre esse tipo de select no site: https://www.w3schools.com/tags/att_select_multiple.asp. Porém, mesmo com essa alteração provavelmente vai ser preciso mudar a forma como os dados do formulário vão ser recebidos e convertidos para fazer essa parte de várias categorias serem selecionadas ao mesmo tempo funcionar. Lembrando que o valor que será passado pelo select é o id da categoria, e não a categoria em si, como mostra no código abaixo com o th:value recebendo o item.id, então no DividasForm vai ser preciso buscar as categorias por Id no banco de dados para converter corretamente os dados:

<option  th:each="item : ${categorias}" th:value="${item.id}" selected="selected" th:text="${item.categoria}"></option>

Também é possível passar direto a categoria inteira como valor na option, assim fica mais fácil de converter os dados, além de que o próprio banco de dados vai inserir na tabela somente o id das categorias na tabela na forma de chave estrangeira (Foreign Key que faz a relação entre tabelas diferentes), e não todas as informações das categorias, então no fim dá no mesmo:

<option  th:each="item : ${categorias}" th:value="${item}" selected="selected" th:text="${item.categoria}"></option>

Outras opções além do <select multiple> é usar um <input type="checkbox" /> para cada categoria, ou então usar alguma abordagem diferente usando JavaScript para criar uma lista com o ID das categorias no próprio arquivo HTML e depois enviar ela para o formulário para só então poder fazer a conversão para uma lista em Java. Outra opção seria colocar um número máximo de categorias e então ter um select para cada categoria que a dívida pode ter, por exemplo, se cada dívida pudesse ter no máximo três categorias, daria para ter três selects. Com essa última abordagem seria necessário ter três parâmetros diferentes para cada categoria, como categoria1, categoria2 e categoria3, depois no seu DividasForm você colocaria as três categorias em uma lista.

<select id="categoria1" name="categoria1" th:field="${categorias}" class="form-control" aria-label="Default select example">
      <option  th:each="item : ${categorias}" th:value="${item}" selected="selected" th:text="${item.categoria}"></option>
   </select>
   <select id="categoria2" name="categoria2" th:field="${categorias}" class="form-control" aria-label="Default select example">
      <option  th:each="item : ${categorias}" th:value="${item}" selected="selected" th:text="${item.categoria}"></option>
   </select>
   <select id="categoria3" name="categoria3" th:field="${categorias}" class="form-control" aria-label="Default select example">
      <option  th:each="item : ${categorias}" th:value="${item}" selected="selected" th:text="${item.categoria}"></option>
   </select>

Então o DividasForm ficaria assim:

private Date dataVencimento;
private BigDecimal valor;
private CategoriaDividas categoria1;
private CategoriaDividas categoria2;
private CategoriaDividas categoria3


    public Dividas converter() {
        List<CategoriaDividas> categorias = new ArrayList<CategoriaDividas>();
        // Fazer validações e depois adicionar na lista
        categorias.add(categoria1);
        categorias.add(categoria2);
        categorias.add(categoria3);
        return new Dividas(dataVencimento, valor, categorias);
    }

Enfim, é um caso mais complexo e com várias possibilidades, mas espero ter ajudado!