0

This is simple java class that has a list of strings for example:

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

html with thymeleaf:

<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>

javascript add and remove new input:

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
}

So far I am only adding html through javascript, but I can not include th: tags there since these tags wont be evaluated and bind to specific object or fields.

Expected functionality:

  • on click of remove button, last input of type text that is bound to specific index of listOfStrings is removed.
  • on click of add button, new input of type text is added and bound to next index of listOfStrings.

In other words I want to make user able to remove and add strings in listOfStrings of that apple object with html buttons.

Feel free to correct my English

Wortig
  • 963
  • 2
  • 11
  • 37

1 Answers1

1

th:field essentially just gives you an ID and a NAME attribute on an element. You cannot add a th:field through JS but you can emulate its behavior by manually adding the appropriate ID and NAME attributes. With that said you should make some slight changes in your HTML and JS code.

You should always have a record of the current total number of strings so you can adjust your string index. For that I've created a div wrapper around all string inputs so we can access it in JS. Afterwards we just read the number of elements inside of it and create a new index out of it.

You can look the implementation below:

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>
BrunoT
  • 414
  • 4
  • 9
  • `${index}` is not evaluated. Change to: `...[' + index + ']'`... – Wortig Apr 19 '22 at 13:05
  • I used string interpolation with backtics ( ` ) instead of regular ticks ( ' ). See docs at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals . – BrunoT Apr 19 '22 at 14:17
  • Nice! Did not notice... Is there a performance difference using these? – Wortig Apr 19 '22 at 15:36
  • You might want to check this thread: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation I used it because I find it cleaner that way :) – BrunoT Apr 19 '22 at 16:14