View 에 사용된 타임리프 항목이, View 를 호출한..
Controller 의 Model 에 담기지 않았거나
Controller 의 파라미터에 JavaBeans 객체 형태로 전달되지 않았다면,
Whitelabel Error...Could not parse as expression...
...Exception evaluating SpringEL expression...
다만, 값이 null 이라도 에러가 발생하지 않음.
Controller 의 자바빈즈 파라미터에 ObjectA objA 가 사용되었다면,
Controller 안에선 'objA' 로 사용되고..
View 에선 'objA' 가 아니라 'objectA ' 형태로, 클래스명의 첫번째 문자를 소문자로 바꾸어 사용.
Controller 의 자바빈즈 파라미터에 @ModelAttribute("objB") ObjectA objA 였다면,
Controller 안에선 'objA' 로 사용되고..
View 에선 'objA' 나 'objectA' 가 아니라 'objB' 형태로 객체를 받아서 사용.
Controller 의 파라미터에 Model model 이 사용되었다면,
ObjectA objA = new ObjectA() 일 때..
model 에 자바빈즈 파라미터를 담을 때 model.addAttribute("obj", objA) 였다면.
View 에선 'objectA' 나 'objA' 가 아니라 'obj' 형태로 객체를 받아서 사용.
model 에 자바빈즈 파라미터를 담을 때 model.addAttribute(objA) 였다면,
View 에선 'objectA' 가 아니라 'objA' 형태로 객체를 받아서 사용.
이는 model.addAttribute("objA",objA) 와 동일.
<form ...>
<input type="text" ...><!-- type="text" 는 생략 가능 -->
<input type="hidden" ...><!-- text 와 동일하게 작동 -->
<input type="password" th:field="*{passwordA}"><!-- value 는 설정되지 않음 -->
<textarea ...></textarea>
<input type="checkbox" ...><!-- 하나 켜고 끄고. 여럿 선택 -->
<input type="radio" ...><!-- 여럿 중 하나 선택 -->
<select ...>...</select><!-- 여럿 중 하나와 여럿으로 선택 -->
</form>
#1/4. (form 이 담긴 view 를 호출하는) 최초의 컨트롤러에서..
public String methodA(Model model){
Object obj = new Object(); // 신규 등록 폼이라면, model.addAttribute(obj) 으로 끝냄
obj.setTextA("Text1");
obj.setPasswordA("Password1");
obj.setTextareaA("Textarea1");
obj.setCheckboxA(true);//체크박스 1개면 true/false
List<String> cl = new ArrayList<String>();
cl.add("cVal1");
cl.add("cVal2");
obj.setCheckboxB(cl);//체크박스 여럿 중, 1개 또는 복수개 선택
obj.setRadioA("rVal1");//라디오 여럿 중, 1개 선택
obj.setSelectA("sVal1");//select 여럿 중, 1개 선택
List sl = new ArrayList();
sl.add("sVal1");
sl.add("sVal2");
obj.setSelectB(sl);// select 여럿 중, 1개 또는 복수개 선택
List<ObjA> checkboxes = new ArrayList<ObjA>();
checkboxes.add(new ObjA("cVal1","cTxt1");
checkboxes.add(new ObjA("cVal2","cTxt2");
obj.setCheckboxes(checkboxes);
List<ObjA> radios = new ArrayList<ObjA>();
radios.add(new ObjA("rVal1","rTxt1");
radios.add(new ObjA("rVal2","rTxt2");
obj.setRadios(radios);
List<ObjA> selects = new ArrayList<ObjA>();
selects.add(new ObjA("sVal1","sTxt1"));
selects.add(new ObjA("sVal2","sTxt2"));
obj.setSelect(selects);
model.addAttribute(obj); // addAttribute("obj", obj); #2 에서 사용되는 명칭으로 변경 가능
return "..."
}
#2/4. form 문 뷰(화면)에서..
<form th:action="@{...}" th:object="${obj}"><!-- 컨트롤러에서 받아온 자바빈즈명 -->
<input type="text" th:field="*{textA}"><!-- 여기서의 *{textA} 는 ${obj.textA} 와 동일 -->
</form>
<input type="text" id="textA" name="textA" th:value="*{textA}"> 을 걸쳐서
<input type="text" id="textA" name="textA" value="Text1"> 으로 자동 변환.
<strong th:if="${#fields.hasErrors('textA')" th:errors="*{textA}"></strong><!-- blank 에러 메시지 -->
#3/4. form 문 다음의 컨트롤러(아마도 DB 처리)에서..
public String methodB(@Validated ObjectA obj, BindingResult bd){
if(bd.hasErrors()) return "...";
...
return "..."
}
#4/4. 마지막 뷰(화면)에서..
<div th:object="${obj}">
<p>- text : <span th:text="*{textA}"</p>
<p>[[*{textA}]]</p>
<p>[(*{textA})]</p>
</div>
공통으로..
@Getter @Setter @ToString
publc class Obj{
@NotBlank
private String texA;
private String passwordA;
private String textareaA;
private boolean checkboxA;//단일
private List<String> checkboxB;//다중 다선
private String radioA;//다중 1선
private String selectA;//다중 1선
private List<String> selectB;//다중 다선
private List<ObjA> checkboxes; // 화면 구성용
private List<ObjA> radios; // 화면 구성용
private List<ObjA> selects; // 화면 구성용
}
@Getter @Setter @ToString
public class ObjA{
private String val;
private String txt;
public ObjA(String val, String txt){
this.val = val;
this.txt= txt;
}
}
#2 단계에서.. th:field
<input type="password" th:field="*{passwordA}"> 이면,
<input type="password" id="passwordA" name="passwordA" value=""> 로 자동 변환.
value 속성에 값이 설정되지 않음.
type="password" 일 때 th:value="*{passwordA}" 설정해도 value="" 임.
자바스크립트에 설정해서 value 를 채울 수 있음.
#4 단계에선..
<p>- password : <span th:text="*{passwordA}"</p>
<p>[[*{passwordA}]]</p>
<p>[(*{passwordA})]</p>
#2 단계에서.. th:field
<textarea th:field="*{textareaA}"></textarea> 이면,<!-- \n 개행 -->
<textarea id="textareaA" name="textareaA">[[*{textareaA}]]</texarea> 를 거쳐
<textarea id="textareaA" name="textareaA">Textarea1</textare> 로 자동변환.
< 와 > 와 엔터('\n')를 그대로 인식하므로, .html() 이 아니라 .text() 처리한 것과 유사.
#4 단계에선..
<p>- textarea : <span th:text="*{textareaA}"</p>
<p>[[*{textareaA}]]</p>
<p>[(*{textareaA})]</p>
#2 단계에서.. th:field, 단일항목..
<input type="checkbox" th:field="*{checkboxA}"> 이면,<!-- th:value 를 설정해도 동일 결과 -->
<input type="checkbox" id="checkboxA1" name="checkboxA" value="[[*{checkboxA}]]" checked="checked"> 거쳐
<input type="checkbox" id="checkboxA1" name="checkboxA" value="true" checked="checked">
<input type="hidden" name="_checkboxA" value="on"> 이 input.checkbox 에 추가 됨.
단일항목 체크박스인 경우, 체크시 on, 언체크시 off 인데..
off 인 경우 값을 전달해 주지 않아서 input.hidden 을 설정하는 것.
checkbox 와 radio 타입은 id 에 넘버링이 자동으로 붙음.
radio 타입에는 input.hidden 이 추가되지 않음.
단일 체크박스이면 #1에서 넘어온 *{checkboxA} 에 해당하는 값은, true 나 false 이어야 함.
boolean 형이 아니려면, input.checkbox 태그에 th:value="*{checkboxA}" 속성 추가.
*{checkboxA} 값이 false 였다면, input.checkbox 에 checked="checked" 속성이 추가되지 않음.
<label th:for="${#ids.prev('checkboxA')}" th:text="*{checkboxA}"></label> 이면,
<label for="checkboxA1">[[*{checkboxA}]]</label> 를 거쳐
#4 단계에선..
<p>- checkbox : <span th:text="*{checkboxA}"</p><!-- true 나 false 형태 -->
<p>[[*{checkboxA}]]</p><!-- true 나 false 형태 -->
<p>[(*{checkboxA})]</p><!-- true 나 false 형태 -->
#2 단계에서.. th:field, 다중다선(여러 항목 중 여럿 선택)..
<span th:each="c : *{checkboxes}">
<input type="checkbox" th:field="*{checkboxB}" th:value="${c.val}"><!-- th:value 기술하지 않으면 에러 -->
<label th:for="${#ids.prev('checkboxB')}" th:text="${c.txt}"></label><!-- th:text 기술하지 않으면 체크박스만 표시 -->
</span>
이면,
<span>
<input type="checkbox" id="checkboxB1" name="checkboxB" value="cVal1"> 와
<input type="hidden" name="_checkboxB" value="on">
<label for="checkboxesB1">cTxt1</label>
</span>
<span>
<input type="checkbox" id="checkboxB2" name="checkboxB" value="cVal2" checked="checked"> 와
<input type="hidden" name="_checkboxB" value="on">
<label for="checkboxesB2">cTxt2</label>
</span>
checkboxes 가 사전에 정의되지 않았고, 해당 form 뷰에서 정의한다면,
th:each 대신에 일일이 기술해 줄 수 있다. (th:field 가 하는 일은 id, name, value, checked, input.hidden)
<input type="checkbox" th:field="*{checkboxB}" th:value="cVal1">
<label th:for="${#ids.prev('checkboxB')}">cTxt1</lable>
<input type="checkbox" th:field="*{checkboxB}" th:value="cVal2">
<label th:for="${#ids.prev('checkboxB')}">cTxt2</lable>
#1에서 넘어온 *{checkboxes} 가 Map 형태면 checked 속성이 켜지는데(체크된 상태),
List 형태면 checked 속성이 꺼진다(체크되지 않은 상태).
#4 단계에선..
<p>- checkbox : <span th:text="*{checkboxB}"</p><!-- [cVal1, cVal2] 형태 -->
<p>[[*{checkboxB}]]</p><!-- [cVal1, cVal2] 형태 -->
<p>[(*{checkboxB})]</p><!-- [cVal1, cVal2] 형태 -->
<p th:each="c : *{checkboxB}"><!-- <p><span>cVal1</span></p><p><span>cVal2</snap></p> -->
<span th:text="${c}"></span>
</p>
#2 단계에서.. th:field, 다중 1선(여러 항목 중 단일 선택)..
항목을 선택할 것이냐 말 것이냐를 표현하려면, checkbox 의 단일항목 사용.
<span th:each="r : *{radios}">
<input type="radio" th:field="*{radioA}" th:value="${r.val}"><!-- th:value 기술하지 않으면 에러 -->
<label th:for="${#ids.prev('radioA')}" th:text="${r.txt}"></label><!-- th:text 기술하지 않으면 라디오만 표시 -->
</span>
이면,
<span>
<input type="radio" id="radioA1" name="radioA" value="rVal1" checked="checked">
<label for="radioA1">rTxt1</label>
</span>
<span>
<input type="radio" id="radioA2" name="radioA" value="rVal2">
<label for="radioA2">rTxt2</label>
</span>
#1에서 List 형태로 넘어오고 checked 속성이 꺼진다(체크되지 않은 상태)..
<span th:each="r, index : *{radios}">
<input type="radio" th:field="*{radioA}" th:value="${r.val}">
<label th:for="'radioA'+${index.count}" th:text="${r.txt}"></label>
</span>
th:for 속성값에 "|radioA${index.count}|" 가능.
#4 단계에선..
<p>- radio : <span th:text="*{radioA}"</p><!-- rVal1 -->
<p>[[*{radioA}]]</p><!-- rVal1 -->
<p>[(*{radioA})]</p><!-- rVal1 -->
<p th:each="r : *{radioA}"><!-- <p><span>rVal1</span></p> -->
<span th:text="${r}"></span>
</p>
#2 단계에서.. th:field, 다중 1선(여러 항목 중 단일 선택)..
<select th:field="*{selectA}">
<option value="">기본내용</option>
<option th:each="s : *{selects}" th:value="${s.val}" th:text="${s.txt}"></option>
</select>
이면,
<select id="selectA" name="selectA">
<option value="">기본내용</option>
<option value="sVal1" selected="selected">sTxt1</option>
<option value="sVal2">sTxt2</option>
</select>
#4 단계에선..
<p>- select : <span th:text="*{selectA}"</p><!-- sVal1 -->
<p>[[*{selectA}]]</p><!-- sVal1 -->
<p>[(*{selectA})]</p><!-- sVal1 -->
<p th:each="s : *{selectA}"><!-- <p><span>sVal1</span></p> -->
<span th:text="${s}"></span>
</p>
#2 단계에서.. th:field, 다중 다선(여러 항목 중 여럿 선택)..
<select th:field="*{selectB}" multiple>
<option value="">기본내용</option>
<option th:each="s : *{selects}" th:value="${s.val}" th:text="${s.txt}"></option>
</select>
이면,
<input type="hidden" name="_selectB" value="1"><!-- input.checkbox 처럼 input.hidden 이 추가. select 는 하나만 -->
<select id="selectB" name="selectB" multiple>
<option value="">기본내용</option>
<option value="sVal1" selected="selected">sTxt1</option>
<option value="sVal2" selected="selected">sTxt2</option>
</select>
#4 단계에선..
<p>- select : <span th:text="*{selectB}"</p><!-- [sVal1, sVal2] -->
<p>[[*{selectB}]]</p><!-- [sVal1, sVal2] -->
<p>[(*{selectB})]</p><!-- [sVal1, sVal2] -->
<p th:each="s : *{selectB}"><!-- <p><span>sVal1</span></p><p<<span>sVal2</span></p> -->
<span th:text="${s}"></span>
</p>
'Server Oriented > Spring' 카테고리의 다른 글
스프링 JPA, 평범한 게시판 구현 DB 테이블 1개 (2) | 2022.12.03 |
---|---|
스프링, 세션과 쿠키 (0) | 2022.11.02 |
스프링 build.gradle 수정후 반드시 실행 (0) | 2022.10.13 |
스프링 Controller Step 2, request 파라미터 수용(매핑시) (0) | 2022.09.30 |
스프링 Controller Step 3, response 할당(View, RESTful) (1) | 2022.09.30 |