1

Using Spring MVC and Thymeleaf, I am constructing an html view with some javascript inside.

Within the page, th:each is used with iteration values to give a set of buttons a unique HTML id.

<td th:each="optionValue,iterStat : ${someObject.optionValues}">
  <button class="btn btn-default" th:id="${'optionBtn_' + (iterStat.count - 1)}" th:text="${optionValue.toString()}" />
</td>

My problem comes when trying to generate javascript that will use a jQuery reference to each button id.

In 'another' view resolution language, I would use the code:

<% for(var i = 0; i < someObject.optionValues.length; i++) { %>
    $('#optionBtn_<%- i %>').on("click", function() {
        doSomething('<%= someObject.optionValues[i] %>');
    });
<% } %>

(the above may not be 100% syntactically correct, but I hope you get the idea - what I'm trying to do is possible using the above style)

but in Thymeleaf, whilst I understand that I can use

th:inline="javascript"

to reference individual model items, I can't see how I can use a look to generate multiple jQuery function call definitions within a script block.

Any ideas? (I may be approaching the problem completely wrong, so am open to new ideas on that front too)

2 Answers 2

2

I'll accept your invitation for new ideas and put on the table my methodology of approaching similar cases.
Clearly the problem is mostly about the communication between back-end and javascript data. The data that the javascript function needs as an argument in this case. Since html5's introduction of data attributes and the improved jQuery's efficiency with them in late versions, you may expose whatever back end data you want as attributes starting with "data-". This is valid according to html5. In jQuery you can access them as if they were jQuery data by translating the html naming convention to camelCase after reducing the initial "data-" pefix. (e.g. data-my-attribute = myAttribute). You can either access them in the original html convention (e.g. my-attribute). Its a matter of preference.

Then your html can be much cleaner:

<td th:each="optionValue,iterStat : ${someObject.optionValues}">
  <button class="btn btn-default" th:id="${'optionBtn_' + (iterStat.count - 1)}" th:text="${optionValue.toString()}" th:attr="data-my-func-attr=${optionValue}"/>
</td>

You can bind your event handlers then as:

$("button.btn").on("click", function() {
      buttonPressed($(this).data("my-func-attr"))
}

or similar.

This way you also keep your code cleaner and separate from the markup which is one principle of Unobtrusive JS

Sign up to request clarification or add additional context in comments.

Comments

1

So I have made a workaround.

Because multiple elements can exist in a page I simply create an outer loop of the tag and create one for each button. The details in the method call will have the same index/text mapping as in the iteration to create the buttons in the first place, although the text creation was a bit tricky:

<script th:each="optionValue,iterStat : ${someObject.optionValues}">
   $([[${'#optionBtn_' + (iterStat.cont - 1)}]]).on("click", function() {
      buttonPressed([[${iterStat.count - 1}]], [[${estimateOption.toString()}]]);
   });
</script>

Not the smallest once the code is generated, but it works.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.