如何添加 html with thymeleaf 并在单击按钮时将对象或其字段绑定到 html?

How to add html with thymeleaf and bind object or its fields to that html on button click?

这很简单 java class 有一个字符串列表,例如:

public class Apple {
    private List<String> listOfStrings;
    // getters setters
}

html 百里香叶:

<form method="post" th:object="${apple}"> // apple is set through Model.addAttribute(.., ..) in Controller
    <input type="text" th:each="str, iter : *{listOfStrings} th:field="*{listOfStrings[__${iter.index}__]}">
    <button id="add" type="button" onclick="add()>Add</button>
    <button id="remove" type="button" onclick="remove()">Remove</button>
    <input type="submit" value="Save">
<form>

java脚本添加和删除新输入:

const newInputTextString="<input type=\"text\" th:field="?">" // th:field won't be evaluated/bound to apples listOfStrings index

function add() {
    const xhttp = new XMLHttpRequest();
    xhttp.onload = function() {
        document.getElementById("add").insertAdjacentHTML("beforebegin", newInputTextString);
    }
    xhttp.open("GET", window.location.href);
    xhttp.send();
}

function remove() {
    // TODO
}

到目前为止,我只是通过 java 脚本添加 html,但我不能在其中包含 th: 标签,因为这些标签不会被评估并绑定到特定对象或字段。

预期功能:

换句话说,我想让用户能够使用 html 按钮在该苹果对象的 listOfStrings 中删除和添加字符串。

随时纠正我的英文

th:field 本质上只是为您提供元素的 ID 和 NAME 属性。您不能通过 JS 添加 th:field,但您可以通过手动添加适当的 ID 和 NAME 属性来模拟其行为。话虽如此,您应该对 HTML 和 JS 代码进行一些细微的更改。

您应该始终记录当前的字符串总数,以便调整您的字符串索引。为此,我为所有字符串输入创建了一个 div 包装器,以便我们可以在 JS 中访问它。之后我们只读取其中的元素数量并从中创建一个新索引。

你可以看看下面的实现:

function createInput(index) {
  var input = document.createElement('input');
  input.setAttribute('type', 'text');
  input.setAttribute('id', `listOfStrings[${index}]`);
  input.setAttribute('name', `listOfStrings[${index}]`);
  input.setAttribute('placeholder', `listOfStrings[${index}]`);
  input.setAttribute('data-index', index);
  input.setAttribute('data-input', '');
  return input;
}

function addString() {
  var inputsWrapper = document.querySelector('[data-inputs]');
  var inputs = inputsWrapper.querySelectorAll('[data-input]');
  var newIndex = inputs.length;
  var newInput = createInput(newIndex);
  inputsWrapper.append(newInput);
}
<form method="post" th:object="${apple}">
  <div style="display: flex; flex-direction: column; max-width: 300px" data-inputs>
    <input 
       type="text"
       placeholder="listOfStrings[0]"
       th:placeholder="${'listOfStrings[__${iter.index}__]'}"
       th:each="str, iter : *{listOfStrings}"
       th:attr="data-index=${__${iter.index}__}" 
       th:field="*{listOfStrings[__${iter.index}__]}"
       data-input />
  </div>
  <button id="add" type="button" onclick="addString()">Add</button>
  <button id="remove" type="button" onclick="remove()">Remove</button>
  <input type="submit" value="Save">
<form>