블로그 이미지

카테고리

데꾸벅 (194)
Publisher (39)
Scripter (97)
Programmer (1)
Designer (30)
Integrator (18)
Pattern Searcher (4)
News (2)
강좌 및 번역 (3)

최근에 올라온 글

최근에 달린 댓글


본 강의자료는 복제, 배포가 불가능합니다. by techbug





Internet Explorer 8




Reference Books
  Designing Interfaces  : UI Pattern과 관련된 내용을 익히기에 좋습니다. [관련사이트]

 Ajax Programming 기초부터 중급까지  : 기초가 부족하신 분들이 보시기에 좋습니다. (예제: JSP)

 Ajax Design Patterns : 설계패턴에 대한 전반적인 내용 (예제:php)

 Ajax in Action : Ajax 설계패턴에 대한 전반적인 내용

 Ajax Patterns and Best Practices : 예제파일이 좋습니다.

 Ajax Security : 로그인 보안 및 알고리즘에 대해 설명

 Ajax design pattern for web2.0 : 설계패턴의 전반적인 내용

 Pro Javascipt Design Patterns : 자바스크립트 설계원칙에 대한 내용

 Foundation of Ajax : 테스팅툴이나 ajax기초에 대해서 설명합니다.
 
 
 
 
 
 
 
웹표준 및 jQuery 관련 추천책 
 웹표준교과서

 CSS 마스터전략

 실용예제로 배우는 웹표준

 웹2.0을 이끄는 방탄웹

 웹표준 Ajax DOM 스크립팅

jQuery1.3 작고 강력한 자바스크립트 라이브러리

jQuery in Action (프로그래밍 jQuery)

















'강좌 및 번역' 카테고리의 다른 글

HTML5  (10) 2010.03.02
프로젝트 관리자가 알아야할 97가지 사실  (1) 2009.12.08
Post by 넥스트리소프트 데꾸벅(techbug)
, |

location.href =====> protocol :// hostname : port / pathname ? search # hash





히스토리기능으로 고민하던 어느분이 메일로 도움을 청해와서 찾아보던중
Ajax History기능구현이 가능하다는걸 알았다.
관련글을 찾아보던중 번역문서를 찾아 포스팅 한다.

원문 :  [ajax-history-libraries],
번역문 : [한빛미디어]
국내관련글 : juDe's blog(친절히도 설명해 놓았다.. ^^) [1],[2],[3],[4]

관련 파일 : dhtmlHistory.js



===========================================================
여기서는 AJAX 응용 프로그램에 즐겨찾기와 뒤로 이동을 지원하는 오픈 소스 자바스크립트 라이브러리를 소개할 것이다. 이 글을 마지막까지 보게된다면 구글맵스지메일과 같은 곳들조차 제대로 지원하지 못했던 웹 사이트에서의 즐겨찾기, 앞으로 이동, 뒤로 이동과 같은 AJAX의 문제들을 해결할 수 있게 될 것이다.

"AJAX: 즐겨찾기와 뒤로 이동을 다루는 방법"에서는 에 이잭스(AJAX) 응용 프로그램들이 현재 당면하고 있는 즐겨찾기, 뒤로 이동과 같은 중요한 문제들을 설명하고, 이 문제를 해결하기 위한 오픈 소스 프레임워크인 RSH(Really Simple History) 라이브러리를 소개하고, 실제 예제들을 몇 가지 소개할 것이다.

([1]역주: 영어사전에서 AJAX는 에이잭스로 소개되며, 그리스의 신화의 아이아스나 오딧세이의 아이아스를 의미한다. sys-con의 AJAX & Rich Internet Applications 프레젠테이션에서도 그 발음을 확인할 수 있다. 네덜란드 축구팀의 이름을 딴 아약스, 또는 아작스 등으로 불리기도 한다. 한편, 세제 제조회사인 Ajax Industries는 "에이잭스社"로 옮긴다.)

여 기서 소개할 프레임워크의 주요 부분은 두 개로 나뉘어진다. 첫째는 클라이언트의 정보를 갖고 있는 임시 세션 캐시를 사용하기 위해 숨겨진 HTML 폼을 사용한다. 이 캐시는 어떤 지점으로 이동하거나 페이지를 떠나는 경우에도 사용할 수 있다. 둘째는 A 태그와 숨겨진 iframe을 사용해서 브라우저 히스토리를 가로챈 다음 브라우저 히스토리 이벤트를 기록하고, 뒤로 이동과 앞으로 이동 버튼에 연결한다. 이 두 가지 기능은 모두 개발시에 쉽게 이용할 수 있도록 간단한 자바스크립트 라이브러리로 되어있다.

문제

즐 겨찾기와 뒤로 이동은 여러 페이지로 구성된 전형적인 웹 응용 프로그램에서 매우 유용하다. 사용자가 웹 사이트를 항해할 때, 브라우저의 주소 창은 새로운 URL로 업데이트되며, 나중에 이 주소를 다시 방문하기 위해 이메일에 붙여넣거나 즐겨찾기에 추가할 수 있다. 뒤로 이동, 앞으로 이동 버튼들도 올바르게 동작하며, 사용자는 방문했던 페이지들을 자유로이 이동할 수 있다.

그 러나, 에이잭스 응용 프로그램은 웹 페이지 하나에서 동작하는 복잡한 프로그램이기 때문에 예외적인 응용 프로그램이다. 브라우저는 이런 기능을 위해 만들어지지 않았다. 웹 응용 프로그램들이 마우스 클릭을 할 때마다 완전히 새로운 페이지를 가져올 수 있게 되었어도 브라우저는 과거에 사로잡혀 있다.

지메일과 같은 AJAX 소프트웨어에서 사용자가 기능을 선택하고, 응용 프로그램의 상태를 바꾸어도 브라우저의 주소창은 항상 같은 위치만 갖고 있기 때문에 응용 프로그램의 특정 화면에 대해 즐겨찾기를 축가하는 것은 불가능하다. 뿐만 아니라, 사용자가 이전 작업을 원상태로 되돌리기위해 뒤로 이동 버튼을 클릭하면 브라우저가 응용 프로그램의 웹 페이지를 떠나기 때문에 사용자들은 놀라게 된다.

해결책

오픈소스 RSH(Really Simple History) 프레임워크는 이러한 문제를 해결하고, 에이잭스 응용 프로그램에서 즐겨찾기, 뒤로 이동, 앞으로 이동을 제어할 수 있게 해준다. RSH는 현재 베타 버전이며, 파이어폭스 1.0, 넷스케이프 7 이상, 인터넷 익스플로러 6 이상의 버전에서 동작하며, 사파리는 현재 지원되지 않는다.(이에 대한 설명은 나의 웹 로그에서 Coding in Paradise: Safari: No DHTML History Possible를 참고하기 바란다.)

몇몇 에이잭스 프레임워크는 현재 즐겨찾기와 히스토리 문제를 해결하려 하고 있지만 이들 프레임워크는 구현 방식 때문에 몇가지 중요한 버그들을 해결하지 못하고 있다.(보다 자세한 사항은 "Coding in Paradise: AJAX History Libraries"를 참고한다) 게다가, 많은 에이잭스 [2]히스토리 프레임워크는 BackbaseDojo 같은 보다 큰 라이브러리안에 통합된 형태로 되어 있다. 이들 프레임워크는 AJAX 응용 프로그램에 대해서 완전히 다른 프로그래밍 모델을 제공하고 있으며, 개발자가 히스토리 기능을 사용하기 위해서는 완전히 새로운 접근방법을 사용할 것을 강요받게 된다.

([2]역주: 히스토리(History)는 "기록" 또는 "방문기록"으로 옮기지만 대부분이 히스토리에 익숙하기 때문에 번역하지 않았다)

반면에, RSH는 기존 AJAX 시스템에 포함시킬 수 있는 간단한 모듈로 되어 있다. 뿐만 아니라, RSH 라이브러리는 다른 히스토리 프레임워크들이 갖고 있는 버그들을 피하기 위한 기술들을 사용하고 있다.

RSH 히스토리 프레임워크는 자바스크립트 클래스 DhtmlHistory, HistoryStorage로 구성되어있다.

DhtmlHistory 클래스는 에이잭스 응용 프로그램을 위한 히스토리 추상화를 제공한다. 즉, 에이잭스 페이지는 브라우저에 새 위치와 그에 대한 히스토리 데이터를 지정한 히스토리 이벤트를 추가하는 add()를 호출한다. DhtmlHistory 클래스는 A 태그에 #new-location과 같은 해쉬를 사용해서 브라우저의 현재 URL을 업데이트하고, 히스토리 데이터와 새 URL을 연결한다. 에이잭스 응용 프로그램 자체를 히스토리 리스너(listener)로 등록하고, 사용자가 뒤로 이동, 앞으로 이동 버튼을 사용해서 이동할 때 마다 히스토리 이벤트는 add() 호출로 저장했던 히스토리 데이터와 브라우저의 위치를 제공한다.

개발자는 HistoryStorage 클래스를 사용해서 어떤 크기의 히스토리 데이터라도 저장할 수 있다. 일반 페이지에서 사용자가 새로운 웹 사이트로 이동할 때 브라우저는 이전 웹 페이지의 모든 응용 프로그램을 제거하고, 응용 프로그램과 자바스크립트의 상태를 정리한다. 따라서, 사용자가 뒤로 이동 버튼을 사용하여 이전 페이지로 돌아가면 모든 데이터가 사라진다. HistoryStorage 클래스는 put(), get(), hasKey()와 같은 간단한 해쉬 테이블 메서드를 제공하는 API를 통해서 이 문제를 해결한다. 사용자가 웹 페이지를 떠난 다음에도 개발자는 이들 메서드를 사용해서 데이터를 저장할 수 있다. 사용자가 뒤로 이동 버튼을 사용해서 돌아오면 HistoryStorage 클래스를 사용해서 데이터에 접근할 수 있다. 이 기능은 사용자가 웹 페이지를 떠난 후에도 브라우저가 폼 양식에 있는 값들을 자동으로 저장한다는 사실을 이용한 것으로, 숨겨진 폼 필드를 사용해서 구현됐다.

예제

이제 바로 간단한 예제로 살펴보자.

먼저, RSH 프레임워크를 사용할 페이지는 dhtmlHistory.js 스크립트를 반드시 인클루드해야 한다.

<!-- Load the Really Simple
       History framework -->
<script type="text/javascript"
            src="../../framework/dhtmlHistory.js">
</script>


DHTML 히스토리 응용 프로그램은 에이잭스 웹 페이지가 있는 디렉터리에 blank.html 파일을 포함시켜야 한다. 이 파일은 RSH 프레임워크에 함께 들어있으며, 인터넷 익스플로러에서 사용한다. 여담이지만, 인터넷 익스플로러는 히스토리 변경 사항을 추적하고 추가하기 위해 "숨겨진 iframe"을 사용한다. 히스토리 기능이 제대로 동작하기 위해서는 실제 위치를 가리키는 것이 필요한데, 이를 위해 비어있는 iframe을 사용한다. 그래서 이름도 blank.html이다.

RSH 프레임워크는 브라우저 히스토리를 조작하기 위한 진입점으로 dhtmlHistory라는 전역 객체를 생성한다. dhtmlHistory로 작업하는 첫번째 단계는 페이지 로딩이 끝난 후에 이 객체를 초기화하는 것이다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();

다음으로, 개발자는 히스토리 변경 이벤트를 [3]구 독하기 위해 dhtmlHistory.addListener()를 사용해야 한다. 이 메서드는 DHTML 히스토리가 변경될 때, 페이지의 새로운 위치, 히스토리 변경 이벤트와 관련된 히스토리 데이터를 인자로 받는 콜백 함수를 인자로 받는다.

([3]역주: 이벤트 구독보다는 이벤트 감시가 더 적절한 표현이지만 원문이 subscribe이므로 구독으로 옮김)

window.onload = initialize;
     
function initialize() {
// DHTML 히스토리 프레임워크를 초기화한다
   dhtmlHistory.initialize();
   
// HTML 히스토리 변경 이벤트를 구독한다
   dhtmlHistory.addListener(historyChange);

historyChange() 메서드는 사용자가 새 위치로 이동한 후의 newLocation과 히스토리 변경 이벤트와 관계된 historyData를 인자로 받는 함수로 이해하기 쉽다.

/** 히스토리 변경 이벤트를 받기 위한 콜백 */
function historyChange(newLocation,
                                  historyData) {
   debug("A history change has occurred: "
            + "newLocation="+newLocation
            + ", historyData="+historyData,
            true);
}


위에서 사용한 debug() 메서드는 예제 소스 파일에 정의된 유틸리티 함수로 전체 예제 안에 포함되어 있다. debug()는 웹 페이지안에 메시지를 출력한다. 두번째 인자는 불리언(boolean) 타입으로 위 인자에서는 true를 사용했다. true를 사용하면 앞의 메시지를 모두 정리한 다음에 새로운 디버그 메시지를 출력한다.

개 발자는 add() 메서드를 사용해서 히스토리 이벤트를 추가한다. 히스토리 이벤트를 추가하는 것은 edit:SomePage와 같이 히스토리 변경 이벤트와 함께 저장할 historyData 값을 제공하기 위한 새 위치를 지정하는 것이다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);

add() 를 호출하는 즉시 사용자 브라우저의 URL 툴바에 새로운 위치가 표시된다. 예를 들어, http://codinginparadise.org/my_ajax_app 주소의 에이잭스 웹 페이지에서 dhtmlHistory.add( "helloworld", "Hello World Data" )를 호출하면 사용자는 브라우저의 URL 툴바에서 다음 주소를 보게 된다.

http://codinginparadise.org/my_ajax_app#helloworld

사 용자는 이 페이지를 북마크할 수 있다. 사용자가 이 북마크를 이용하면 에이잭스 응용 프로그램은 #helloworld 값을 읽어서 웹 페이지를 초기화할 수 있다. 해시 # 다음의 위치 정보는 RSH 프레임워크에 의해 투명하게 인코딩과 디코딩이 수행된다.

historyData 는 URL을 사용하는 것 보다 쉽게 에이잭스 위치 변화에 따른 복잡한 상태들을 저장할 수 있게 해준다. historyData는 숫자, 문자열, 객체와 같은 자바스크립트 타입으로 사용할 수 있는 값이다. 이를 사용하는 예로는 사용자가 DHTML 텍스트 에디터에서 다른 페이지로 이동하는 경우에 에디터의 내용을 모두 저장하는 것이다. 사용자가 뒤로 이동 버튼을 클릭해서 다시 DHTML 페이지로 돌아오면 브라우저는 히스토리 변경 리스너에게 객체를 돌려주게 된다.

개발자는 중첩된 객체나 복잡한 상태를 표현하는 배열과 같은 자바스크립트 객체 전체를 historyData에 사용할 수 있다. 히스토리 데이터에는 간단한 데이터 타입이나 NULL 타입 뿐만 아니라 JSON(자바스크립트 객체 표기법)으 로 표현할 수 있는 무엇이든 될 수 있다. 그러나, DOM 객체에 대한 참조나 XMLHttpRequest와 같은 스크립트 가능한 브라우저 객체는 저장되지 않는다. historyData는 북마크와 함께 보존되는 데이터가 아니며, 브라우저를 종료하거나, 브라우저 캐시를 제거하거나, 사용자가 히스토리를 정리하면 사라지는 데이터이다.

dhtmlHistory를 사용하는 마지막 단계는 isFirstLoad() 메서드이다. 어떤 브라우저에서는 웹 페이지를 항해할 때 다른 페이지로 이동했다가 뒤로 이동 버튼을 눌러서 처음 사이트로 돌아오면 첫번째 페이지가 완전히 재로딩되고 onload 이벤트가 발생한다. 페이지를 반복적으로 재로딩하는 경우가 아니라 처음 로드될 때만 특별한 방법으로 초기화하길 원하는 코드를 망쳐버릴 수 있다. isFirstLoad() 메서드는 웹 페이지를 처음 로드한 경우와 사용자가 브라우저 히스토리에 저장된 웹 페이지에서 뒤로 이동한 경우를 구별해준다.

예제 코드에서는 페이지가 처음 로드되었을 때 히스토리 이벤트를 추가하길 원한다. 페이지가 로드된 다음에 사용자가 페이지로 돌아가기 위해 뒤로 이동 버튼을 누른 경우에는 히스토리 이벤트를 추가하지 않는다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);

이제 historyStorage 클래스를 살펴보자. dhtmlHistory, historyStorage와 마찬가지로 historyStorage라는 전역 객체 하나를 통해서 모든 기능을 제공한다. 이 객체는 해시 테이블을 시뮬레이션 하기 위해 put(keyName, keyValue0, get(keyName), hasKey(keyName)과 같은 메서드를 제공한다. 키 이름은 반드시 문자열이어야하며, 키 값은 XML로 된 문자열이나 자바스크립트 객체와 같이 복잡한 것도 사용할 수 있다. 예제에서는 페이지가 처음 로드될 때 historyStorage안에 간단한 XML을 저장하기 위해 put()을 사용하고 있다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);
                              
      // cache some values in the history
      // storage
      debug("Storing key 'fakeXML' into "
               + "history storage", false);
      var fakeXML =
         '<?xml version="1.0" '
         +         'encoding="ISO-8859-1"?>'
         +         '<foobar>'
         +             '<foo-entry/>'
         +         '</foobar>';
      historyStorage.put("fakeXML", fakeXML);
   }

다음으로, 사용자가 다른 페이지로 이동한 다음에 뒤로 이동 버튼을 통해 돌아온 경우에 get() 메서드를 사용해서 저장된 값을 가져올 수 있고, hasKey()를 사용해서 키가 있는지 확인할 수 있다.

window.onload = initialize;
     
function initialize() {
   // initialize the DHTML History
   // framework
   dhtmlHistory.initialize();
   
   // subscribe to DHTML history change
   // events
   dhtmlHistory.addListener(historyChange);
         
   // if this is the first time we have
   // loaded the page...
   if (dhtmlHistory.isFirstLoad()) {
      debug("Adding values to browser "
               + "history", false);
      // start adding history
      dhtmlHistory.add("helloworld",
                               "Hello World Data");
      dhtmlHistory.add("foobar", 33);
      dhtmlHistory.add("boobah", true);
         
      var complexObject = new Object();
      complexObject.value1 =
                           "This is the first value";
      complexObject.value2 =
                           "This is the second data";
      complexObject.value3 = new Array();
      complexObject.value3[0] = "array 1";
      complexObject.value3[1] = "array 2";
         
      dhtmlHistory.add("complexObject",
                               complexObject);
                              
      // cache some values in the history
      // storage
      debug("Storing key 'fakeXML' into "
               + "history storage", false);
      var fakeXML =
         '<?xml version="1.0" '
         +         'encoding="ISO-8859-1"?>'
         +         '<foobar>'
         +             '<foo-entry/>'
         +         '</foobar>';
      historyStorage.put("fakeXML", fakeXML);
   }
   
   // retrieve our values from the history
   // storage
   var savedXML =
                     historyStorage.get("fakeXML");
   savedXML = prettyPrintXml(savedXML);
   var hasKey =
                historyStorage.hasKey("fakeXML");
   var message =
      "historyStorage.hasKey('fakeXML')="
      + hasKey + "<br>"
      + "historyStorage.get('fakeXML')=<br>"
      + savedXML;
   debug(message, false);
}

preetyPrintXml()은 전체 예제에 포함된 유틸리티 메서드로 디버깅을 위해 웹 페이지에 XML을 출력한다.

데 이터는 오직 현재 페이지의 히스토리에서만 유지된다는 점에 주의해야 한다. 즉, 브라우저를 종료하거나 사용자가 새로운 창을 열고 에이잭스 응용프로그램의 주소를 다시 입력하는 경우에 히스토리 데이터는 이용할 수 없다. 히스토리 데이터는 오직 뒤로, 앞으로 이동 버튼에 한해서만 유지된다. 사용자가 브라우저를 종료하거나 캐시를 정리하면 사라진다. 실제로, 오랜시간 유지할 수 있는 방법을 원한다면 에이잭스 대용량 저장 시스템(AMAXX, Ajax Massive Storage System)을 보기 바란다.

예제는 이것으로 끝났으며, 데모를 체험해보거나 전체 소스 코드를 다운로드하기 바란다.

예제2: 오라일리 메일

두 번째 예제는 Gmail과 유사한 오라일리 메일이라는 가짜 AJAX 이메일 응용프로그램이다. 오라일리 메일을 통해 dhtmlHistory 클래스를 사용해서 브라우저 히스토리를 제어하는 방법, historyStorage 객체를 사용해서 히스토리 데이터를 캐시하는 방법에 대해 설명할 것이다.

오라일리 메일 사용자 인터페이스는 두 부분으로 구성되어 있다. 페이지의 왼쪽 사이즈는 받은 편지(Inbox), 임시 보관(Draft) 같은 다양한 이메일 폴더와 옵션으로 구성된 메뉴다. 사용자가 받은 편지 같은 메뉴를 선택하면 페이지의 오른쪽에 내용을 업데이트한다. 실제 응용프로그램에서는 선택된 메일박스의 내용을 가져와서 표시해야하지만 오라일리 메일에서는 선택한 옵션만 간단하게 보여준다.

오라일리 메일은 브라우저 히스토리에 메뉴 변경사항을 추가하고, 위치 바를 업데이트하는데 RSH 프레임워크를 사용하기 때문에 사용자가 응용프로그램을 북마크하고 브라우저의 뒤로, 앞으로 이동 버튼을 사용해서 메뉴 변경 이전으로 이동할 수 있다.

historyStorage 사용법을 설명하기 위해 Address Book(주소록)이라는 메뉴를 추가했다. 주소록은 이름과 이메일 주소를 자바스크립트 배열로 저장하고 있지만 실제 응용프로그램이라면 원격 서버에서 배열 내용을 가져와야 할 것이다. 그러나, 오라일리 메일에서는 배열을 직접 생성하고, 이름과 이메일 주소를 몇 개 추가한 다음에 historyStorage 객체에 저장한다. 사용자가 웹 페이지를 떠난후에 돌아오면 오라일리 메일 응용프로그램은 원격 서버에서 접속하는 대신 캐시에서 주소록을 가져온다.

주소록은 initialize() 메서드에서 저장하고 가져올 수 있다.

/** 페이지가 로딩을 끝낸 후에 초기화를 수행하는 함수 */
function initialize() {
    // DHTML 히스토리 프레임워크를 초기화한다
    dhtmlHistory.initialize();
   
    // DHTML 히스토리 리스너에 등록한다
    dhtmlHistory.addListener(handleHistoryChange);

    // 주소록을 가져올 수 없으면 직접 추가한 주소록을
    // 히스토리 저장소에서 캐싱한다.
   if (window.addressBook == undefined) {
         // 주소록을 전역 객체로 저장한다
       // 실제 응용프로그램에서는 백그라운드에서 서버로부터
       // 주소록을 가져와야한다
         window.addressBook =
             ["Brad Neuberg 'bkn3@columbia.edu'",
               "John Doe 'johndoe@example.com'",
               "Deanna Neuberg 'mom@mom.com'"];
               
         // 주소록이 있으면 이를 캐시에 보관한다
       // 사용자가 페이지를 떠난 후에 뒤로 이동으로 돌아온 경우에도 사용한다
       historyStorage.put("addressBook",
                                     addressBook);
    }
    else {
         // 히스토리 저장소에서 캐시된 주소록을 가져온다
         window.addressBook =
                      historyStorage.get("addressBook");
    }

히 스토리 변경을 다루는 코드는 이해하기 쉽다. 아래 코드에서처럼 사용자가 뒤로 또는 앞으로 이동 버튼을 클릭할 때 handleHistoryChange가 호출된다. 그러면 newLocation을 갖게 된다. 이를 이용해서 사용자 인터페이스를 올바른 상태로 업데이트할 수 있다.

/** 히스토리 변경 이벤트를 처리한다 */
function handleHistoryChange(newLocation,
                                           historyData) {
    // 위치가 없으면 수신함의 기본 위치를 보여준다
    if (newLocation == "") {
         newLocation = "section:inbox";
    }
   
    // 위치 변화가 있으면 표시할 섹션을 추출한다.
    // newLocation은 "section:"으로 시작한다
    newLocation =
             newLocation.replace(/section\:/, "");
   
    // DHTML 히스토리 변경에 따라 브라우저를 업데이트한다
    displayLocation(newLocation, historyData);
}

/** 오른쪽 컨텐트 영역에 주어진 위치를 표시한다*/
function displayLocation(newLocation,
                                     sectionData) {
    // 선택한 메뉴 항목을 가져온다
    var selectedElement =
                  document.getElementById(newLocation);
                 
    // 이전에 선택된 메뉴 항목을 제거한다
    var menu = document.getElementById("menu");
    for (var i = 0; i < menu.childNodes.length;
                                                               i++) {
         var currentElement = menu.childNodes[i];
         // DOM 요소 노드인지 확인한다
         if (currentElement.nodeType == 1) {
             //모든 클래스 이름을 제거한다
             currentElement.className = "";
         }                                                         
    }
   
    // UI에서 다르게 나타나는 새로 선택된 메뉴 항목
    selectedElement.className = "selected";
   
    // 화면 오른쪽에 새로운 섹션을 표시한다.
    // sectionData에 따라 섹션을 결정한다

    // 앞서 캐시된 로컬 주소 데이터를 사용해서 주소록을 보여준다
    if (newLocation == "addressbook") {
         // 주소록을 보여준다
         sectionData = "<p>Your addressbook:</p>";
         sectionData += "<ul>";
         
         // 주소록이 아직 없다면 캐시에서 주소록을 가져온다
         if (window.addressBook == undefined) {
             window.addressBook =
                      historyStorage.get("addressBook");
         }
         
         // 주소록을 표시한다
         for (var i = 0;
                      i < window.addressBook.length;
                               i++) {
             sectionData += "<li>"
                                    + window.addressBook[i]
                                    + "</li>";                          
         }
         
         sectionData += "</ul>";
    }
   
    // sectionData가 없다면 원격으로 가져온다. 이 예제에서는 주소록을 제외한
    // 모든 데이터는 가짜를 이용하고 있다
    if (sectionData == null) {
         // 실제 응용프로그램에서는 섹션의 내용을 원격으로 가져와야한다
         sectionData = "<p>This is section: "
             + selectedElement.innerHTML + "</p>";  
    }
   
    // 제목과 내용을 업데이트한다
    var contentTitle =
             document.getElementById("content-title");
    var contentValue =
             document.getElementById("content-value");
    contentTitle.innerHTML =
                                    selectedElement.innerHTML;
    contentValue.innerHTML = sectionData;
}


오라일리 메일 데모를 체험하거나 소스 코드를 다운로드할 수 있다.

결론

RSH(Really Simple History) API를 사용해서 에이잭스 응용프로그램에서 즐겨찾기와 뒤로, 앞으로 이동 버튼을 사용하기 위한 방법을 보았으며 직접 에이잭스 응용프로그램을 작성하는데 발판이 될 수 있는 예제 코드들을 살펴보았다. 나는 즐겨찾기와 히스토리를 완전히 지원하는 여러분의 에이잭스 발명품들을 볼 수 있기를 기대한다.

감사의 글

이 글을 검토해준 모든 사람과 RSH 프레임워크를 제작한 모두에게 감사드린다: Michael Eakes, Jeremy Sevareid, David Barrett, Brendon Wilson, Dylan Parker, Erik Arvidsson, Alex Russell, Adam Fisk, Alex Lynch, Joseph Hoang Do, Richard MacManus, Garret Wilson, Ray Baxter, Chris Messina, and David Weekly.

참고자료
  • 이 기사의 모든 소스코드 다운로드
  • RSH 프레임워크 다운로드
  • 오라일리 메일 데모와 오라일리 메일 소스 코드 다운로드. 전체 예제 다운로드에는 시험할 수 있는 보다 많은 예제들이 있다
  • Coding in Paradise(코딩천국): AJAX, DHTML, 자바 기술, 위키위키와 같은 협업 기술 등을 다루는 저자의 웹로그
  • "Coding in Paradise: AJAX: Bookmarks and Back Buttons, Advanced Example"
  • "Coding in Paradise: Safari: No DHTML History Possible"
  • "Coding in Paradise: AJAX Tutorial: Saving Session Across Page Loads Without Cookies, On The Client Side"
  • "Coding in Paradise: AJAX History Libraries"
Bard Neuberg는 모질라, JXTA, 자카르타 피드 파서 등에 코드를 공헌하고 있으며, 오픈소스 커뮤니티에서 폭넓은 공헌을 하고 있다.


출처 :
http://network.hanbitbook.co.kr/



'Scripter > AJAX' 카테고리의 다른 글

Ajax Framework 분석  (0) 2009.05.04
CSSHttpRequest  (0) 2008.11.12
HTTP Header에 대하여  (0) 2008.11.08
100라인 Ajax Wrapper  (0) 2008.04.19
어떤 AJAX Framework를 선택할 것인가.  (0) 2008.03.14
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Gwt-Ext
, .Net-Ext에 이어 PHP-Ext 프로젝트가 오픈했다.
처음 데꾸벅이 Rails를 공부할때 봤던 Extjs가 이제는 많은 개발자들의 관심의 대상이 된것은사용자들에게 익숙한 윈도우즈시스템과 같은 화려한 UI 덕분이 아닐까 싶다.
프로젝트를 오픈한 Sergei Wlater Guerra는 PHP-Ext를 "PHP4, PHP5로  막강한 UI Layer로 쓰여진 오픈소스 위젯 라이브러리"라고 정의했다.

아직은 0.8.1이지만 좀더 나은 발전을 기원하며... 후훗!!


PHP 소스

echo Ext::onReady(
        Javascript::stm(ExtQuickTips::init()),
        Javascript::assign("data",Javascript::valueToJavascript($myData)),
        //Javascript::valueToJavascript($myData),
        $store->getJavascript(false, "ds"),
        $italicRenderer,
        $changeRenderer,
        $pctChangeRenderer,
        $colModel->getJavascript(false, "colModel"),
        $gridForm->getJavascript(false, "gridForm")
);


$gridForm = new ExtFormPanel("company-form");
$gridForm->Frame = true;
$gridForm->LabelAlign = EXT_FORM_LABEL_ALIGN_LEFT;
$gridForm->Title = "Company Data";
$gridForm->BodyStyle = "padding: 5px;";
$gridForm->Width = 750;
$gridForm->Layout = EXT_CONTAINER_LAYOUTS_COLUMN;






'Scripter > EXTJS' 카테고리의 다른 글

Ext JS Ext.ux.YoutubePlayer  (0) 2008.03.29
Extjs를 이용한 간단한 form submit  (0) 2008.03.14
Extjs Grouping Header Grid Plugins  (13) 2008.03.06
Extjs 크로스 도메인 관련  (0) 2008.02.28
Extjs 한국어로 나타내기 (locale설정)  (9) 2008.02.27
Post by 넥스트리소프트 데꾸벅(techbug)
, |
서버없이 ExtJS를 사용하고 싶다면 아래 파일을 다운로드 받아 HTML에 넣고 사용한다.
사내솔루션(넥스트리)개발시 서버와 상관없이 작업을 하려다 찾은소스이다.




소스를 까(?)보면 Ext.lib.Ajax 부분을 오버라이드하고 있는데 기본적으로 크로스 도메인에서는 다른 프록시를 써서 해당 우회하는 방법으로 크로스도메인을 사용한다.
그러나 ExtJS에서는 로컬을 우회 프록시로 사용하는갑다..

중간에 XhrHeader값을 보면 "X-Requested-With"를 보면 커스텀헤더를 생성해서 사용하는것을 볼수 있는데 이것은 Prototype.js나 jQuery에서도 이와 같은 방법을 사용하고 있다.

제로보드에도 이러한 스크립트가 있던데 함 뜯어봐야겠다..


[실제 PHP로 헤더값만들기]
function isAjax() {
 return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
     $_SERVER ['HTTP_X_REQUESTED_WITH']  == 'XMLHttpRequest';
}


[사용법]
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script>
<script type="text/javascript" src="../extjs/adapter/ext/localXHR.js"></script>


[실제 localXHR.js 소스]

Ext.apply( Ext.lib.Ajax ,
{ forceActiveX:false,
  createXhrObject:function(transactionId)
        {
            var obj={  status:{isError:false}
                     , tId:transactionId}, http;
            try
            {
               
        if(Ext.isIE7 && !!this.forceActiveX){throw("IE7forceActiveX");}
               
        obj.conn= new XMLHttpRequest();
       
            }
            catch(e)
            {
                for (var i = 0; i < this.activeX.length; ++i) {
                    try
                    {
                        obj.conn= new ActiveXObject(this.activeX[i]);
           
                        break;
                    }
                    catch(e) {
                    }
                }
            }
            finally
            {
                obj.status.isError = typeof(obj.conn) === undefined;
            }   
            return obj;
           
        },
       
        getHttpStatus: function(reqObj){
       
            var statObj = {  status:0
                    ,statusText:''
                    ,isError:false
                    ,isLocal:false
                    ,isOK:false
                    ,error:null};
           
            try {
                if(!reqObj)throw('noobj');
                statObj.status = reqObj.status;
               
                statObj.isLocal = !reqObj.status && location.protocol == "file:" ||
                           Ext.isSafari && reqObj.status === undefined;
               
                statObj.isOK = (statObj.isLocal || (statObj.status > 199 && statObj.status < 300));
                statObj.statusText = reqObj.statusText || '';
            } catch(e){ //지원하지 않는 status 혹은 너무 빨리 호출했을 경우
              }
           
            return statObj;
       
        },
        handleTransactionResponse:function(o, callback, isAbort)
        {
   
       
        callback = callback || {};
        var responseObject=null;
       
         if(!o.status.isError){
            o.status = this.getHttpStatus(o.conn);        
            /* 필요하다면 적당한 status와 XMLDOM을 이용하여 생성하거나 향상시킨다.*/
             responseObject = this.createResponseObject(o, callback.argument);
         }
       
          if(o.status.isError){
            /* exception이 발생했을 경우 다시 체크한다.*/
           responseObject = Ext.applyIf(responseObject||{},this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false)));
          
         }
       
         responseObject.options = o.options;
         responseObject.stat = o.status;
         
         if (o.status.isOK && !o.status.isError) {
            if (callback.success) {
                if (!callback.scope) {
                    callback.success(responseObject);
                }
                else {
                    callback.success.apply(callback.scope, [responseObject]);
                }
            }
          } else {

            if (callback.failure) {
                if (!callback.scope) {
                    callback.failure(responseObject);
                }
                else {
                    callback.failure.apply(callback.scope, [responseObject]);
                }
            }

         }
       
        if(o.options.async){
            this.releaseObject(o);   
            responseObject = null;
        }else{
            this.releaseObject(o);
            return responseObject;
        }
           
    },
    createResponseObject:function(o, callbackArg)
    {
        var obj = {};
        var headerObj = {},headerStr='';

        try{  //잘못된 인코딩일경우
        obj.responseText = o.conn.responseText;
        }catch(e){obj.responseText ='';}

        obj.responseXML = o.conn.responseXML;

        try{
        headerStr = o.conn.getAllResponseHeaders()||'';
        } catch(e){}

        if(o.status.isLocal){

           o.status.isOK = ((o.status.status = (!!obj.responseText.length)?200:404) == 200);

           if(o.status.isOK && (!obj.responseXML || obj.responseXML.childNodes.length == 0)){

            var xdoc=null;
            try{   //MS ActiveX가 작동하지 않을 경우
                if(typeof(DOMParser) == 'undefined'){
                    xdoc=new ActiveXObject("Microsoft.XMLDOM");
                    xdoc.async="false";
                    xdoc.loadXML(obj.responseText);

                }else{
                    try{  //Opera 9 가 xml contents 파싱에 실패할 경우
                        var domParser = new DOMParser();
                        xdoc = domParser.parseFromString(obj.responseText, 'application\/xml');
                    }catch(ex){}
                    finally{domParser = null;}

                }
            } catch(ex){
                o.status.isError = true;
                o.status.error = ex;

                }

            obj.responseXML = xdoc;
            }

            if(obj.responseXML){

            var parseBad = (obj.responseXML.parseError || 0) != 0 || obj.responseXML.childNodes.length == 0;
            if(!parseBad){
                headerStr = 'Content-Type: ' + (obj.responseXML.contentType || 'text\/xml') + '\n' + headerStr ;
                }               
            }       


        }   

       var header = headerStr.split('\n');
       for (var i = 0; i < header.length; i++) {
            var delimitPos = header[i].indexOf(':');
            if (delimitPos != -1) {
            headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
            }
        }

        obj.tId = o.tId;
        obj.status = o.status.status;
        obj.statusText = o.status.statusText;
        obj.getResponseHeader = headerObj;
        obj.getAllResponseHeaders = headerStr;
        obj.stat = o.status

        if (typeof callbackArg !== undefined) {
        obj.argument = callbackArg;
        }

        return obj;
        },
       
    request : function(method, uri, cb, data, options) {
               
                 options = Ext.apply({async:true,
               headers:false,
               userId:null,
               password:null,
               xmlData:null }, options||{});
                                       
                    var hs = options.headers;
                    if(hs){
                        for(var h in hs){
                            if(hs.hasOwnProperty(h)){
                                this.initHeader(h, hs[h], false);
                            }
                        }
                    }
                    if(options.xmlData){
                        this.initHeader('Content-Type', 'text/xml', false);
                        method = 'POST';
                        data = options.xmlData;
                    }
                               
            return this.makeRequest(method, uri, cb, data, options);
           
        },
        asyncRequest:function(method, uri, callback, postData)
        {
            var o = this.getConnectionObject();

            if (!o || o.status.isError) {
                return null;
            }
            else {
                o.options = options;
                try{
            o.conn.open(method, uri, true);
        } catch(ex){
            o.status.isError = true;
            o.status.error = ex;
            return Ext.apply(o,this.handleTransactionResponse(o, callback));
           
        }
       
       
        if (this.useDefaultXhrHeader) {
            if (!this.defaultHeaders['X-Requested-With']) {
            this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
            }
        }

        if(postData && this.useDefaultHeader){
            this.initHeader('Content-Type', this.defaultPostHeader);
        }

         if (this.hasDefaultHeaders || this.hasHeaders) {
            this.setHeader(o);
        }

        this.handleReadyState(o, callback);
       
        try{ o.conn.send(postData || null);
        } catch(ex){
            o.status.isError=true;
            o.status.error = ex;
            return Ext.apply(o,this.handleTransactionResponse(o, callback));
        }
           
                   
        return o;
            }
        },
       
        makeRequest:function(method, uri, callback, postData, options)
        {
            var o = this.getConnectionObject();
                    
            if (!o || o.status.isError) {
                return null;
            }
            else {
                o.options = options;   
                try{
            o.conn.open(method, uri, options.async, options.userId, options.password);
        } catch(ex){
            o.status.isError = true;
            o.status.error = ex;
            var r=this.handleTransactionResponse(o, callback);
            return Ext.apply(o,r);
        }

        if (this.useDefaultXhrHeader) {
            if (!this.defaultHeaders['X-Requested-With']) {
            this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
            }
        }

        if(postData && this.useDefaultHeader){
            this.initHeader('Content-Type', this.defaultPostHeader);
        }

         if (this.hasDefaultHeaders || this.hasHeaders) {
            this.setHeader(o);
        }

        if(o.options.async){ //blocking call때문에 Timer가 작동하지 않음
            this.handleReadyState(o, callback);
        }
       
        try{ o.conn.send(postData || null);
        } catch(ex){
            //Ext.apply(o,this.handleTransactionResponse(o, callback));
        }
               
        return options.async?o:Ext.apply(o,this.handleTransactionResponse(o, callback));
            }
   }});
   
Ext.lib.Ajax.forceActiveX = (document.location.protocol == 'file:');

Post by 넥스트리소프트 데꾸벅(techbug)
, |
이전블로그(http://blog.naver.com/techbug)에 포스팅했던 글을 백업받아 옵니다.
--------[이후]-----------------------


GWT Ext 2.0 이 릴리즈됐다.
2007년 ajax framework and libraries에서 prototype.js, jQuery다음 3위를 차지하더니만 선전하고 있는 extjs 아자...
맞바람을 탄다는 기분은 이런 것이라... 데꾸벅 뽯팅!

관련사이트 :
http://ajaxian.com/archives/gwt-ext-20-released
http://gwt-ext.com/
http://gwt-ext.googlecode.com/files/gwtext-2.0.1.zip

데모 : http://gwt-ext.com/demo/


사용자 삽입 이미지

Post by 넥스트리소프트 데꾸벅(techbug)
, |
사용자 삽입 이미지

















Jaroslav Benc는 XML을 이용하여 Java 프로젝트용 ExtJS JSP Taglib 생성기를 만들었다.

아직 완변하진 않지만 그의 사이트에 포스트된 로드맵을 보자면

  • Hibernate integration - HibernateStore component
  • DWR integration etc.
  • Eclipse plugin
  • UX Tags: Ext.ux.*, Ext.portal.*, Ext.feedreader.*, Ext.desktop.*
을 지원한다고 하는데 ExtJS의 config 옵션을 모두 xml 어트리뷰트로 처리가능하며 모든 ID값 (ExtJS에서 Ext.getCmp("id") )으로 처리된


참조사이트 : http://www.exttld.com/index.php

Post by 넥스트리소프트 데꾸벅(techbug)
, |

들어가며

본 포스팅은 사내(넥스트리소프트) 솔루션 개발시 가이드작성했던 문서를 다시 정리하여 올린것입니다.


기본개념

기본적인 Grid 사용법및 DataStore 및 RowSelectionModel에 대해 알아본다.
GridPanel을 이용하여 데이타스토어에 저장된 데이타를 가져오는 법과 기본적인 그리드 렌더링 방법, 각각의 Row에 대한 Model에서 Data를 축출하는 법에 대해서 기술한다.

아래소스다운로드



 

Step 1. Basic Array Grid

basicGrid.html

<html>
<head>
<title>Basic Grid</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="basicGrid.js"></script>
</head>
<body id="basicGridBody">
<div id="DIV_GRID_HERE"></div> <!-- Grid를 렌더링 할 위치 -->
</body>
</html>


 

basicGrid.js

BasicGridClass = function(){
    return {
        init: function(){
            //데이타스토어에 사용할 데이타
            var myData = [
                ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
                ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
                ['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
                ['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
                ['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
                ['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
                ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
                ['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
                ['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
                ['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
                ['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
                ['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
                ['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
                ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
                ['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
                ['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
                ['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
                ['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
                ['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
                ['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
                ['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
                ['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
                ['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
                ['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
                ['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
                ['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
                ['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
                ['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
                ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
            ];
                      
            this.store = new Ext.data.SimpleStore({
                fields: [{
                    name: 'company'
                }, //1번째 컬럼을 'company'로 정의
                {
                    name: 'price',
                    type: 'float'
                },//2번째 컬럼을 'price'로 정의하며 데이타 타입은 float으로 정의
                {
                    name: 'change',
                    type: 'float'
                }, {
                    name: 'pctChange',
                    type: 'float'
                }, {
                    name: 'lastChange',
                    type: 'date',
                    dateFormat: 'n/j h:ia'
                }]
            });
            //데이타 스토어 로드하기
            this.store.loadData(myData);
            
            //그리드 그리기
            this.grid = new Ext.grid.GridPanel({
                store: this.store, //그리드에 사용될 데이타 스토어 정의
                columns: [ //그리드 헤드 및 데이타 정제(ColumnModel 정의)
                {
                    id: 'company',
                    header: "Company",
                    width: 160,
                    sortable: true,
                    dataIndex: 'company'
                }, {
                    header: "Price",
                    width: 75,
                    sortable: true,
                    renderer: 'usMoney',
                    dataIndex: 'price'
                }, {
                    header: "Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'change'
                }, {
                    header: "% Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'pctChange'
                }, {
                    header: "Last Updated",
                    width: 85,
                    sortable: true,
                    renderer: Ext.util.Format.dateRenderer('m/d/Y'),
                    dataIndex: 'lastChange'
                }],
                stripeRows: true, //Row마다 CSS class를 적용하고 싶을때 처리
                autoExpandColumn: 'company', //정의된 ColumnModel에서 자동으로 늘리고 싶은 컬럼
                loadMask: {
                    msg: '데이타 로드중'
                }, //그리드 로드시 화면로딩 indicator설정
                //그리드 선택모델(selectionModel)정의 : RowSelectionMode -> Row별로 처리하겠다.
                sm: new Ext.grid.RowSelectionModel({
                    singleSelect: true //Row 하나만 선택가능하게 하기
                }),
                viewConfig: { //그리드의 Dataview 설정
                    forceFit: true //가로에 그리드의 크기를 맞춘다.
                },
                height: 350,
                width: 800,
                title: '기본기리드'
            });
            //그리드를 DIV_GRID_HERE 라는 ID값을 가진 객체에 렌더링한다.
            this.grid.render('DIV_GRID_HERE');
            //그리드 로드시 첫번째 Row를 자동으로 선택되게 한다.
            this.grid.getSelectionModel().selectFirstRow();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);


 

 

 

Step 2. How to use HttpProxy & ScriptTagProxy

Step1에서 배열데이타를 사용할때(Ext.data.SimpleStore)와 직접 통신하여 사용할때(Ext.data.Store)를 비교하면서 아래 소스를 보기 바란다.
Simplestore 는 기본적으로 네트웍을 사용하지 않으므로 Proxy를 사용하지 않는다. 그러나 Store, JsonStore, GroupingStore의 경우 Proxy를 사용하여 데이타의 위치를 지정할수 있다.

SimpleStore vs. (Json, Grouping) Store


SimpleStore
(Json, Grouping) Store
Proxy
없음(없을시 HttpProxy사용)
HttpProxy, ScriptTagProxy, MemoryProxy
Reader
ArrayReader
JsonReader, XmlReader, ArrayReader
Load Method
store.loadData(data);
store.load()
Extend
Ext.data.Store
Ext.data.Store

 

 

 

 

 

basicGrid.js

BasicGridClass = function(){
    return {
        init: function(){
            ////DataStore를 정의한다. Ext.data.Store
            this.store = new Ext.data.Store({
                // Data를 가져올 Proxy설정
                proxy: new Ext.data.HttpProxy({
                    url: 'basicGrid.json',
                    method: 'POST'
                }),
                // Client쪽에서 Sort할 경우 소트할 항목
                sortInfo: {
                    field: 'price',
                    direction: "DESC"
                },
                reader: new Ext.data.JsonReader({
                    root: 'testData'
                },
                [{
                    name: 'company'
                }, {
                    name: 'price',
                    type: 'float'
                }, {
                    name: 'change',
                    type: 'float'
                }, {
                    name: 'pctChange',
                    type: 'float'
                }, {
                    name: 'lastChange',
                    type: 'date',
                    dateFormat: 'n/j h:ia'
                }])
            });
            this.grid = new Ext.grid.GridPanel({
                store: this.store,
                columns: [{
                    id: 'company',
                    header: "Company",
                    width: 160,
                    sortable: true,
                    dataIndex: 'company'
                }, {
                    header: "Price",
                    width: 75,
                    sortable: true,
                    renderer: 'usMoney',
                    dataIndex: 'price'
                }, {
                    header: "Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'change'
                }, {
                    header: "% Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'pctChange'
                }, {
                    header: "Last Updated",
                    width: 85,
                    sortable: true,
                    renderer: Ext.util.Format.dateRenderer('m/d/Y'),
                    dataIndex: 'lastChange'
                }],
                stripeRows: true,
                autoExpandColumn: 'company',
                loadMask: {
                    msg: '데이타 로드중'
                },
                sm: new Ext.grid.RowSelectionModel({
                    singleSelect: true
                }),
                viewConfig: {
                    forceFit: true
                },
                height: 350,
                width: 800,
                title: '기본 그리드'
            });
           
            this.grid.render('DIV_GRID_HERE');
            this.grid.getSelectionModel().selectFirstRow();
            this.store.load(); //데이타를 로드한다.
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);

  HttpProxy나 ScriptTagProxy로 서버 통신을 한다. ScriptTagProxy에 대한 고찰(?) 을 참조하라.

  • HttpProxy : 같은 도에인일 경우
  • ScriptTagProxy: 다른 도메인일 경우
  • MemoryProxy: 메모리에 있는 데이타

 

basicGrid.json
{
    testData: [{
        'company': '3m Co',
        'price': 71.72,
        'change': 0.02,
        'pctChange': 0.03,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Alcoa Inc',
        'price': 29.01,
        'change': 0.42,
        'pctChange': 1.47,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Altria Group Inc',
        'price': 83.81,
        'change': 0.28,
        'pctChange': 0.34,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'American Express Company',
        'price': 52.55,
        'change': 0.01,
        'pctChange': 0.02,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'American International Group, Inc.',
        'price': 64.13,
        'change': 0.31,
        'pctChange': 0.49,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'AT&T Inc.',
        'price': 31.61,
        'change': -0.48,
        'pctChange': -1.54,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Boeing Co.',
        'price': 75.43,
        'change': 0.53,
        'pctChange': 0.71,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Caterpillar Inc.',
        'price': 67.27,
        'change': 0.92,
        'pctChange': 1.39,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Citigroup,Inc.',
        'price': 49.37,
        'change': 0.02,
        'pctChange': 0.04,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'E.I. du Pont de Nemours and Company',
        'price': 40.48,
        'change': 0.51,
        'pctChange': 1.28,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Exxon Mobil Corp',
        'price': 68.1,
        'change': -0.43,
        'pctChange': -0.64,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'General Electric Company',
        'price': 34.14,
        'change': -0.08,
        'pctChange': -0.23,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'General Motors Corporation',
        'price': 30.27,
        'change': 1.09,
        'pctChange': 3.74,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Hewlett-Packard Co.',
        'price': 36.53,
        'change': -0.03,
        'pctChange': -0.08,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Honeywell Intl Inc',
        'price': 38.77,
        'change': 0.05,
        'pctChange': 0.13,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Intel Corporation',
        'price': 19.88,
        'change': 0.31,
        'pctChange': 1.58,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'International Business Machines',
        'price': 81.41,
        'change': 0.44,
        'pctChange': 0.54,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Johnson & Johnson',
        'price': 64.72,
        'change': 0.06,
        'pctChange': 0.09,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'JP Morgan & Chase & Co',
        'price': 45.73,
        'change': 0.07,
        'pctChange': 0.15,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'McDonald\'s Corporation',
        'price': 36.76,
        'change': 0.86,
        'pctChange': 2.40,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Merck & Co., Inc.',
        'price': 40.96,
        'change': 0.41,
        'pctChange': 1.01,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Microsoft Corporation',
        'price': 25.84,
        'change': 0.14,
        'pctChange': 0.54,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Pfizer Inc',
        'price': 27.96,
        'change': 0.4,
        'pctChange': 1.45,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'The Coca-Cola Company',
        'price': 45.07,
        'change': 0.26,
        'pctChange': 0.58,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'The Home Depot,Inc.',
        'price': 34.64,
        'change': 0.35,
        'pctChange': 1.02,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'The Procter & Gamble Company',
        'price': 61.91,
        'change': 0.01,
        'pctChange': 0.02,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'United Technologies Corporation',
        'price': 63.26,
        'change': 0.55,
        'pctChange': 0.88,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Verizon Communications',
        'price': 35.57,
        'change': 0.39,
        'pctChange': 1.11,
        'lastChange': '9/1 12:00am'
    }, {
        'company': 'Wal-Mart Stores, Inc.',
        'price': 45.45,
        'change': 0.73,
        'pctChange': 1.63,
        'lastChange': '9/1 12:00am'
    }]
}

 

 

 

 

 

Step 3. Data-Mapping and Renderer

BasicGridClass = function(){
    return {
        init: function(){
            //데이타 스토어 정의
            this.store = new Ext.data.Store({
                proxy: new Ext.data.HttpProxy({
                    url: 'basicGrid.json',
                    method: 'POST'
                }),
                sortInfo: {
                    field: 'price',
                    direction: "DESC"
                },
                reader: new Ext.data.JsonReader({
                    root: 'testData'
                }, [{
                    name: 'company',
                    type: 'string',
                    mapping: 'company',
                    convert: this.convertCompany
                }, //응답받은 JSON의 company와 맵핑된다. 맵핑정보 변경을 converting한다.
                {
                    name: 'price',
                    type: 'float',
                    mapping: 'price'
                }, {
                    name: 'change',
                    type: 'float',
                    mapping: 'change'
                }, {
                    name: 'pctChange',
                    type: 'float',
                    mapping: 'pctChange'
                }, {
                    name: 'lastChange',
                    type: 'date',
                    dateFormat: 'n/j h:ia',
                    mapping: 'lastChange'
                }])
            });
            this.grid = new Ext.grid.GridPanel({
                store: this.store,
                columns: [{
                    id: 'company',
                    header: "Company",
                    width: 160,
                    sortable: true,
                    dataIndex: 'company'
                }, // 맵핑된 company의 name을 dataIndex로 잡는다.
                {
                    header: "Price",
                    width: 75,
                    sortable: true,
                    renderer: 'usMoney',
                    dataIndex: 'price'
                }, {
                    header: "Change",
                    width: 75,
                    sortable: true,
                    dataIndex: 'change'
                }, {
                    header: "% Change",
                    width: 75,
                    sortable: true,
                    renderer: this.pctChange,
                    dataIndex: 'pctChange'
                }, // 화면에 렌더링할때  renderer를 이용해 칼라를 바꿔준다.
                {
                    header: "Last Updated",
                    width: 85,
                    sortable: true,
                    renderer: Ext.util.Format.dateRenderer('m/d/Y'),
                    dataIndex: 'lastChange'
                }],
                stripeRows: true,
                autoExpandColumn: 'company',
                loadMask: {
                    msg: '데이타 로드중'
                },
                sm: new Ext.grid.RowSelectionModel({
                    singleSelect: true
                }),
                viewConfig: {
                    forceFit: true
                },
                height: 350,
                width: 800,
                title: '기본 그리드'
            });
           
            this.grid.render('DIV_GRID_HERE');
            this.grid.getSelectionModel().selectFirstRow();
            this.store.load();
           
        },
        //화면에 렌더링할때 renderer에 의해서 칼라를 바꿔주느 메쏘드
        pctChange: function(val){
            if (val > 0) {
                return '<span>' + val + '%</span>';
            }
            else
                if (val < 0) {
                    return '<span>' + val + '%</span>';
                }
            return val;
        },
        // 데이타 맵핑할때 자식노드가 있을 경우 해당 자식노드로 변환하여 반환한다.
        convertCompany: function(value, p, record){
            return (value.name != undefined) ? value.name : value;
        }
       
    }
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);

위의 convert 와 renderer 옵션을 이용하여 여러가지 다양한 형태의 grid 생성이 가능하다. 또한 JsonReader의 root 노드또한 Json 표기 형식의 . (dot) 연산자로 해당 자식 노드를 가져올수 있다.

 

추가적으로 extjs의 API Doc에는 rederer에 대해서 다음과 같이 기술하고 있다.

rederer의 파라미터값들
* value : Object - 셀에 들어가는 데이타
* metadata : Object - 정의한 객체 메타데이타
* css : String : 테이블테그에서 td에 먹이는 css
* attr : String : HTML 애트리뷰트 정의
* record : Ext.data.record : 데이타가 압축된 레코드
* rowIndex : Number
* colIndex : Number
* store : Ext.data.Store

 

 

Step 4. DataStore Load & Loadexception Handling

 데이타스토어가 모두 로드가 완료됐을 경우 혹은 서버이상으로 로드가 되지 않았을 경우를 예를 들어 설명한다.

BasicGridClass = function(){
return {
init: function(){
//데이타 스토어 정의
this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'basicGrid.json'
}),
sortInfo: {
field: 'price',
direction: "DESC"
},
reader: new Ext.data.JsonReader({
root: 'testData'
}, [{
name: 'company',
type: 'string',
mapping: 'company'
}, {
name: 'price',
type: 'float',
mapping: 'price'
}, {
name: 'change',
type: 'float',
mapping: 'change'
}, {
name: 'pctChange',
type: 'float',
mapping: 'pctChange'
}, {
name: 'lastChange',
type: 'date',
dateFormat: 'n/j h:ia',
mapping: 'lastChange'
}])
});
this.grid = new Ext.grid.GridPanel({
store: this.store,
columns: [{
id: 'company',
header: "Company",
width: 160,
sortable: true,
dataIndex: 'company'
}, {
header: "Price",
width: 75,
sortable: true,
renderer: 'usMoney',
dataIndex: 'price'
}, {
header: "Change",
width: 75,
sortable: true,
dataIndex: 'change'
}, {
header: "% Change",
width: 75,
sortable: true,
dataIndex: 'pctChange'
}, {
header: "Last Updated",
width: 85,
sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'
}],
stripeRows: true,
autoExpandColumn: 'company',
loadMask: {
msg: '데이타 로드중'
},
sm: new Ext.grid.RowSelectionModel({
singleSelect: true
}),
view: new Ext.grid.GridView({
forceFit: true,
enableRowBody: true,
ignoreAdd: true,
emptyText: 'No Record found'
}),
height: 350,
width: 800,
title: '기본 그리드'
});

this.grid.render('DIV_GRID_HERE');
//this.grid.getSelectionModel().selectFirstRow();
this.store.load();


this.grid.on('rowcontextmenu', function(grid, index, e){
alert('오른쪽 버튼 클릭');
}, this);

// Row에서 마우스 오른쪽 클릭시
this.grid.on('rowclick', function(grid, index, e){
alert('클릭');
}, this);

// Row 클릭시
this.grid.on('rowdblclick', function(grid, index, e){
alert('더블클릭');
}, this);

// Row 더블클릭시
this.gsm = this.grid.getSelectionModel();

// 데이타 스토어에서 데이타 로드 완료시 첫번째 Row 선택되게하기
this.store.on('load', this.gsm.selectFirstRow, this.gsm);
this.store.on('load', function(store, records, options){
alert('데이타로드완료')
}, this);

// 데이타 스토어에서 데이타 로드실패시 exception throw
this.store.on('loadexception', function(a, conn, resp){
//alert(resp.status.toString() +'\n'+ resp.statusText);
this.grid.emptyText = 'data load error';
}, this);

// Ext.data.Dataproxy 통신 에러시
this.store.proxy.on('loadexception', function(proxy, dataObj, callbackArgs, e){
if (Ext.gecko)
console.log(e);
});

}
}
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);

 

 

 

Step 5. 선택된 Row에서 값 추출하기

BasicGridClass = function(){
return {
init: function(){
//데이타 스토어 정의
this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'basicGrid.json'
}),
sortInfo: {
field: 'price',
direction: "DESC"
},
reader: new Ext.data.JsonReader({
root: 'testData'
}, [{
name: 'company',
type: 'string',
mapping: 'company'
}, {
name: 'price',
type: 'float',
mapping: 'price'
}, {
name: 'change',
type: 'float',
mapping: 'change'
}, {
name: 'pctChange',
type: 'float',
mapping: 'pctChange'
}, {
name: 'lastChange',
type: 'date',
dateFormat: 'n/j h:ia',
mapping: 'lastChange'
}])
});
this.grid = new Ext.grid.GridPanel({
store: this.store,
columns: [{
id: 'company',
header: "Company",
width: 160,
sortable: true,
dataIndex: 'company'
}, // DataIndex정의
{
header: "Price",
width: 75,
sortable: true,
renderer: 'usMoney',
dataIndex: 'price'
}, {
header: "Change",
width: 75,
sortable: true,
dataIndex: 'change'
}, {
header: "% Change",
width: 75,
sortable: true,
dataIndex: 'pctChange'
}, {
header: "Last Updated",
width: 85,
sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'
}],
stripeRows: true,
autoExpandColumn: 'company',
loadMask: {
msg: '데이타 로드중'
},
//RowSelectionModel 이거나 CellSelectionModel일 경우만 데이타 가져오기
sm: new Ext.grid.RowSelectionModel({
singleSelect: true
}),
view: new Ext.grid.GridView({
forceFit: true,
enableRowBody: true,
emptyText: 'No Record found'
}),
height: 350,
width: 800,
title: '기본 그리드'
});

this.grid.render('DIV_GRID_HERE');
this.store.load();
this.gsm = this.grid.getSelectionModel();
this.store.on('load', this.gsm.selectFirstRow, this.gsm);

// ① Row에서 오른쪽 마우스 클릭했을 경우
this.grid.on('rowcontextmenu', this.getRowData, this);
// ① Row 클릭했을 경우
this.grid.on('rowclick', this.getRowData, this);
// ① Row 더블클랙했을 경우
this.grid.on('rowdblclick', this.getRowData, this);
// ① Row가 선택됐을 경우
this.gsm.on('rowselect', this.rowSelect, this);
},

getRowData: function(thisGrid, rowIndex, eventObject){
// ② 클릭,컨텍스트,더블클릭시 해당 이벤트가 일어난 Row의 Record가져오기
var record = this.store.getAt(rowIndex) || this.gsm.getSelected();
// ③ 선택된 레코드에서 데이타 가져오기
alert(record.data.company + '\n' + record.get('company'));
},
// ④ rowSelectionModel에서 선택된 Row의 데이타 가져오기
rowSelect: function(selectionModel, index, record){
alert(record.data.company + '\n' + record.get('company'));
}

}
}
();
Ext.EventManager.onDocumentReady(BasicGridClass.init, BasicGridClass, true);


  • ① : Ext.grid.GridPanel에 등록된 Event  사용법에 유의할것
    this.grid.on('rowclick',this.getRowData(), this );
    잘못된표현  - argument가 없이 ()로만 사용하였을 경우 closure로 인식하여 바로 실행된다.

    this.grid.on('rowclick',this.getRowData(grid,rowIdex,eventObject), this);
    정확한 표현 - argument가 있을시는 closure로 인식하지 않음

    this.grid.on('rowclick',this.getRowData, this);
    정확한 표현

    this.grid.on('rowclick',function(grid,rowIndex,eventObject){this.getRowData(grid,rowIndex,evnetObjt);},this);
    정확한 표현


  • ② 선택된 Row에서 Record를 가져오는 방법에는 위오 같이 두가지 방법이 있다. 
    데이터스토어.getAt(Row의 Index Number) - 데이타 스토어의 인덱스에서 가져오는 방법
    셀렉션 모델.getSelected() - RowSelectionModel이나 CellSelectionModel에서 가져오는 방법

  • ③ Record에서 해당 데이타셋 가져오기 : 아래의 가져올DataIndex는 Columns에서 정의한 DataIndex를 가져온다.
    레코드.data.가져올DataIndex텍스트 
    레코드.get('가져올DataIndex텍스트)

  • ④ rowSelectionModel일 경우 rowselect 이벤트를 이용하여 선택된 Row의 값을 가져올수 있다.  클릭이나 더블클릭, 컨텍스트 메뉴와 달리 사용자로 부터 이벤트를 받는것이 아니라 데이터 스토어가 최초 로드 되거나 하였을 경우 해당 이벤트를 바로 자아 처리해줄 로직이 있을 경우에는 상당히 유용하다. 만약 예를 들어 초기 데이타 로드시 첫번째 Row가 선택된지 선택된 Row의 상세정보를 오른쪽 패널에 보여줄 경우 선택된 Row의 정보를 사용자의 어떠한 액션 없이 해당 레코드의 값을 받아 올수 있다. 데이타 스토어의 'load'이벤트를 사용할때 첫번째 Row를 선택하여 해당 선택된 row의 데이타를 뽑아내는 방법으로도 가능하다.

 


Post by 넥스트리소프트 데꾸벅(techbug)
, |

기본적으로 ExtJS의 레이아웃을 잡을때 Center Region은  Hide/show 및 Collapse/Expand가 되지 않으나 해당 Center Region을 감추는 방법에 대해서 기술한다. (꼼수). 단, 해당 꼼수(^^)는 fullscreen이거나 전체 사이즈가 고정되어 있다고 전제하에 동작한다.  만약 좌측패널이 전체 화면으로 center region을 감췄을때 브라우저 사이즈를 줄이거나 한다면 다시 syncSize해줘야 한다.



basicLayout.html

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="basicLayout.js"></script>
</head>
<body id="basicLayoutBody"></body>
// body container의 id(basicLayoutBody)를 이용하여 전체 화면사이즈를 가져온다.
</html>

 

basicLayout.js

LeftArea = function(viewport) {

    this.viewport = viewport;

    LeftArea.superclass.constructor.call(this, {
        region : 'west',
        title : 'WEST',
        width : 250,
        layout : 'fit',
        margins : '5 5 5 5',
        html : '좌측',
        tbar : [{
            text : 'CenterPanel 없애기',
            tooltip : {
                title : '전체화면',
                text : '화면을 전체/축소합니다.'
            },
            pressed : false,
            enableToggle : true,
            scope : this,
            toggleHandler : this.showHideCenterRegion
        }]
    })
};

Ext.extend(LeftArea, Ext.Panel, {
    // toggleHandler는 2개의 arguments를 가지고 있다. pressed는 클릭시 true , 그렇지 않을시 false반환
        showHideCenterRegion : function(btn, pressed) {
            if (pressed) {
                var fullWidth = this.viewport.getFullBody('width') - 10;
                this.setWidth(fullWidth);
                this.viewport.CenterPanel.hide();
                this.viewport.CenterPanel.ownerCt.doLayout();
            } else {
                this.setWidth(250);
                this.viewport.CenterPanel.show();
                this.viewport.CenterPanel.ownerCt.doLayout();
            }
        }

    });

BasicLayoutClass = function() {
    return {
        init : function() {
            Ext.QuickTips.init();
            this.viewport = new Ext.Viewport( {
                layout : 'border',
                items : [this.WestPanel = new LeftArea(this),
                        this.CenterPanel = new Ext.Panel( {
                            region : 'center',
                            title : 'CENTER',
                            layout : 'fit',
                            margins : '5 5 5 0',
                            html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
                        })]
            });

            this.viewport.doLayout();
            this.viewport.syncSize();
        },

        getFullBody : function(wh) {
            if (wh == 'width')
                return Ext.get('basicLayoutBody').getWidth();
            else if (wh == 'height')
                return Ext.get('basicLayoutBody').getHeight();
            else
                return Ext.get('basicLayoutBody').getWidth();
        }

    }
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);


Post by 넥스트리소프트 데꾸벅(techbug)
, |

Basic Concept

BCF 에서는 Ext.Viewport를 이용하여 Layout을 잡는것을 기본으로 한다. Ext.Viewport는 브라우저의 Viewport(<body></body>)영역에 자동으로 document Body에 렌더링하는 유틸리티 컨테이너 클래스이다. 브라우저의 크기가 변경되었을때 자동으로 레이아웃을 재계산하여 full-screen으로 렌더링한다.
주의 :  Viewport는 document.body에 다른 어떤 container와 같이 렌더링 할수 없다.  페이지당 단 한개의 Viewport만 사용된다.

 

Region Concept














north
   west          east
   center  
     
     south






브 라우저에서 위와 같이 그리드를 갖추고 있다고 하면 Viewport에서는 north, west,east,south,center 라는 영역(Region)을 갖는 5개의 Panel이 자동으로 생성된다. 위의 영역중 Center를 제외하고는 모두 Collapse/Expand 및 Hide/show 가 가능하다. 레이아웃을 생성할때에는 반드시 Center Region이 하나 이상(Center Region이 TabPanel 일 경우 여러개 존재) 존재하여야 한다.

기본적으로 아래 소스에서 사용되어질 레이아웃 종류는 BorderLayout이다. 이전 장을 반드시 숙지할것

최종 소스 :  다운로드

 

 

Step 1 : 기본 틀 잡기

 각 소스는 이미 설명했던 0010. Basic Structure0020.Basic Format을 이용하여 재사용성을 고려하여 만든다.

basicLayout.html

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="basicLayout.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

basicLayout.js

//BasicLayout 기본 클래스를 정의한다.
BasicLayoutClass = function() {
return {
init : function() {
this.viewport = new Ext.Viewport( {
layout : 'border',               //기본적으로 모든 viewport는 borderLayout으로 시작한다.
items : [
{
    region : 'north',    //상단 영역
    title : 'NORTH',    //상단 타이틀
    collapsible : true,    //상단 영역을 감추기 버튼 나타나기
    height : 200,        //상단 영역의 높이  (상/하단은 높이, 좌/우측은 너비)
    split : true,        //아래 영역과 splitting한다. (5px정도 차이를 둔다.)
    layout : 'fit',        //레이아웃 매니저는 fitLayout
    html : '상단'          //패널의 body HTML을 정의한다.
},
{
    region : 'south',
    title : 'SOUTH',
    collapsible : true,
    height : 200,
    split : true,
    layout : 'fit',
    html : '하단'
},
{
    region : 'east',
    title : 'EAST',
    collapsible : true,
    width : 250,
    split : true,
    layout : 'fit',
    html : '우측'

},
{
    region : 'west',
    title : 'WEST',
    collapsible : true,
    width : 250,
    split : true,
    layout : 'fit',
    html : '좌측'
},
{
    region : 'center',    //Center Region의 경우 Width,Height를 표시하지 않는다. (상/하, 좌/우 패널에서 모두 정의된 높이/너비)
    title : 'CENTER',
    layout : 'fit',
    html : '중간'
}
]
});
this.viewport.doLayout();    //viewport의 레이아웃을 그린다. (생략가능 : 되도록 작성 )
this.viewport.syncSize();    //viewport의 사이즈를 sync한다. (생략가능 : 되도록 작성 )

}
}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);   

 

 

 

Step 2 : 상/하단 및 우측 판넬 없애기

basicLayout.js

BasicLayoutClass = function() {
return {
init : function() {

this.viewport = new Ext.Viewport( {
layout : 'border',
items : [
// Step 1에서 정의된 좌측 패널 : 
//Step1에서는 Default 값인 new Ext.Panel() 이 생략됨
this.WestPanel = new Ext.Panel({
region : 'west',
title : 'WEST',
collapsible : true,
//초기 로딩시 collapse된 상태에서 보여진다.
collapsed : true,
width : 250,
split : true,
layout : 'fit',
margins:'5 0 5 5',
cmargins : '5 5 5 5',
html : '좌측'
}),
this.CenterPanel = new Ext.Panel({
region : 'center',
title : 'CENTER',
layout : 'fit',
margins:'5 5 5 0',
html : '중간'
})
]
});

this.viewport.doLayout();
this.viewport.syncSize();

}
}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);



Step3 : 좌측 패널을 별도의 Class로 분리하여 빼내기

basicLayout.js

LeftArea = function(){
//기존 좌측 판넬의 config option을 그래도 호출하여 사용
LeftArea.superclass.constructor.call(this,{
region : 'west',
title : 'WEST',
collapsible : true,
collapsed:false,
width : 250,
split : true,
layout : 'fit',
margins:'5 0 5 5',
cmargins : '5 5 5 5',
html : '좌측'
});
};

Ext.extend(LeftArea, Ext.Panel,{
//Ext.Panel을 상속받아 사용하였다, GridPanel일 경우는
//Ext.grid.GridPane을 사용하듯이 여러 Panel정의하여 사용가능
//Left Area의 Public,Private,Privileged 메쏘드를 정의한다.
});



BasicLayoutClass = function() {
return {
init : function() {
this.viewport = new Ext.Viewport( {
layout : 'border',
items : [
// LeftArea Class로 별도로 선언
this.WestPanel= new LeftArea(),
this.CenterPanel = new Ext.Panel({
region : 'center',
title : 'CENTER',
layout : 'fit',
margins:'5 5 5 0',
html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})
]
});

this.viewport.doLayout();
this.viewport.syncSize();

}
}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);


 

Step 4 : 좌측 패널에 ToolBar 붙여 오른쪽 패널의 Body를 Update하기

basicLayout.js

// 메인 클래스인 BasicLayoutClass를 인자로 넘겨받음 this.WestPanel = new LeftArea(this)
LeftArea = function(viewport){       

// 인자로 받은 private변수를 public 변수로 변환
this.viewport = viewport;   
 // LeftArea에서만 사용가능한 private 변수
var privateNum = 0;       

LeftArea.superclass.constructor.call(this,{
region : 'west',
title : 'WEST',
collapsible : true,
collapsed:false,
width : 250,
split : true,
layout : 'fit',
margins:'5 0 5 5',
cmargins : '5 5 5 5',
html : '좌측',
// 툴바 붙이기 TopToolbar의 경우 tbar, BottomToolbar의 경우 bbar로 표시
tbar : [           
// 툴바 안의 액션버튼 붙이기 new Ext.Action({})으로 표기가 생략
{                 
text:'클릭하세요',
scope:this,    //scope는 handler에서 사용할 이벤트영역 : this는 LeftArea 클래스를 가르킴               
handler:function(){
privateNum++;// LeftArea 클래스의 private변수
this.viewport.updateBody(privateNum);// this.viewport로 BasicLayoutClass.viewport로도 가능
}
}
]
})
};

Ext.extend(LeftArea, Ext.Panel,{
//Left Area의 Public,Private,Privileged 메쏘드를 정의한다.
});


BasicLayoutClass = function() {
return {
init : function() {
this.viewport = new Ext.Viewport( {
layout : 'border',
items : [
this.WestPanel= new LeftArea(this),    // 클래스 호출시 BasicLayoutClass를 인자로 넘겨줌
this.CenterPanel = new Ext.Panel({
region : 'center',
title : 'CENTER',
layout : 'fit',
margins:'5 5 5 0',
html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>',
tbar : [
this.firstMenu = new Ext.Action({    // 좌측 판넬에서 정의했던 툴바를 선언하는 다른 방식
text:'좌측패널 보였다/안보였다',
scope:this,
handler:function(){
if(this.WestPanel.isVisible()){        // 좌측패널의 보였다/안보였다
this.WestPanel.collapse(true);    // this.WestPanel로 좌측패널을 컨트롤
}else {
this.WestPanel.expand(true);
}
}
})
]
})
]
});

this.viewport.doLayout();
this.viewport.syncSize();
},

// 좌측 패널클래스 new LeftArea에서 직접 호출하는 Public Method
updateBody : function(privateNum){
Ext.get('_CONTENTS_AREA_').update('좌측판넬 클릭시 '+privateNum+'번 변했네요!');
}

}
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);





Post by 넥스트리소프트 데꾸벅(techbug)
, |

시작하기 전에
Edit section

이 튜토리얼을 공부하기 위한 가장 좋은 방법은 파이어폭스파이어버그를 바로 곁에 두는 것입니다. 이 방법으로 본 튜토리얼내의 예제를 즉시 테스트 해볼 수 있습니다.

만약 아직 가지고 있지 않다면 두 가지 모두 설치 하세요.

정의
Edit section

scope
1. (명사) 어떤 활동 혹은 작업 또는 힘을 가지거나 제어를 할 수 있는 지역 [1]
2. (명사) 프로그램 내에서 변수의 접근 범위; 예를 들어 어떤 함수가 다른 함수에서 생성된 변수를 사용할 수 있는지 여부. [2]

함수 실행 시 어디에 있는 변수에 접근이 가능 한지 찾는 중에 누군가가 "이건 스코프 문제야" 혹은 "이건 잘못된 스코프에서 실행 중이야" 라고 하거나 그와 비슷하게 이야기를 하면 우리는 어떻게 이것을 처리해야 할까요?

이제 가봅시다
Edit section

사실 자바스크립트 내에서 선언된 함수들은 객체에 소속된 메소드들 입니다. 아래와 같이 코드를 작성하더라도 말이죠:

function fn() {
    alert(11);
}

제가 농담을 하고 있다고 말하시겠죠. 하지만 이게 진짜 진실인지 아주 쉽고 간단하게 증명 해낼 수 있습니다. 이 예제를 위해서 어떠한 자바스크립트 파일이나 서버, 혹은 html이 필요치 않습니다. 단지 파이어폭스를 열고 파이어버그의 하단 윈도우를 열어서 콘솔 탭을 클릭하고 브라우저의 status 바 바로 위에 >>> 입력란에 코드를 입력하면 됩니다.

아래와 같이 입력하고:

function fn() { alert(11); };

엔터를 입력하면 아무 반응이 없습니다. 그렇죠? fn 함수만 선언 한 것이지요. 다음과 같이 입력합니다:

fn();

엔터를 입력하면 11이 적힌 경고 창을 볼 수 있습니다. 여기까지 잘 따라 오셨습니다. 다시 입력해봅시다:

window.fn();
this.fn();

같은 결과가 보이나요? 함수 fn은 window 객체의 메서드가 된 것을 알 수 있고 두 번째 라인은 this 변수가 window 객체를 가리키고 있다는 사실을 증명합니다. 이런고로 자신의 함수를 window.myFunction(...)처럼 호출할 필요가 없다는 것이죠. 이것은 편리하기도 하고 게으른 프로그래머들의 일을 덜어줍니다.

window 객체
Edit section

window 객체는 항상 존재하며 브라우저 윈도우 그 자체 라고 생각 하시면 됩니다. 이것은 document 객체와 전역(global)으로 선언된 모든 변수와 같은 다양한 객체를 담고 있습니다.

다시 파이어버그를 열어서 Script 탭으로 변경 시키고 우측에 "New watch expression..." 라고 적혀 있는 박스에 window 라고 입력 합니다. 그러면 window 객체에 어떤 것들이 있는지 살펴 볼 수 있습니다.

특히 방금 전 선언했던 fn 함수를 찾아 보시기 바랍니다.

각각의 frame 혹은 iframe은 자신만의 고유한 영역에서 독립적인 window 객체를 가지고 있습니다.

스코프(scope) 이해하기
Edit section

이제 조금 더 어려운걸 해봅시다. 다시 파이어버그의 Console 탭을 선택하고 다음과 같이 입력합니다:

var o1 = {testvar:22, fun:function() { alert('o1: ' + this.testvar); }};
var o2 = {testvar:33, fun:function() { alert('o2: ' + this.testvar); }};

우리가 무엇을 한 것일까요? 객체 o1o2 를 선언했고 이 둘은 같은 이름의 프로퍼티와 메서드를 가지고 있지만 각각의 프로퍼티는 다른 값을 가지고 있습니다.

아래와 같이 입력합니다:

fun();
window.fun();
this.fun();

에러가 발생하지요? 좋습니다. window 객체(this와 동일)는 fun 메소드를 가지고 있지 않습니다. 아래와 같이 해보세요:

o1.fun();
o2.fun();

이제 22와 33이 보이죠? 아주 좋습니다!

이제 이 복잡한 부분의 마지막입니다. 지금까지는 간단한 함수들을 사용했고 그래서 개별 객체의 타입(type)에 대해서 신경 쓸 필요가 없었습니다. 하지만 o1.fun 이 아주 기능이 많고 지난주부터 작업해서 이제 마지막 작업 단계에 있다고 칩시다. 코드 내에 산재해 있는 this 변수가 100 라인 정도 된다고 상상해 보자 구요. o1.fun을 호출(실행)시 this가 o2를 가리키고 있다면 어떻게 해결해야 할까요? 아래와 같이 입력해봅시다:

o1.fun.call(o2);

o1의 fun 메소드가 실행 중 일 때 강제로 변수 this 가 o2를 지정되게 설정된걸 보셨나요? 좀더 유식하게 말하면 다음과 같습니다: 메소드 o1.fun이 객체 o2의 스코프(scope)내에서 실행 된다

정리하면 스코프는 어떤 객체의 메서드로서 함수가 실행 중 일 때 this 변수의 값이다 라고 생각하시면 될 것 같습니다.

변수의 가시범위(Visibility)
Edit section

변수의 가시범위는 이전 단락인 스코프 항목과 밀접한 관계에 있습니다. 우리는 이미 변수(함수를 포함한 모든 변수)가 어떤 객체나 함수 밖에 선언되면 전역으로 등록 된다고 알고 있습니다. 기술적으로 다시 말하면 window 객체의 전역 프로퍼티가 되는 것이지요.

전역 변수들은 어디에서든지 보입니다;어떤 함수들의 안에서든 밖에서든. 만약 하나의 함수에서 전역 변수를 수정하게 되면 다른 함수들은 변경된 값을 보게 됩니다.

이제 객체가 자신만의 고유한 프로퍼티(앞서 testvar 예제처럼)를 갖는다는 걸 알 수 있게 되었습니다. 이것은 객체의 안과 밖에서 접근 가능한데, 맞는지 직접 시험해 봅시다:

alert(o1.testvar); // o1의 프로퍼티인 testvar를 외부에서 접근

내부에서 접근하는 것은 아까 두 개의 fun 메소드 시연에서 이미 해봤으므로 그냥 넘어 갑니다.

이제 마지막으로 남은 퍼즐의 조각은 지역 변수는 함수 내에서 var 키워드와 같이 선언 되어야 한다는 겁니다:

i = 44; 
function fn2() { 
    var i = 55; 
    alert(i); 
}
fn2();

어떤 것을 보았나요? 55 이지요. 변수 i 는 fn2 내에 선언 되어 fn2의 지역 변수가 되었고 44가 들어있는 전역 변수인 i 에 접근하지 않습니다.

하지만 이렇게 하면:

alert(i);

전역 변수 i 에 접근하기 때문에 44가 나오는 것을 볼 수 있습니다.

이 문서가 스코프와 변수 가시범위 공부에 많은 도움이 되길 바랍니다.

더 읽을만한 것들:


'Scripter > EXTJS' 카테고리의 다른 글

Extjs 기본 DomQuery  (0) 2008.02.18
Extjs 초보자를 위한 애플리케이션 레이아웃(2.02)  (8) 2008.02.17
ExtJS Tutorial  (1) 2008.02.17
ExtJS 시작하기  (8) 2008.02.17
ExtJS 개발하기 위해 선행되는 지식들  (7) 2008.02.15
Post by 넥스트리소프트 데꾸벅(techbug)
, |

ExtJS 시작하기

Scripter/EXTJS / 2008. 2. 17. 14:49

ExtJs란

ExtJs는 웹애플리케이션을 만들기위한 client-side-JavaScript framework이다. JackSolocum이  Yahoo! User Interface (YUI) library의 확장으로 만들기 시작했으며 Yui-ext란 Project명으로 빠르게 성장했다.


라이센스

ExtJS는 개발목적의 오픈소스로 제공되는 LGPL 라이센스와  로얄티프리 커머셜 라이센스로 구분된다.( licensing page )
상업용 라이센스는 SVN 접속이 허락되며 전화/팩스 및 이메일로 지원을 받을수 있는것이 고작(?)이다.


호환되는 서버 플랫폼

 PHP,Ruby on Rails (Embeded로 지원), .NET, Java 등 여러가지 서버 플랫폼을 지원한다. 사이트( server-side frameworks ) 를 참조하기 바란다.


ExtJS를 사용하기 위해 요구되는 라이브러리

1.0.1a 버전에서는  YUI, jQuery or Prototype/Script.aculo.us. 가 반드시 필요했으나 1.1에서는 Ext adapter를 채택하여 더이상 다른 외부 라이브러리 없이 구동할수 있다. 또한 외부 라이브러리를 사용하고 싶다면 해당 라이브러리 어답터가 별도로 구비되어 있으며 Extension Mode로 제공되는 기타 다른 라이브러리를 참조할수가 있다.

Base Library Include Order Get Library
Ext Standalone ext-base.js
ext-all.js (or your choice of files)
http://www.extjs.com/download
Yahoo! UI (.12+) yui-utilities.js (or your choice of YUI base files)
ext-yui-adapter.js
ext-all.js (or your choice of files)
http://developer.yahoo.com/yui/
jQuery (1.1+) jquery.js
jquery-plugins.js // required jQuery plugins
ext-jquery-adapter.js
ext-all.js (or your choice of files)
http://jquery.com/
http://docs.jquery.com/Plugins
Prototype (1.5+) /
Scriptaculous (1.7+)
prototype.js
scriptaculous.js?load=effects (or whatever you want to load)
ext-prototype-adapter.js
ext-all.js (or your choice of files)
http://www.prototypejs.org/
http://script.aculo.us/


3-party adapters와의 호환시 이슈사항

  • jQuery에서의 애니메이션기능(하이라이팅기능과 같은) 은 다음 사이트를 참조하라. References: 1, 2
  • DatePicker와 같은 jQuery버그를 해결하는 방안 References: 1, 2


에러상황에 따른 대처

  • 최대한 사용하는 버전의   API documentation. 를 이용한다.
  • 최신의 버전으로 유지하고 해당 사이트를 항상 방문해서 release나 bugpatch상황을 주시한다.
  • 브레이스 ( { )나 null을 리턴하는 경우 캐리지 리턴값을 주의한다.  아래와 같이 return 뒤에 한줄을 띈 ( { )는 에러를 발생시킨다. (헉~ 자바스크립트 포매터가 위험하겠군...)
var myExample = function()
{
return
{
foo: 'bar',
boo: 'far'
}
};

위의 코드는 아래와 같이 작성해야 한다.

var myExample = function()
{
return {
foo: 'bar',
boo: 'far'
}
};

만일 여의치 않다면 다음과 같이 대처한다.

  • 개발시에는 ext-all-debug.js 를 사용하여 해당 소스 추적이 쉽게 한다. 단 운영시에는 ext-all.js로 obfuscated된 버전으로 사용한다.
  • 되도록이면 스크립트 디버거를 사용하도록 한다.
    Firefox Firebug : 사용법( 
    How-to: using Firebug to debug your script  )
    IE Companion JS
  • http://blog.naver.com/techbug 사이트에서 확인가능 ^^;


다른 URL혹은 domain이 틀린 사이트에서 데이타를 가지고 오고 싶을때

아래와 같이 HttpProxy보다는 ScriptTagProxy를 사용한다.

var proxy = new Ext.data.HttpProxy({
url: '/DoSearch.php'
});
 
// Add the HTTP parameter searchTerm to the request
proxy.on('beforeload', function(p, params) {
params.searchTerm = searchValue;
});

Hidden Field 표시하는법

See http://extjs.com/forum/showthread.php?t=11698#2 or http://extjs.com/forum/showthread.php?t=6710#2


그리드의 셀에 클릭이벤트 핸들러 사용하는법(가장 많이 헷갈리고 많이 사용하는 것)

function(grid, rowIndex, columnIndex, e) {
var record = grid.getStore().getAt(rowIndex); // 레코드의 Row를 가져온다.
var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // 컬럼의 필드명을 가져온다.
var data = record.get(fieldName);
}


Ajax로 불러온 마크업중의 스크립트가 실행되지 않을때

Ext.Updater.defaults.loadScripts나  Updater.update 메쏘드나 Element.update 메쏘드 사용


그리드의 Row를 각각 틀리게 표현하고 싶을때 (getRowClass를 사용한 expander사용)

http://extjs.com/deploy/dev/docs/?class=Ext.grid.GridView&member=getRowClass

load()로 빈 데이타스토어(getCount()==0)를 불러올때

HttpProxy나 ScriptTagProxy와 같이 remote data 를 사용할때  Store.load() 는 비동기로 호출되며 서버블로킹없이 즉시 요청한다. "load""loadexception"이벤트로 쉽게 작업을 할수 있다. 이후 Grid Data Load에서 설명 ^^


Ext.get으로 컴포넌트 불러올때

Ext.get 는 단지 HTML Element의 객체를 가져온다. 즉, document.getElementById와 같다.
판넬이나 폼판넬등 컴포넌트를 불러올때는 Ext.getCmp를 사용해야 한다.


tree Node에서 아이콘 표시하는법

myNode.appendChild(new AsyncTreeNode({
text: "A folder",
iconCls: "folder"
});

CSS의 class seletor를 이용한다. (CSS 표준을 항상 염두해 둘것....)

.x-tree-node img.folder, .x-tree-node-collapsed img.folder{
background: url("../images/default/tree/folder.gif");
}
 
.x-tree-node-expanded img.folder {
background: url("../images/default/tree/folder-open.gif");
}

"Ext is undefined"라고 에러 메세지 뜨면

당근 ext-all.js 파일의 패쓰가 잘못됐으니 확인해야지....쩝..이런것도 적어야 하나..

<script type="text/javasscript" src="/somepath.js"></script>
<script type="text/javascript" src="/somepath.js"></script>

"XX has no properties"라는 에러가 떨어질때

해당 엘리먼트 id값을 찾을수 없다는 얘기다. 아래에서는 id='save-button'를 가진 객체를 찾을수 없다는 얘기다.

// constructors:
var tb = new Ext.Toolbar('toolbar');
 
// creating Element references:
var saveBtn = Ext.get('save-button');
<div id="toolbar"></div>
<input id="save-button" type="button" value="Save" />

extjs.com/s.gif를 계속 찾고 있다면 아래와 같이 한다.

1x1pixel짜리 투명이미지를 절케 표시하고 있다. 잘 해두도록...

Ext.BLANK_IMAGE_URL = '/images/ext/resources/images/aero/s.gif';  // 1.1
Ext.BLANK_IMAGE_URL = '/images/ext/resources/images/default/s.gif'; // 2.0

설명서를 잘 읽어보도록...반드시....


익칠이(IE)에서는 안돌아가는데 불여우(FF)에서 잘 돌아갈때

쉼표를 잘 찾아보도록....

testFunc = function() {
return {
titlebar: true,
collapsible: true, // <--- BOOM goes the comma! :D
}
}

JSLint를 이용하면 쉽게 찾을수 있다.



가끔씩 깨진 화면들이 나올때

이런경우 난감한...문제가 발생할수도 있는데 나의 경우는 전체 화면을 다시 그린적도 있다.. ㅠ.,ㅠ;

반드시 해당 컴포넌트(판넬,폼판넬, 콤보박스, 그리드 판넬)의 ID값을 반드시 입력해 준다. 또한 같은화면에 같은 ID값을 가진 객체가 있는지 반드시 확인한다.



다른 도움을 받고 싶을을 정도로 울고 싶을때

- ExtJS Forum에 들어가 도움을 청해본다. 그리고 Google 신에게 물어본다.  답이 다 있다. 다만 시간이 걸릴뿐이다.

영어 해석은 기본, 중국어은 옵션.. ^^;  

위의 내용만 알면 어느정도 ExtJS를 사용하면서 겪을 문제를 반정도는 해결할수 있다... 나의 경우 그랬으니깐... ㅡ.,ㅡ;


끝.


'Scripter > EXTJS' 카테고리의 다른 글

Extjs 기본 DomQuery  (0) 2008.02.18
Extjs 초보자를 위한 애플리케이션 레이아웃(2.02)  (8) 2008.02.17
Extjs scope에 대한 고찰  (2) 2008.02.17
ExtJS Tutorial  (1) 2008.02.17
ExtJS 개발하기 위해 선행되는 지식들  (7) 2008.02.15
Post by 넥스트리소프트 데꾸벅(techbug)
, |
넥스트리 포스팅했던글을 다시 정리하여 올리다.
사내 제품개발시 ExtJS를 사용할 일이 있어 내용을 보다가 해당사이트(www.extjs.com)을 번역하기로 맘먹었다.



매뉴얼번역: http://extjs.com/learn/Manual:Resources

ExtJS로 개발하기 위해서는 다음과 같은 것들이 필요하다. 반드시 읽어 볼것... 이미 앞의 EXTJS 시작하기 에서  맛뵈기(?)로 보았으니 이를 개발하기 위한 준비물들이 필요하겠다.


일반적인 JavaScript Learning Guide
Edit section

참고사이트
Edit section

  1. Mozilla Developer Center: Core JavaScript 1.5 Reference Manual
  2. Yahoo UI개발자인 Douglas Crockford's JavaScript
  3. Yahoo Developer Network : YUI Theater  ( 더글라스 크록포드의 동영상 강의 수록)
  4. Peter-Paul Koch의 QuirksMode
  5. http://home.cogeco.ca/~ve3ll/jstutor0.htm

그외 볼만한 사이트(Dead Trees)
Edit section

  1. JavaScript: The Definitive Guide by David Flanagan
  2. Pro JavaScript Techniques by John Resig
  3. Ajax and REST Recipes by Christian Gross
  4. ppk on JavaScript by Peter-Paul Koch
  5. Professional JavaScript for Web Developers by Nicholas C. Zakas
  6. Ajax Patterns and Best Practices by Christian Gross
  7. Dynamic HTML by Danny Goodman
  8. Head First Design Patterns by Freeman & Freeman

 

OO JavaScript 참조사이트

  1. How to achieve private, public, and privileged members in JavaScript, by Dustin Diaz.
    : Method scoping에 대한 좋은 설명
  2. OOP in JS, Part 1 : Public/Private Variables and Methods, by Gavin Kistner.
    : Scoping 및 prototyping에 대한 샘플
  3. A JavaScript Module Pattern, by Eric Miraglia in YUI Blog.
    : 더글라스 크록포드가 얘기한 module pattern에 대한 설명
  4. Again with the Module Pattern - reveal something to the world, by Christian Heilmann.
    : 모듈패턴
  5. JavaScript for Object-Oriented Programmers (Appendix B of the book AJAX in Action).

DOM

 

 

그외 기술

Yahoo! User Interface Library (YUI)

Ext JS 은 yui-ext에서 부터 시작되었다. 아래 사이트에서 YUI에 대해서 좀 봐두는것도 좋을지도..

  1. YUI Library
  2. ydn-javascript: Yahoo! User Interface Library Group
  3. YUI Blog

JSON 관련

 

 

Platform-Specific Resources

ExtJS를 서버사이드 프레임웍으로 사용한 녀석들.....

PHP
Edit section

대표적인 샘플코드는 모두 PHP코드로 되어 있는 걸 보면  PHP에서 개발하기 가장 쉬운것 같음.
ExtJS 내장 PHP프레임웍 :  Qcodo Development Framework

Java

Direct Web Remoting (DWR) : http://getahead.org/dwr

.NET

  • AjaxPro 는 .Net 을 이용한 서버사이드 Ajax
  • Jayrock  :  .Net에 JSON / JSON-RPC 를 이용 
  • Json.NET  : .Net에서 JSON을 쓰게 만들어주는 라이브러리 (PHP의 toJSON()이나, Struts의 JSON과 같은건가 보다... ㅡ.,ㅡ; ) 

Ruby on Rails

Ext plugin - ROR는 내장 플러그인으로 작동하므로 gem을 이용해 받으면 되겠다...  (찾기가 더 힘드네.. ㅡ.,ㅡ;)

IBM Lotus Notes/Domino

IBM's Lotus Notes/Domino : IBM 머찌다라는 말만..

 

 

통합개발툴(IDE)
Edit section

Eclipse Based IDEs

Eclipse 은 오픈소스 통합개발툴 (among other things!)이다. ExtJS개발을 위해 이클립스를 사용하기 위해서는 먼저 자바스크립트 사용할수 있는 플러그인을 설치해야 한다.
아래의 플러그인을 추천한다.

  • Aptana - JavaScript, HTML, CSS, PHP languages, FTP/SFTP 지원, RIA/Web2.0등 Ajax 관련 프로젝트에 적합 (느리다는 단점이 있음.. ㅠ.,ㅠ)
  • Spket - JavaScript, XUL, SVG, Laszlo, Silverlight,등 각 모듈 라이브러리 지원(http://blog.naver.com/techbug/150024518549 )
  • JSEclipse - JavaScript

위에 열거된 플러그 인들은 각자 자바스크립트 에디터를 포함하고 있다.  자바스크립트를 열때 "Open with"로 열어서 사용하도록...플러그인을 선택할때는 코드 assist를 지원하는지 확인해 봐야 할듯

 

Aptana + Spket + ExtJS(강추..)
  • Aptana다운로드 http://www.aptana.com/download_all.php
  • Aptana > Help > Software Updates > Find and Install > Search for new features to install > New remote site.. > "http://www.spket.com/update/"
  • Spket설치 ( Spket IDE Tutorial )
    1. Window > Preferences > Spket > JavaScript Profiles > New > "ExtJS"
    2. "ExtJS" 선택하고  "Add Library"에서 "ExtJS"선택
    3.  "ExtJS" 선택하고 "Add File", 에서 "./ext-2.0/source" 디렉토리에서 "ext.jsb"를 선택
    4. ExtJS profile을 디폴트로 선택 
    5. Aptana 플러그인을 새 시작
    6. 새로운 JS파일을 만들면 ExtJS의 Code completion options를 볼수 있다.

AptanaIDE

  • Aptana는 상당히 매력적인 플러그인다. 대부분의 Ajax Library를 제공한다.

 

 


 

다양한 디버깅툴

Firebug (FF에서 강추)

Firebug는 Firefox에 애드온되어 작용하는데 웹페이지의 내용(CSS,HTML,Javscript등)을 실시간으로 볼수있는 상당히 매력적인 도구이다.


Screencasts


Tutorials


다른 브라우저에서 Firebug console을 이용하기

HTTP Trafic Viewer

피들러는 서버와 PC사이의 HTTP 트래픽 로그를 볼수 있는 디버깅 프록시이다. IE전용이나 Firefox의 proxy고급옵션에서 사용할수 있으며 자신이 원하는대로 필터링 하여 사용할수 있다.

MS Script Editor

IE Web Developper

IE WebDeveloper는 인터넷 익스플로러 애드온 프로그램이다. 

Testing Data Generator

SQL,CSV,xml, excel등 테스트 데이타를 생성해준다.

http://www.benjaminkeen.com/software/data_generator/

YSlow

YSlow는  rules for high performance web sites를 기반으로 현재 페이지의 속도를 분석해준다. YSlow 는 Firefox 애드온 프로그램으로 인기가 있다.Firebug web development tool.

  • Performance report card
  • HTTP/HTML summary
  • List of components in the page
  • Tools including JSLint

http://developer.yahoo.com/yslow/

DebugBar - IE 플러그인 (강추)

  • DOM Inspector,
  • HTTP Inspector
  • Javascript Inspector and Javascript Console
  • HTML Validator
  • And many more features

http://www.debugbar.com/?langage=en


'Scripter > EXTJS' 카테고리의 다른 글

Extjs 기본 DomQuery  (0) 2008.02.18
Extjs 초보자를 위한 애플리케이션 레이아웃(2.02)  (8) 2008.02.17
Extjs scope에 대한 고찰  (2) 2008.02.17
ExtJS Tutorial  (1) 2008.02.17
ExtJS 시작하기  (8) 2008.02.17
Post by 넥스트리소프트 데꾸벅(techbug)
, |