본문 바로가기

Server Oriented/Spring

스프링 뷰, 타임리프 form 과 결과

 

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="textAname="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>