In general, I did some test. It turns out that for different situations either you need to use iterStat, or not. Depends on whether the list, Map, Object or something else was transferred.
In this case, the object passed to th: object = "$ {selectedProduct}" is an object that contains only a List <> mapList;
th:field="*{product.productId}" th:field="*{mapList[__${iterStat.index}__].productId}" th:field="${mapList[__${iterStat.index}__].productId}" th:field="${__${product.productId}__}" th:field="${product.productId}" th:field="${product.productId != null} ? ${product.productId} : ${product.enumName}" th:value="*{product.productId}" th:value="${mapList[__${iterStat.index}__].productId}" th:value="*{mapList[__${iterStat.index}__].productId}" th:value="${__#{product.productId}__}" th:value="${product.productId}" th:value="${product.customName != null} ? ${product.productId} : ${product.enumName}"
For me, it was appropriate for the form to display the value, but it could not be changed, this is the option
<input readonly type="text" th:field="*{mapList[__${iterStat.index}__].productId}"/>