Spring MVCのModel#addAttributeにCollectionを指定した時の属性名は・・・

Spring MVCでは、View(ThymeleafやJSPなど)とデータを連携する場合は、`org.springframework.ui.Model`インタフェースのメソッド(`addAttribute`)を使います。

> **Note:** リダイレクト時は？
>
> ちなみに・・・リダイレクト時は`org.springframework.web.servlet.mvc.support.RedirectAttributes`インタフェースのメソッド(`addFlashAttribute`)を使います。

その際に、`addAttribute`や`addFlashAttribute`に`Collection`オブジェクトを指定すると、どんな属性名になるか知っていますか？
答えは・・・`Collection`に格納されているオブジェクトのクラス名から生成した属性名に「`List`」を付与した値が、最終的な属性名になります。

# `Model#addAttribute`の使用

コードで見た方が誤解がないので、コードをみてみましょう :sweat_smile: 

```java
@RequestMapping(method = RequestMethod.GET)
public String list(@Validated ApiResponseSearchForm form, BindingResult result, Model model) {
    if (result.hasErrors()) {
        return "response/list";
    }
    List<ApiResponse> apiResponses =
        apiResponseService.findAll(form.getPath(), form.getMethod(), form.getDescription());
    model.addAttribute(apiResponses); // ← ここでModelに追加！！
    return "response/list";
}
```

View(本投稿ではThymeleaf)からは、以下のようにデータを参照します。

```html
<tr th:each="apiResponse : ${apiResponseList}"> <!-- ← ここでModelに追加したオブジェクトを参照！！ -->
    <td>
        <div class="checkbox">
            <label>
                <input type="checkbox" name="ids" th:value="${apiResponse.id}"/>
            </label>
        </div>
    </td>
    <td>
        <a class="btn btn-default" th:href="@{/manager/responses/{id}(id=${apiResponse.id})}">
            <span class="glyphicon glyphicon-edit"></span>
            <span th:text="#{action.edit}"></span>
        </a>
    </td>
    <td th:text="${apiResponse.path}"></td>
    <td th:text="${apiResponse.method}"></td>
    <td th:text="${apiResponse.dataKey}"></td>
    <td th:text="${apiResponse.statusCode}"></td>
    <td th:text="${apiResponse.description}"></td>
</tr>
```

上の例だと、`List`に格納されているクラスが`ApiResponse`なので、`apiResponseList`が属性名になります。なお、デフォルトの属性名が気に入らない :confused: 場合は、明示的に属性名を指定することもできます！！

```java
List<ApiResponse> apiResponses =
    apiResponseService.findAll(form.getPath(), form.getMethod(), form.getDescription());
model.addAttribute("apiResponses", apiResponses); // ← 属性名を明示的に指定
```

> **Note:** 配列は？
>
> 配列も`Collection`と同じ動作になります。

ちなみに・・・
クラス名から生成されるベースの属性名は、「[`org.springframework.core.Conventions#getVariableName(Object)`](https://github.com/spring-projects/spring-framework/blob/v4.3.1.RELEASE/spring-core/src/main/java/org/springframework/core/Conventions.java#L78-L102)」を使って生成しているので、細い仕様はソースコード読んでください！！ :wink: 

# `@ModelAttriute`の使用

また、`Model`にオブジェクトを格納する手段として`@ModelAttribute`を付与したメソッドがサポートされており、こちらも同様の仕様で属性名が生成されます。こちらは、「[`org.springframework.core.Conventions#getVariableNameForReturnType(Method, Class<?>, Object)`](https://github.com/spring-projects/spring-framework/blob/v4.3.1.RELEASE/spring-core/src/main/java/org/springframework/core/Conventions.java#L168-L208)」を使って生成しています。

```java
@ModelAttribute
public List<ApiResponse> getApiResponseList() {
    return apiResponseService.findAll(form.getPath(), form.getMethod(), form.getDescription());
}
```

こちらもデフォルトの属性名が気に入らない :confused: 場合は、明示的に属性名を指定することもできます！！

```java
@ModelAttribute("apiResponses") // ← 属性名を明示的に指定
public List<ApiResponse> getApiResponseList() {
    return apiResponseService.findAll(form.getPath(), form.getMethod(), form.getDescription());
}
```

# 要素が空の時の動作 (追記)

`Model#addAttribute`や`RedirectAttributes#addFlashAttribute`に指定した`Collection`の要素が空の場合は、デフォルトの属性名を生成することができないため`Model`の中にオブジェクトが格納されません。つまり、Viewからアクセスすると`null`として扱われる点を意識しておきましょう。Thymeleafであれば、`#lists.isEmpty`メソッドを使えば`null`セーフな空判定が可能です。

```html
<button type="button" class="btn btn-default" name="delete" data-toggle="modal"
        data-target="#deleteModal" disabled="disabled" 
        th:if="${not #lists.isEmpty(apiResponseList)}"> <!-- ← これ -->
    <span class="glyphicon glyphicon-trash"></span> 削除
</button>
```

# まとめ
`Collection`の要素が空の時の動きはちょっと注意が必要ですが・・・デフォルトの属性名を使おう！！