[Tapestry 5.4] Custom 컴포넌트 개발방법.

2013. 11. 19. 15:26 - 에릭투스

이제 Requirejs도 사용할 수 있고, 기본 DOM도 Jquery로 변경하였으니, 사용자 컴포넌트를 만들어 봅시다!!

 커스텀 컴포넌트의 값을 컴포넌트를 구현하는 객체에서 값을 받을 수 있도록 하는게 목표입니다.


기본적은 구조는 화면에서는 TML(HTML 레이아웃)자바스크립트 화면을 꾸미고,

자바와의 연동(태피스트리)AbstractField를 이용해서 만들었습니다.



좀더 자세히 알기위해, 

bootstrap ACE테마에 있던, 한번 Spinner 커스텀 컴포넌트를 구현해보겠습니다!!






1. Custom 컴포넌트(자바) 생성

 : "baseURL.component.*" Spinner 컴포넌트를 만들고, AbstractField를 상속 받습니다. 

   (ex /src/com.nkia.t5first.component.Spinner.java)

public class Spinner extends AbstractField{
  @Override
  protected void processSubmission(String controlName) {
   
  }
}






2. Custom 컴포넌트(TML) 생성.

: "baseURL.component.*" Spinner 컴포넌트를 만들고, AbstractField를 상속 받습니다. 

  (ex /resource/com.nkia.t5first.component.Spinner.tml)

: Spinner의 경우, fuelux의 자바스크립트 컴포넌트를 이용합니다. (http://exacttarget.github.io/fuelux/#spinner)

: 따라서, fuelux의 Spinner컴포넌트의 맞게 레이아웃을 잡아 줍니다.

        
	



*몇가지 Tapestry태그가 나왔지만, 천천히 파악해보겠습니다!!

  1) t:type="any"의 경우, 커스텀 컴포넌트에서 사용합니다. 

     또한, ${clientId}와 ${controlName}은 설정한적이 없지만 어떻게 처리되느냐!! AbstractField에서 자동으로 지정해줍니다.

  

  2) clientId는 화면에 렌더될때, 만약 ID를 지정하지 않았다면, 

     자동으로 ID를 지정해주는데, 이렇게 지정된 ID를 clientId에 저장합니다. 

     (물론 지정했을 경우, 지정된 ID가 clientId에 저장됩니다.)

  

  3) controlName은 해당 컴포넌트의 name 필드를 의미합니다.  

     즉, 이후에 나올 form전송시 request.getParameter로 값을 받을때 사용하므로, 

     이 이름(controlName)을 통해 커스텀 컴포넌트의 실제 값을 전달 받는다고 생각하시면 되겠씁니다.

 







 3. Custom 컴포넌트(Java) 내용 작성.

  : 모두들 다 아시듯이, 태피스트리의 경우, 화면의 관한 렌더처리, 폼전송(이벤트처리) 등을 자바에서 설정합니다.

    따라서, 앞으로 우리가 구현할 기능은 다음과 같습니다.


   1) 화면에 처음 렌더시 fuelux/Spinner 형태로 변환 시켜줄 것.

   2) 이미 값이 지정되어 있다면 값 설정.

   3) 폼 전송시 실제값을 연결하는 매핑작업.



구현해보도록 하겠습니다.


@Import(stylesheet={"classpath:/META-INF/assets/css/fuelux.css","classpath:/META-INF/assets/css/fuelux-responsive.css"})
public class Spinner extends AbstractField{
       
       @Parameter(required=true, autoconnect=true, allowNull=false)
       private Integer value;

       @Inject
       private JavaScriptSupport jss;
	
	@BeginRender
        void beginRender(){
                JSONObject json = new JSONObject();
                     json.put("clientId", clientId);
                
                 if(value==null)
                   json.put("value", 0);
                else
                   json.put("value", this.value);
               
                jss.require("fuelux/SpinnerModule").with(json);
        }
       
       @Override
       protected void processSubmission(String controlName) {
                String result = request.getParameter(controlName);
                if(result == null){
                        this.value = 0;
                       return;
                }
                this.value = Integer.valueOf(result);
       }
}

*@Import를 통해, 컴포넌트 구현에 필요한 css를 로드합니다.


*@Parameter 는 구현시 인자값으로 받겠다는 것을 의미합니다. 변수명을 통해 인자값을 받습니다.


*@Parameter(autoconnect=true) 가 중요한데, 이는 이를 구현할 자바의 변수로 연결된다는 의미입니다. 

 (이후 구현하는 화면에서 다시 한번 설명하겠습니다.)


*@BeginRender는 화면이 렌더 되기전에, 자바스크립트 컴포넌트를 이용해 화면을 꾸며야하기 때문입니다.

JavascriptSupport를 통해 requirejs를 사용하고, /META-INF/modules 밑에서 사용할 모듈을 불러옵니다.


*with()를 통해 Java에서 Javascript로 JSON을 넘겨줄 수 있습니다.




define(["fuelux/spinner"],function($,all){
	return function(spec){
		var _clientId = spec.clientId;
		var _value = spec.value;
		
		jQuery("#"+_clientId+"-input").val(_value);
	}
});

*fuelux/SpinnerModule.js를 구현합니다.


*아래는, 기본 모듈 형식입니다.

define(["사용할 모듈,.."],function(사용할 모듈 변수,..){

    return function(with을 통해 넘긴 인자값..){

//Module 비즈니스 로직..

     }

 });


*폼 전송시 실제값을 연결하는 매핑작업은 이해를 돕기 위해 구현 후 설명하도록 하겠씁니다..

여기까지 진행이 되었다면, 커스텀 컴포넌트는 완료되었습니다. (물론, 중요한 processSubmission은 설명하지 않았지만.)


*일단, 이 컴포넌트를 활용하여 Demo페이지를 만들어 보도록 하겠습니다.






4. 페이지(TML) 생성

 : 컴포넌트와 마찬가지로, "baseURL.page.*" SpinnerDemo를 만듭니다.

  (ex /resource/com.nkia.t5first.page.SpinnerDemo.tml)



Spinner Component


Spinner Value : ${value}


* 앞서 만든 컴포넌트를 사용하기 위해 <t:만든컴포넌트(대소문자구분 없음)>.


* 앞서 만든 컴포넌트 구현에 필요한 인자값으로 Integer value를 지정합니다.







5. 페이지(Java) 구현

  


public class SpinnerDemo {
	@Persist
	@Property
	Integer value;
	
	@SetupRender
	void setupRender(){
		if(value == null)
			value = 1;
	}
}



* 이제 중요한 processSubmission에 대해 설명하겠습니다.


*커스텀 컴포넌트를 구현한 화면에서 form 전송시, 각 컴포넌트의 processSubmission 메소드가 실행됩니다.

이를 통해, request.getParameter(controlName)을 통해, 전달받은 값을 받고, 이를 autoconnect로 연결된 변수에 값을 저장합니다.

(즉, Spinner컴포넌트를 구현한 value 변수에 실제 값이 저장되게 됩니다.)





6. 결론,

 1) 왠만한 컴포넌트는 현재와 같은 방식으로 개발이 가능할 것으로 보입니다. 

    (List로 받아야할 경우, hidden으로 JSON문자열을 통해 값을 주고 받기)

 

 2) 다만, 복잡한 컴포넌트( 트리, Select, 타임라인 등 )의 경우, 어떻게 구현하는 것이 개발하기 편할지 고민해봐야할 듯합니다.

 

 3) Validataion과 같은 기능 추가에 대한 R&D가 필요합니다.


 4) 각각 컴포넌트를 별도로 구현하다 보니, 한 화면에서 다수의 컴포넌트가 나올때 CSS 충돌 가능성이 높습니다. 

    (bootstrap 의존성 문제도요. ex 2.3 or 3.0 )

    이러한 문제가 있을 수 있다보니, 이를 고려해서 컴포넌트 개발에 앞서 필요한 컴포넌트 목록을 정해야하지 않을까 싶습니다.




현재까지, 작업한 컴포넌트는

1. Spinner      예제  : http://nkia-tapestry.herokuapp.com/spinnerdemo


2. Datepicker  예제  : http://nkia-tapestry.herokuapp.com/datefielddemo


3. DateRangepicker 예제 : http://nkia-tapestry.herokuapp.com/datefieldrangedemo


4. 소스 다운로드 및 기타  : http://nkia-tapestry.herokuapp.com



다른 카테고리의 글 목록

Workspace/Web Dev 카테고리의 포스트를 톺아봅니다