객체에 open이라는 속성을 하나 추가했다.
체크박스는,
checked 상태에서는 open=true 을 반환하고,
unchecked 상태에서는 open=false를 반환하는 것이 대신, 아무것도 반환하지를 않는다.
그래서 open=null로 인식된다.
사실은 checked 상태에서는 open=on, unchecked 상태에서는 아무것도 반환하지 않는다. (null)
SpringBoot가 on을 true로 바꿔줘서, open=true 값을 받는 것이다.
물론 if(open==true) , else ~~ 이렇게 코딩할수도 있지만 귀찮기도 하고, 나중에 객체를 수정할 때는 아예 생성될때 open 속성이 존재하지 않았기 때문에 문제가 생길수도 있다.
따라서 html에서 히든 필드를 생성해 준다.
<div class="form-check">
<input type="checkbox" id="open" name="open" class="form-check-input">
<input type="hidden" name="_open" value="on"/><!-- 히든 필드 -->
<label for="open" class="form-check-label">판매 오픈</label>
</div>
히든필드는 name에 체크박스의 name 앞에 언더바_
를 추가해 준다. ( open → _open )
그렇게 되면 checked 상태에서는 open=on , _open=on
을 둘 다 반환해서 SpringBoot가 open=true로 변환하고, _open은 없애버린다.
또한, unchecked 상태에서는 open=on
은 반환하지 않고, _open=on
만을 반환해서,
SpringBoot가 open=false로 변환해준다.
이 과정에서 타임리프를 이용하면 조금 더 단순화 할 수 있다.
th:object - th:field 를 사용하여 위의 예제를 바꿔준다.
(해당 <div>
태그의 상위태그에 th:object="${item}"
가 포함되어 있다.)
<input>
태그에 th:field="*{open}"
를 추가해줘서 id, name 태그를 대체한다.
<div class="form-check">
<input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
<label for="open" class="form-check-label">판매 오픈</label>
</div>
아래는 해당 페이지에서 ‘페이지 소스보기’ 한 결과이다.
th:field
기능을 쓰면, 체크박스 사용 시 타임리프가 히든필드를 자동으로 생성해 준다.
<input type="checkbox" id="open" class="form-check-input" name="open" value="true">
<input type="hidden" name="_open" value="on"/>
하나의 공통된 클래스의 속성들 중 선택을 하는 다중 체크박스를 만들어 보자.
우선, 해당 기능을 사용하는 모든 메소드에 regions라는 이름의 Map을 추가해준다.
그러나 아래처럼 똑같은 것을 반복해서 사용하는것은 좋지 않다.
@GetMapping("/add")
public String addForm(Model model){
model.addAttribute("item",new Item());
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL","서울");
regions.put("BUSAN","부산");
regions.put("JEJU","제주");
return "form/addForm";
}
메소드 레벨에서 @ModelAttribute
를 사용할 수 있다.
@ModelAttribute("regions")
public Map<String,String> regions(){
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL","서울");
regions.put("BUSAN","부산");
regions.put("JEJU","제주");
return regions;
}
컨트롤러에 위와 같은 메소드를 만들면, 해당 컨트롤러를 요청할 때, regions에서 반환한 값이
자동으로 model에 담기게 된다.
즉, 해당 컨트롤러 아래에 모든 메소드에서 세가지의 지역이 담긴 regions가 model에 담긴다.
물론, item 객체에 regions 속성이 있어야 한다.
public class Item { //생략 private List<String> regions; //생략 }
( <div>
태그의 상위 태그에 th:object=”${item}”
가 적용되어 있다. )
<div th:each="region : ${regions}" class="form-check form-check-inline">
<input type="checkbox"
th:field="*{regions}" th:value="${region.key}"
class="form-check-input">
<label th:for="${#ids.prev('regions')}" th:text="${region.value}"
class="form-check-label">서울</label>
</div>
th:each="region : ${regions}"
: item의 regions에서 값을 하나씩 꺼내서 region에 담는다.
*{regions}
는 ${item.regions}
와 같다. )
th:value="${region.key}"
: region의 key가 th:value값이 된다.
th:for="${#ids.prev('regions')}"
: 동적으로 id를 생성시킨다.
위의 반복문을 통해 웹페이지에서 아래와 같은 html이 생성된다.
<div class="form-check form-check-inline">
<input type="checkbox" value="SEOUL" class="form-check-input" id="regions1" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="regions1" class="form-check-label">서울</label>
</div>
<div class="form-check form-check-inline">
<input ="checkbox" value="BUSAN" class="form-check-input" id="regions2" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="regions2" class="form-check-label">부산</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" value="JEJU" class="form-check-input" id="regions3" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="regions3" class="form-check-label">제주</label>
</div>
라디오버튼에서 사용하기 위해 Item 클래스에 private ItemType itemType;
속성을 추가한다.
라디오 버튼은 여러 선택지중에 하나만 선택할 때 사용할 수있다.
BOOK, FOOD, ETC 세개의 속성을 가진 ENUM을 이용해보자.
public enum ItemType {
BOOK("도서"), FOOD("음식"), ETC("기타");
private final String description;
//생성자, getter 생략
}
컨트롤러에 @ModelAttribute를 메소드 레벨에 적용해서 사용할 수 있다.
@ModelAttribute("itemTypes")
public ItemType[] itemTypes(){
ItemType[] values = ItemType.values();
return ItemType.values();
}
<div>
<div>상품 종류</div>
<div th:each="type : ${itemTypes}" class="form-check form-check-inline">
<input type="radio" th:field="*{itemType}" th:value="${type.name()}"
class="form-check-input">
<label th:for="${#ids.prev('itemType')}"
th:text="${type.description}"
class="form-check-label">
BOOK
</label>
</div>
</div>
셀렉트 박스도 라디오버튼과 마찬가지로 여러개의 선택지중 하나만 선택할 수 있다.
셀렉트 박스에서 사용하기 위해 Item 클래스에 private String deliveryCode;
속성을 추가한다.
메소드 레벨에서 @ModelAttribute
를 사용하여, deliverycode list를 만들어주자.
@ModelAttribute("deliveryCodes")
public List<DeliveryCode> deliveryCodes(){
List<DeliveryCode> deliveryCodes = new ArrayList<>();
deliveryCodes.add(new DeliveryCode("FAST", "빠른 배송"));
deliveryCodes.add(new DeliveryCode("NORMAL", "일반 배송"));
deliveryCodes.add(new DeliveryCode("SLOW", "느린 배송"));
return deliveryCodes;
}
<div>
<div>배송 방식</div>
<select th:field="*{deliveryCode}" class="form-select">
<option value="">==배송 방식 선택==</option>
<option th:each="deliveryCode : ${deliveryCodes}"
th:value="${deliveryCode.code}"
th:text="${deliveryCode.displayName}">FAST</option>
</select>
</div>
(참고)김영한님 인프런 Spring MVC-2
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2