Why thymeleaf <intput type="hidden"> does not work?

Viewed 45

I'm creating a simple Spring Boot application that displays the db tables on the web page.Currently I'm stuck at making CRUD operations for the first table, particulary the Edit operation. I created three html files which are the templates: index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Welcome to My Application</title>
    <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}"/>
</head>
<body>
<div class="container-fluid text-center">
    <h1>Welcome to my Application!</h1>
    <a class="h2" th:href="@{/states}">Manage States</a>
</div>
</body>
</html>

states.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Welcome to My Application</title>
    <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}"/>
</head>
<body>
<div class="container-fluid text-center">
    <div><h2>Manage States</h2></div>
    <div class="m-2">
        <a class="h3" th:href="@{/states/new}">Add New State</a>
    </div>
    <div th:if="${message}" class="alert alert-success">
        [[${message}]]
    </div>
    <div>
        <table class="table table-bordered">
            <thead class="thead-dark">
            <tr>
                <th>ID</th>
                <th>Official State Name</th>
                <th>Official Language</th>
                <th>State Currency</th>
                <th></th>
            </tr>
            </thead>
            <tbody>
            <th:block th:each="state : ${listStates}">
                <tr>
                    <td>[[${state.idState}]]</td>
                    <td>[[${state.officialStateName}]]</td>
                    <td>[[${state.officialLanguage}]]</td>
                    <td>[[${state.stateCurrency}]]</td>
                    <td>
                        <a class="h4 mr-3" th:href="@{'/states/edit/' +${state.idState}}">Edit</a>
                        <a class="h4" th:href="@{'/states/delete/' +${state.idState}}">Delete</a>
                    </td>
                </tr>
            </th:block>
            </tbody>
        </table>
    </div>
</div>
</body>
</html>

states_form.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>[[${pageTitle}]]</title>
    <link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}"/>
</head>
<body>
<div class="container-fluid">
    <div class="text-center"><h2>[[${pageTitle}]]</h2></div>

    <form th:action="@{/states/save}" method="post" th:object="${state}"
        style="max-width: 500px; margin: 0 auto;">
        <input type="hidden" th:field="*{idState}">
        <div class="border border-secondary rounded p-3">
            <div class="form-group row">
                <label class="col-sm-4 col-form-label">Official State Name</label>
                <div class="col-sm-8">
                    <input type="text" th:field="*{officialStateName}" class="form-control" required minlength="4" maxlength="40"/>
                </div>
            </div>
            <div class="form-group row">
                <label class="col-sm-4 col-form-label">Official Language</label>
                <div class="col-sm-8">
                    <input type="text" th:field="*{officialLanguage}" class="form-control" required minlength="4" maxlength="40"/>
                </div>
            </div>
            <div class="form-group row">
                <label class="col-sm-4 col-form-label">State Currency</label>
                <div class="col-sm-8">
                    <input type="text" th:field="*{stateCurrency}" class="form-control" required minlength="4" maxlength="40"/>
                </div>
            </div>
            <div class="text-center">
                <button type="submit" class="btn btn-primary m-2">Save</button>
                <button type="button" class="btn btn-secondary m-2" onclick="cancelForm()">Cancel</button>
            </div>
        </div>

    </form>

</div>
<script type="text/javascript">
    function cancelForm(){
        window.location = "[[@{/states}]]"
    }
</script>
</body>
</html>

I have a POJO class named State:

import jakarta.persistence.*;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "state")
public class State implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id_state")
private Integer idState;

@Column(name = "official_state_name")
private String officialStateName;

@Column(name = "official_language")
private String officialLanguage;

@Column(name = "state_currency")
private String stateCurrency;

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(name = "state_war",
        joinColumns = {
                @JoinColumn(name = "id_state", referencedColumnName = "id_state",
                        nullable = false, updatable = false)},
        inverseJoinColumns = {
                @JoinColumn(name = "id_war", referencedColumnName = "id_war",
                        nullable = false, updatable = false)})
private Set<War> wars = new HashSet<>();

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(name = "state_event",
        joinColumns = {
                @JoinColumn(name = "id_state", referencedColumnName = "id_state",
                        nullable = false, updatable = false)},
        inverseJoinColumns = {
                @JoinColumn(name = "id_event", referencedColumnName = "id_event",
                        nullable = false, updatable = false)})
private Set<Event> events = new HashSet<>();

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(name = "state_capital",
        joinColumns = {
                @JoinColumn(name = "id_state", referencedColumnName = "id_state",
                        nullable = false, updatable = false)},
        inverseJoinColumns = {
                @JoinColumn(name = "id_capital", referencedColumnName = "id_capital",
                        nullable = false, updatable = false)})
private Set<Capital> capitals = new HashSet<>();

@OneToMany(mappedBy = "state", fetch = FetchType.LAZY,
        cascade = CascadeType.ALL)
private Set<Figure> figures;

@OneToMany(mappedBy = "state", fetch = FetchType.LAZY,
        cascade = CascadeType.ALL)
private Set<Governor> governors;

@OneToMany(mappedBy = "state", fetch = FetchType.LAZY,
        cascade = CascadeType.ALL)
private Set<Statistics> statistics;

public Integer getIdState() {return this.idState;}

public String getOfficialStateName() {
    return this.officialStateName;
}

public void setOfficialStateName(String officialStateName) {
    this.officialStateName = officialStateName;
}

public String getOfficialLanguage() {
    return this.officialLanguage;
}

public void setOfficialLanguage(String officialLanguage) {
    this.officialLanguage = officialLanguage;
}

public String getStateCurrency() {
    return this.stateCurrency;
}

public void setStateCurrency(String stateCurrency) {
    this.stateCurrency = stateCurrency;
}
}

And Spring boot components:

Repository:

import com.historicalreferencebook.historicalreferencebook.domain.State;
import org.springframework.data.repository.CrudRepository;

public interface StateRepository extends CrudRepository<State, Integer> {
}

Service:

   import com.historicalreferencebook.historicalreferencebook.domain.State;
   import 
  com.historicalreferencebook.historicalreferencebook.exceptions.StateNotFoundException;
import com.historicalreferencebook.historicalreferencebook.repository.StateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class StateService {
@Autowired
private StateRepository stateRepository;

public List<State> findAll(){
    return (List<State>) stateRepository.findAll();
}

public void save(State state) {
    stateRepository.save(state);
}

public State get(Integer id) throws StateNotFoundException {
    Optional<State> result= stateRepository.findById(id);
    if(result.isPresent()){
        System.out.println(id);
        return result.get();
    }
    throw new StateNotFoundException("Could not find any state with id" + id);
}
}

Main contoller:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MainController {

@GetMapping("")
public String showHomePage(){
    return "index";
}
}

State controller:

import com.historicalreferencebook.historicalreferencebook.domain.State;
import 
com.historicalreferencebook.historicalreferencebook.exceptions.StateNotFoundException;
import com.historicalreferencebook.historicalreferencebook.service.StateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.servlet.mvc.support.RedirectAttributes;

import java.util.List;

@Controller
public class StateController {

@Autowired
private StateService service;

@GetMapping("/states")
public String showAllStates(Model model){
    List<State>  listStates = service.findAll();
    model.addAttribute("listStates", listStates);
    return "states";
}

@GetMapping("/states/new")
public String showNewForm(Model model){
    model.addAttribute("state", new State());
    model.addAttribute("pageTitle", "Add New State");
    return "state_form";
}

@PostMapping("/states/save")
public String saveState (State state, RedirectAttributes redirectAttributes){
    service.save(state);
    redirectAttributes.addFlashAttribute("message", "The state has been saved successfully!");
    return "redirect:/states";
}

@GetMapping("/states/edit/{id}")
public String showEditForm(@PathVariable("id") Integer id, Model model, RedirectAttributes redirectAttributes){
    try {
        State state = service.get(id);
        model.addAttribute("state", state);
        model.addAttribute("pageTitle", "Edit State (ID: " + id + ")");
        return "state_form";
    } catch (StateNotFoundException e) {
        redirectAttributes.addFlashAttribute("message", "The state has been saved successfully!");
        return "redirect:/states";
    }
}
}

This is how the web page looks like: enter image description here

So i did not actually made delete button work, but my problem is, when i try to edit the existing state the db creates a new entity instead of editing the existing one. When trying to edit: enter image description here

enter image description here

It should replace the content of the field "State Currency" but instead it generates a new "Poland". I'm guessing it must be something to do with the idState that I'm missing in the

    <input type="hidden" th:field="*{idState}"> 

As the database I use PostgreSQL. Bootstrap version 4.3.1

0 Answers
Related