Search code examples
springthymeleaf

How do I dynamically generate this table in the form?


I'm passing the attribute into the model and making sure it's not null...

Controller:

@GetMapping(Mappings.MAIN) // AliasFor @RequestMapping(method = RequestMethod.GET)
    public String getMainView(Model model,
                              @ModelAttribute(AttributeNames.VIEW_RECIPE) ViewRecipe recipe){

        log.info(recipe.toString());

        if(recipe==null){
            recipe = new ViewRecipe();
            recipe.setIngredientList(new ArrayList<>());

        }
        model.addAttribute(AttributeNames.VIEW_RECIPE, recipe);


        return ViewNames.MAIN_VIEW;
    }

Utility classes:

public class ViewNames {
    public static final String HOME = "home";

public class AttributeNames { 
    public static final String VIEW_RECIPE="view-recipe";

public class Mappings {
    public static final String HOME ="home";

Thymeleaf template:

<form id="get-recalculated-recipe" action="#" method="post">
    <table>
        <tr><th>Quantity</th>
            <th>Unit</th>
            <th>Ingredient Name</th>
            <th>Multiplier</th>

        </tr>
        <tr th:each="ing : ${view-recipe.ingredientList}">
            <td>
            <input type="text" th:field="*{ing.quantity}"/>
            </td>
            <td>
                <input type="text" th:field="*{ing.unit}"/>
            </td>
            <td>
                <input type="text" th:field="*{ing.ingredientName}"/>
            </td>
            <td>
                <input type="text" th:field="*{ing.ingredientName}"/>
            </td>
        </tr>
    </table>
</form>

Solution

  • First off, you are not making sure the ingredient list is not null. Think of the case you call your controller and recipe is not null, but the ingredient list attribute of recipe is. It will be sent as null to your view. You can change it for example to:

    if(recipe==null){
            recipe = new ViewRecipe();
            recipe.setIngredientList(new ArrayList<>());
    }else if(recipe.getIngredientList() == null){
            recipe.setIngredientList(new ArrayList<>());
    }
    model.addAttribute(AttributeNames.VIEW_RECIPE, recipe);
    

    Secondly, you are using selection expressions (*{...}) in your th:fields, which is good, but you need to have a command object defined in your form too and reference it from the inputs. You can do this adding the command object to your form like this:

    <form id="get-recalculated-recipe" th:object="${view-recipe}" action="#" method="post">
    

    and changing your th:fields to reference it using an iterator so the input information gets submited when you post the form. Also adding th:value to actually get the current value of the attributes when the page loads:

    <tr th:each="ing, iter : ${view-recipe.ingredientList}">
        <td>
        <input type="text" th:value="${ing.quantity}" th:field="*{ingredientList[__${iter.index}__].quantity}"/>
        </td>
        <td>
            <input type="text" th:value="${ing.unit}" th:field="*{ingredientList[__${iter.index}__].unit}"/>
        </td>
        <td>
            <input type="text" th:value="${ing.ingredientName}" th:field="*{ingredientList[__${iter.index}__].ingredientName}"/>
        </td>
    </tr>
    

    I suggest you have a look at https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html , it will help you in the future.