블로그 이미지

카테고리

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

최근에 올라온 글

최근에 달린 댓글

'Extjs'에 해당되는 글 46건

  1. 2009.03.31 Examples Of ExtJS in Action 2
  2. 2009.01.06 Prototype.js Cross-Domain Ajax 플러그인 2
  3. 2008.11.12 Extjs 3.0 Roadmap 10
  4. 2008.08.07 Ext2.2 Release 11
  5. 2008.04.22 ExtJS 2.1 릴리즈 4
  6. 2008.04.16 Extjs Qtips 사용하기 2
  7. 2008.04.16 JSON에 대한 작은 단상 : JSON Diet 1
  8. 2008.04.16 Extjs 기본레이아웃에 각종 패널붙이기 12
  9. 2008.04.07 ExtJS를 이용한 EditorGrid 붙이기 2
  10. 2008.04.01 ExtJS 로드맵
  11. 2008.03.29 Ext JS Ext.ux.YoutubePlayer
  12. 2008.03.14 Extjs를 이용한 간단한 form submit
  13. 2008.03.07 Extjs를 이용한 ExtPHP, PHP-Ext 프로젝트 오픈
  14. 2008.03.06 Extjs Grouping Header Grid Plugins 13
  15. 2008.02.28 Extjs 크로스 도메인 관련
  16. 2008.02.27 Extjs 한국어로 나타내기 (locale설정) 9
  17. 2008.02.26 ExtJS을 이용한 RestFul한 통신하기 2
  18. 2008.02.26 GWT Ext 2.0 이 릴리즈됐다.
  19. 2008.02.26 ExtJS를 지원하는 IDE 개발이 시작되다.
  20. 2008.02.26 ExtTLD : XML을 이용한 Extjs 컴포넌트 생성
  21. 2008.02.26 드디어 Adobe AIR v1.0 & Flex3.0 릴리즈 2
  22. 2008.02.26 Extjs v2.02 릴리즈
  23. 2008.02.25 Extjs 기본레이아웃에 grid panel 붙이기
  24. 2008.02.25 Extjs Apatana ext2.0 plugin
  25. 2008.02.22 Extjs Grid panel 및 데이타 축출하기 8
  26. 2008.02.22 Extjs 레이아웃에서 center region 숨기기 꽁수
  27. 2008.02.21 Extjs 그리드안에서 프로세스바 구현하기 3
  28. 2008.02.21 Extjs 프로세스바 구현하기
  29. 2008.02.21 Extjs을 이용한 특정이벤트 컨텍스트 메뉴달기
  30. 2008.02.20 Extjs와 Adobe AIR를 이용한 Window.gadget만들기



Manning에서 나온 Extjs In Action 의 샘플파일을 링크걸어놓은 사이트가 있어 올린다.
고환율때문에 사기는 그렇고 아직 MEAP(Manning Early Access Program)상태라 어둠의 경로로 얻으려고 했으나 좋은건 사주는게... ㅡ.,ㅡ;



General Apps

  • CRM : [ demo ]
  • Ajax Yahoo IM : [ demo ]
  • Mail Center (Webmail) : [ demo ]
  • File Sports : [ demo ]
  • SeeMe (using Google Maps) : [ demo ]
  • ExtPlorer : [ demo ]
  • Group Office : [ demo ] (demo/demo)

Development Tools

Books

  • ExtJS In Action [ link ]
  • Learning ExtJS [ link ]



--------[ 이건 그냥 덤으로.. ^^ ] -------------------------------------------------------
IE7   [ 4.00 Kb ]
astar   [ 4.00 Kb ]
clock   [ 4.00 Kb ]
cssdropshadow   [ 4.00 Kb ]
gzoutput   [ 4.00 Kb ]
loopsbench   [ 4.00 Kb ]
pwdsecurity   [ 4.00 Kb ]
tris   [ 4.00 Kb ]
DOM.js   [ 2.42 Kb ]
ColorFader.html   [ 1.20 Kb ]
ColorFader.js   [ 3.01 Kb ]
CssSwitcher.js   [ 1.35 Kb ]
LoadVars.html.old   [ 2.48 Kb ]
FolderTree.swf   [ 58.21 Kb ]
LoadVars.html   [ 3.41 Kb ]
LoadVars.js.old   [ 9.11 Kb ]
LoadVars.js   [ 7.92 Kb ]
Sqlitei_Example.html   [ 12.56 Kb ]
SimpleSWF.js   [ 911 bytes ]
TEST_PHP4_MYSQL.zip   [ 1.51 Kb ]
TEST_SqliteMPTTA.zip   [ 1.37 Kb ]
adsense.html   [ 9.34 Kb ]
bigdollar.html   [ 2.96 Kb ]
browser_agent.html   [ 899 bytes ]
cssswitcher.html   [ 3.48 Kb ]
fap.example.js   [ 3.89 Kb ]
fakedom.html   [ 2.05 Kb ]
testf5.html   [ 1.16 Kb ]
flashPlayerVersion.html   [ 694 bytes ]
flashPlayerVersion.js   [ 1.11 Kb ]
jsbenchmark.html   [ 9.19 Kb ]
multipletest.html   [ 1.66 Kb ]
myfolder_flash.xml   [ 26.70 Kb ]
onload.html   [ 501 bytes ]
onload2.html   [ 1.89 Kb ]
onload3.html   [ 542 bytes ]
onload4.html   [ 2.61 Kb ]
onmousewheel.html   [ 33.91 Kb ]
onmousewheel.js   [ 1.18 Kb ]
othertest.swf   [ 810 bytes ]
othertest.txt   [ 652 bytes ]
simpleswf.html   [ 277 bytes ]
simpleswf.zip   [ 20.68 Kb ]
singletest.html   [ 1.17 Kb ]
singletest.swf   [ 765 bytes ]
singletest.txt   [ 656 bytes ]
singletesthalfheight.txt   [ 654 bytes ]
wheelscroll.gif   [ 1.90 Kb ]
testf5.swf   [ 629 bytes ]
testf5.txt   [ 648 bytes ]

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

Ext.Updater 깜빡거림 방지  (0) 2009.05.06
Extjs Core 3.0 Beta 릴리즈  (4) 2009.04.07
extjs RowAction 붙이기  (1) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Extjs 3.0 Roadmap  (10) 2008.11.12
Post by 넥스트리소프트 데꾸벅(techbug)
, |


예전 블로그에 포스팅한 글이 우연히 스쿨[PHPSchool]에서 보다가 발견했다.
그때에 2007년이였으니 Prototype 1.5.0 에서 적용되던 것이였는데...... 

extjs에서 크로스도메인 처리하던 localXHR.js와 같은 기능을 하는것이다.
localXHR에서 사용된 개념과 비슷하게 여기에서도 동적으로 <script>태그로 생성한다. 시간나실때 짬짬히 뜯어보시는것도... 괜찮을듯 하다.

    this.node = document.createElement('SCRIPT');
    this.node.type = 'text/javascript';
    this.node.src = arg.url;  <---- 

사용법은 간단한다. prototype.js 를 import한뒤 아래에 transport.js를 import해서 기존 Prototype.js의 Ajax.Request 클래스를 상속받아 쓰면 된다.

Prototype.js에서 사용할때와 같이

new Ajax.Request('myurl', {
    method: 'GET',
    crossSite: true,
    parameters: Form.serialize(obj),
    onLoading: function() {
        //things to do at the start
    },
    onSuccess: function(transport) {
        //things to do when everything goes well
    },
    onFailure: function(transport) {
        //things to do when we encounter a failure
    }
});

Prototype.js 1.6.0 버전에서 쓰려면 기존 transport.js 하단 상속부분을 다음과 같이 수정해 주면 된다.

[1.5.0 버전]
Ajax.Request.prototype = Object.extend(Ajax.Request.prototype,{
    initialize: function(url, options) {
        this.setOptions(options);
        this.transport = (!this.options.crossSite) ? Ajax.getTransport() : new scriptTransport;
        this.request(url);
        }   
});

[1.6.0 버전]
Ajax.Request.prototype = Object.extend(Ajax.Request.prototype, {
    initialize: function(url, options) {
        this.options = {
          method:       'post',
          asynchronous: true,
          contentType:  'application/x-www-form-urlencoded',
          encoding:     'UTF-8',
          parameters:   '',
          evalJSON:     true,
          evalJS:       true
        };
        Object.extend(this.options, options || { });

        this.options.method = this.options.method.toLowerCase();

        if (Object.isString(this.options.parameters))
          this.options.parameters = this.options.parameters.toQueryParams();
        else if (Object.isHash(this.options.parameters))
          this.options.parameters = this.options.parameters.toObject();
       
        this.transport = (!this.options.crossSite) ? Ajax.getTransport() : new scriptTransport;
        this.options.asynchronous = (!this.options.crossSite) ? this.options.asynchronous : false;
        //turns of the timed onLoad executer
        this.transport.respondToReadyState = this.respondToReadyState.bind(this);
        this.request(url);
        }
});





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

Extjs 3.0 Roadmap

Scripter/EXTJS / 2008. 11. 12. 16:42

벌써 2.2가 Release되고 3.0이 Extjs Forum글에서 언급되고 있다.
Extjs Roadmap을 포스팅한지가 지난 4월이였는데 이번주 Ajaxian사이트에 올라온 Extjs Designer동영상을 보니 다시금 불끈~~!!


Ext JS 2.1 (Released April 21, 2008)

View the release notes for full details.

  • Full REST support
  • Remote component loading samples
  • First class support for non-Ajax form submits
  • Upgrade all adapter libraries to current versions
  • Additional support for AIR platform
  • Grid filtering sample
  • StatusBar
  • Slider
  • CheckBox/Radio group controls (deferred to 2.2)

Ext JS 2.2 (Released August 4, 2008)

View the release notes for full details.

  • Styled Checkbox and Radio button controls
  • CheckBox/Radio group controls
  • Browser history support
  • XML TreeLoader sample
  • FileUpload field sample
  • MultiSelect and ItemSelector sample
  • Firefox 3.0 support
  • Slider (already released in 2.1)
  • Ext.Ajax enhancements (deferred to 3.0)
  • Support for reset styles scoped to Ext components only (deferred to 3.0 to coincide with additional CSS refactoring to enable easier custom theming)

Ext JS 3.0 (Early 2009)

  • All new lightweight, high-speed core base library
  • Flash Charting API
  • Ext.Direct - Remoting and data streaming/comet support
  • Integrated client-server data binding/marshaling of updates
  • ListView component
  • Enhanced Button and Toolbar components
  • ARIA/Section 508 accessibility improvements
  • CSS updates for reset style scoping and easier custom theming
  • Update the Ext event registration model
  • Ext.Ajax enhancements
  • Browser history support (released in 2.2)


출처 : http://extjs.com/products/extjs/roadmap.php

아래 동영상은 3.0 Release시 같이 출시된 designer tool 이다... 흠.. 이거 쓰면 괜찮겠는데.. Trust Form Designer랑 비슷하다.. ㅡ.,ㅡ;







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

extjs RowAction 붙이기  (1) 2009.03.31
extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Ext2.2 Release  (11) 2008.08.07
ExtJS 2.1 릴리즈  (4) 2008.04.22
RESTFul 한 ExtJS  (0) 2008.04.16
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Ext2.2 Release

Scripter/EXTJS / 2008. 8. 7. 10:30

일전에 포스팅한 extjs 로드맵에서 언급했듯이 extjs 2.2 가 릴리즈 됐다.

그리고 데꾸벅의 한글화 부분(ext-2.2/build/locale/ext-lang-ko.js)도 추가적으로 패키징되어 들어가 있다. 캬햐햐
/**
 * Korean Translations By nicetip
 * 05 September 2007
 * Modify by techbug / 25 February 2008
 */
트리로더 확장과 불여우2에서 스크롤문제(overflow)와 IE6 버그(relative)일때 스크롤문제부분(스쿨학우여러분 땡큐 많이 참조했어요.. ^^)도 부분적으로 채택이 됐네요.. 크크..
FF3 Beta1에서 에러나던 버튼위치부분은... 쿨럭.. ㅡ.,ㅡ;

2.2 (2008 여름) : 릴리즈노트

  • Ext.Ajax 기능향상
  • Ext컴포넌트만의 초점을 맞춘(scoped) REST style지원
  • XML을 포함한 추가적인 TreeLoader 데이타로딩
  • 슬라이더 컴포넌트
  • 파일업로드 필드

릴리즈노트에 나타난 새롭게 추가된 기능들은 다음과 같다.

[ CheckboxGroup / RadioGroup ]

... },{
    xtype: 'checkboxgroup',
    fieldLabel: 'Multi-Column (horizontal)',
    columns: 3,
    items: [
        {boxLabel: 'Item 1', name: 'cb-horiz-1'},
        {boxLabel: 'Item 2', name: 'cb-horiz-2', checked: true},
        {boxLabel: 'Item 3', name: 'cb-horiz-3'},
        {boxLabel: 'Item 4', name: 'cb-horiz-4'},
        {boxLabel: 'Item 5', name: 'cb-horiz-5'}
    ]
},{ ...

각 개별적인 item으로만 적용했던 라디어버튼과 체크박스를 그룹핑하여 만든 컴포넌트가 추가되었다. [샘플보기]
사용자 삽입 이미지



[히스토리컴포넌트 추가] [샘플보기]
listeners: {
    'tabchange': function(tabPanel, tab){
        // Ignore tab1 since it is a separate tab panel and we're managing history for it also.
        // We'll use its handler instead in that case so we don't get duplicate nav events for sub tabs.
        if(tab.id != 'tab1'){
            Ext.History.add(tabPanel.id + tokenDelimiter + tab.id);
        }
    }
}

사용자 삽입 이미지

위의 탭기능중 sub-tab3 를 선택한위 상단 탭을 클릭햇을 경우 다시 돌아왔을때 sub-tab3이 선택되어 있게 만들어준다. 이전 버전에서는 별도의 글로벌변수로 선언해준뒤 사용했었는데 이번 릴리즈에는 포함되어 나와서 상당히 편할듯하다..
히스토리 컴포넌트 사용시에는 URL 핸들링이 하는지 URL자체가 변하는 효과를 볼수 있다.
사용자 삽입 이미지


[MultiSelect / ItemSelector] [데모보기]
이 기능은 사용자 확장기능에 올라왔던 기능인데 이번 릴리즈에 포함되었있다.
전통적인 방식의 <select multiple> 기능을 extjs로 구현한 것이다.
사용자 삽입 이미지


[FileUploadField][데모보기]
사용자 확장기능에서 가장 많이 찾던 swfFileUpload에서 차용한듯한 소스가 이번 릴리즈에 포함되어 있다. ajax fileupload를 찾는 국내 사용자에게는 상당히 유용한 컴포넌트가 될듯하다. Form 컴포넌트에 포함되어 있다.
var fibasic = new Ext.form.FileUploadField({
    renderTo: 'fi-basic',
    width: 400
});


[XmlTreeLoader] [데모보기]
사용자 확장기능이다. 이전 treeLoader와 사용법은 비슷하다.


[GMapPanel][데모보기]
사용자 삽입 이미지
구글맵을 보여줄수 있는 panel이 추가되었다.
이것또한 사용자 확장기능이였으나 이번 릴리즈에 포함되었다.








사실 새롭게 추가된 기능들만 보자면 로드맵에 나왔던 내용들은 거의 없다.
향상된 기능들이라면 Firefox3 및 IE8 버전을 지원하다는 내용과 드래그앤드랍 기능(특정DnD영역그리드패널 Row의 드래그엔 드랍, 폼내널에서의 드래그엔드랍), 그리도 속도 향상, status기능등이 많이 향상되었다.

또한 축약어 들이 많이 등장했는데 Ext.MessageBox()를 msg()와 같이 표현한다던가, 브라우저 버전에 대한 글로벌 변수 Ext.isGecko2, Ext.isGecko3 등이 더 추가되었다.

그외 아래 링크로 확인하시기 바랍니다.






오픈소스 라이센스에 대한 이해~!! 는 다음 파일을 참조하세요








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

extjs 2.2의 IE에서 iframe document load 버그패치  (0) 2009.01.21
Extjs 3.0 Roadmap  (10) 2008.11.12
ExtJS 2.1 릴리즈  (4) 2008.04.22
RESTFul 한 ExtJS  (0) 2008.04.16
Extjs Qtips 사용하기  (2) 2008.04.16
Post by 넥스트리소프트 데꾸벅(techbug)
, |

ExtJS 2.1 릴리즈

Scripter/EXTJS / 2008. 4. 22. 09:55

프로젝트가 바빠서 한동안 들리지 않았더니만...
extjs 2.1 이 릴리즈됐다.

많은 변화가 있었는데 가장 큰 변화라면 라이센스정책이 기존의 LPGL에서 3가지로 구분되어 나뉘어졌다. YUI에서 출발한 extjs는 아닐거라고 생각했는데 유료로 전환해 버렸네~
우선 가장 이번 2.1버전에서 달라진 점(Release Note)을 찾아본다면 아래와 같다.

  1. 라이센스 정책 변화
  2. Full REST 지원
  3. Ext.StatusBar 컴포넌트 추가 (IE7,FF에서 보안정책때문에 애먹어서 구현하고 있었는데.. ㅠ.,ㅠ)
  4. 컴포넌트 Config옵션 Remote Loading지원
  5. Grid 필터링 추가
  6. Layout Browser Sample 추가
  7. Spotlight 기능추가
  8. 아이콘 팝업 추가
  9. 슬라이드기능 추가
  10. validation기능 추가(향상이라고 해야 하나?)
  11. Card Panel Layout추가 (Wizard기능)
  12. Markup re-Rendering기능 향상

라이센스정책
많은 개발자들의 활발한 활동과 커뮤니티의 활성화로 Ext가 안정화 되면서 Ext개발자와 공헌자들이 계속 개발을 할수 있게 하기위해 라이센스 정책을 변경함 ( 뭐~ 다들 일케 말하더라~ )  여기서 말하는 듀얼 라이센스 정책이라는것은 개발된 애플리케이션의 소스를 공개하지 않는다면 commercial License이고 공개한다면 Open Source가 된다는 거다.. ㅡ.,ㅡ;
둘다 Open Source이긴 하지만.. 돈있으면 소스공개하지 말고 commercial로 쓰고 돈없으면 소스 공개해서 OpenSource로 까발리라는 얘기다~~

1. Commercial License :
 Quid Pro Quo 원칙에 근거하여 배포할 목적으로 애플리케이션을 개발한다면 적정한 수 만큼의 라이센스를 구입해야 함. (듀얼 라이센스정책)
 - 구매한 소스에 대해서는 상업용으로 전환 가능.
  - GPL에 의해 소스를 수정할수 있음.
2. Open Source
 기존의 LGPL에서 GPL에 따른 라이센스 정책
3. OEM / Reseller License




샘플보기 : http://www.extjs.com/deploy/dev/examples/samples.html


헉... Firebug를 켠 상태로 들어갔더니만 이런 메세제가 ㅡ.,ㅡ; firebug detecting기능을..

사용자 삽입 이미지





그리드 필터링 기능 : 언제가 사용자중에 이 기능을 extesion으로 올렸던것이 추가되었다.

사용자 삽입 이미지






Sportlight기능 : 설치하기나 step으로 나눈것 설명할때 편하겠군효~~
사용자 삽입 이미지






Drag & Drop 기능향상

사용자 삽입 이미지









슬라이드 기능 (기존 scriptaculous사이트에서 보던 예제를 extjs로... ㅡ.,ㅡ;)


사용자 삽입 이미지






StatusBar 기능
사용자 삽입 이미지






흠.. StatusBar기능 괜찮네~ 이것도 사용자 extension이였던 기능이였는데..
사용자 삽입 이미지







개발시 가장 까다로웠던 validation check기능이 많이 추가되었다.
사용자 삽입 이미지







CardLayout을 이용한 Wizard기능 추가
사용자 삽입 이미지






흠... 이건.. 추가라고 할것도 없이 사용자 편의성을 위해 아이콘추가 ㅡ.,ㅡ;
사용자 삽입 이미지







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

Extjs 3.0 Roadmap  (10) 2008.11.12
Ext2.2 Release  (11) 2008.08.07
RESTFul 한 ExtJS  (0) 2008.04.16
Extjs Qtips 사용하기  (2) 2008.04.16
Extjs 기본레이아웃에 각종 패널붙이기  (12) 2008.04.16
Post by 넥스트리소프트 데꾸벅(techbug)
, |

HTML작업을 하다 보면 Tooltip을 사용할 일이 많아진다.
예를 들어

<img src="이미지 주소" alt="이미지설명"  title="이미지설명" />

<a href="" title="링크설명">

<table summary="테이블에 대한 간략한 소개">

 와 같이 사용하는데 사용자 마음대로 원하는 형태로 나오기가 어렵다는 단점이 있다. 또한 일정시간이 지나면 자동으로 없어지므로 사용하기가 까다롭다.

단 이미지가 없을 경우 엑박(엑스박스)을 없애기 이미지에 대한 내용이 들어가게 해주는 기능이 있으나 이럴경우 ExtJS를 사용하여 다음과 같이 작성해 줄수 있다.

 MARKUP

<div id="아이디" ext:qtitle="툴팀 타이틀" ext:qtip="데꾸벅<br />머리아포">

 JAVAScript (ExtJS)

Ext.QuickTips.init();
Ext.QuickTips.getQuickTip();

 과 같이 적어주면 자동으로 Ext.Tooltip() 과 같은 효과를 볼수 있다.


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

ExtJS 2.1 릴리즈  (4) 2008.04.22
RESTFul 한 ExtJS  (0) 2008.04.16
Extjs 기본레이아웃에 각종 패널붙이기  (12) 2008.04.16
ExtJS를 이용한 EditorGrid 붙이기  (2) 2008.04.07
ExtJS 로드맵  (0) 2008.04.01
Post by 넥스트리소프트 데꾸벅(techbug)
, |

JSON은 XML보다 더 심플하게 작성하는 방법에 대한 아이디어가 화장실에서 문득.. ㅡ.,ㅡ;
XML보다 좀더 구조화되고 심플하게 작성!!

개념은 일반적으로 Data를 구조화하기 위해 필요한 태그나 엘리먼트들을 줄이면 상당히 적은양의 데이타로 빠르게 통신할수 있을텐데라는 생각에서 출발한다.
Http통신이 원래는 RESTFul한 사상에서 출발해서 지금은 대부분 잘못쓰고 있는것처럼, JS(simple)ON 철학이 이런것에서 출발한것인데 잘못 쓰고 있다는 생각이... 아주 문득 들었다.

기존 JSON :

{
    "users": [
        {"first": "Homer",
         "last": "Simpson"},
        {"first": "Hank",
         "last": "Hill"},
        {"first": "Peter",
         "last": "Griffin"}
    ],
    "books": [
        {"title":"JavaScript",
         "author": "Flanagan",
         "year": 2006},
        {"title": "Cascading Style Sheets",
         "author": "Meyer",
         "year": 2004},
        {"title": "The C Programming Lanaguage",
         "author": "Kernighan",
         "year": 1988},
        {"title": "The Scheme Programming Language",
         "author": "Dybvig",
         "year": 2003},
        {"title": "Design Patterns",
         "author": "The Gang of Four",
         "year": 1995}

    ]

}

위와 같이 first,last 라는 attribute값들이 들어가는데 이것만 별도로 처리하여 배열식으로 처리한다면...

다이어트된 JSON :

{
    "users": {
        "cols": ["first", "last"],
        "rows": [["Homer", "Simpson"],
                 ["Hank", "Hill"],
                 ["Peter", "Griffin"]]
    },
    "books": {
        "cols": ["title", "author", "year"],
        "rows": [["JavaScript", "Flanagan", 2006],
                 ["Cascading Style Sheets", "Meyer", 2004],
                 ["The C Programming Language", "Kernighan", 1988],
                 ["The Scheme Programming Language", "Dybvig", 2003],
                 ["Design Patterns", "Gang of Four", 1995]]
    }
}


반복되는 키들을 줄임으로써 리소스를 상당량 줄일수 있겠다.문제는 서버상에서 JSON 생성할때랑 프리젠테이션 레이어에서 처리해줄때 약간 신경을 더 써야 한다는거...

흠.. Extjs에서는 어케 하지? ㅡ.,ㅡ;

Post by 넥스트리소프트 데꾸벅(techbug)
, |
사내 위키에 올렸던 글을 다시 정리하여 포스팅하다!
기본 레이아웃 viewport에 각 Region마다 서로다른 패널(panel)을 붙여봅니다.

Basic Concept

각 각의 패널(Panel)에 대하여 알아본다. 기본적인 레이아웃은 이미 앞장에서 설명했던 소스를 이용하여 각각의 Region에 서로 다른 Panel을 붙여본다. 서로다른 패널들이 레이아웃에서 어떻게 붙고(append)되고 자동으로 생성되며(Create) 삭제(remove and destroy)되는지에 대해서 알아본다. 최종적으로 재사용할 컴포넌트모듈을 작성하여 적용하는 방법(Step4)을 소개한다.
각 패널들에 대한 상세한 옵션에 대해서는 장차 설명하기로 하고 여기에서는 기본적인 레이아웃에 패널을 넣는 방식에 대해서 알아본다.

최종소스 :







Step 1.  Basic Layout and BoxComponent

우선 이전에 포스팅했던 기본레이아웃 그리기를 준비한다.

[basicLayout.html]

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="../../../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]

// 좌측 패널 (region : west ) 클래스
WestArea
= function() {
 WestArea.superclass.constructor.call(this, {
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  layout : 'fit',
  margins : '5 0 5 5',
  cmargins : '5 5 5 5',
  html : '좌측'
 })
};
Ext.extend(WestArea, Ext.Panel, {});

// 우측 패널 (region: east) 클래스
EastArea
= function() {
 EastArea.superclass.constructor.call(this, {
  region : 'east',
  title : 'EAST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  layout : 'fit',
  margins : '5 5 5 0',
  cmargins : '5 5 5 5',
  html : '우측'
 })
};
Ext.extend(EastArea, Ext.Panel, {});

// 중앙 컨텐츠 패널 (region : center) 클래스
CenterArea
= function() {
 CenterArea.superclass.constructor.call(this, {
  region : 'center',
  title : 'CENTER',
  layout : 'fit',
  margins : '5 0 5 0',
  html : '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
 })
};
Ext.extend(CenterArea, Ext.Panel, {});

// 메인 클래스
BasicLayoutClass = function() {
 return {
  init : function() {
   Ext.QuickTips.init();
   this.viewport = new Ext.Viewport( {
    layout : 'border',
    items : [this.WestPanel = new WestArea(),
      this.EastPanel = new EastArea(),
      this.CenterPanel = new CenterArea()]
   });

   this.viewport.doLayout();
   this.viewport.syncSize();
  }
 }
}();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);


 위와 같이 각각의 region마다 별도의 파일로 클래스를 관리한다.

  • WestArea.js
  • EastArea.js
  • CenterArea.js
  • basicLayout.js

사용자 삽입 이미지










Step 2.  Tab Panel - center regionEdit section

중앙컨텐츠 패널(center region : CenterArea.js )에 여러개의 TabPanel을 넣어보자

CenterArea = function(viewport) {
 this.viewport = viewport;
 CenterArea.superclass.constructor.call(this, {
  region : 'center',
  title : false,
  // TabPanel일 경우 Nested된 Panel의 타이틀이 영역을 차지하므로 title은 항상 false가 되어야함.
  margins : '5 0 5 0',
  deferredRender : false, // CotainerLayout이 로드될때 defer없이 렌더링한다.
  tabPosition : 'top', // Tab이 패널의 위쪽에 위치하게 한다. ( 'top'/'bottom'
  // 두가지를 지원하며 scrolling TabPanel은 'top'만
  // 지원한다.)
  activeTab : 0, // 처음 로드(렌더링)될때 첫번째(tab index : 0 ) 탭이 activate되게 한다.
  // 기본 Panel을 사용하거나 다른Panel이 Nested 될수 있다.
  items : [this.FirstPanel = new Ext.Panel( {
   id : 'CENTER_TAB_01',
   title : '첫번째 탭',
   autoScroll : true,
   html : "첫번째"
  }), this.SecondPanel = new Ext.Panel( {
   id : 'CENTER_TAB_02',
   title : '두번째 탭',
   autoScroll : true,
   html : "두번째"

  }), this.ThirdPanel = new Ext.Panel( {
   id : 'CENTER_TAB_03',
   title : '세번째 탭',
   autoScroll : true,
   closable : true,
   html : "세번째"
  })]
 })
  // activate - 탭이 활성화 될때 일어나는(fire)되는 이벤트
 this.SecondPanel.on('activate', function(panel) {
  alert(panel.title + '이 활성화 되었습니다.' + panel.getId());
 }, this);

 // deactivate - 탭이 비활성화될때 일어나는 이벤트
 this.SecondPanel.on('deactivate', function(panel) {
  alert(panel.title + '이 비활성화 되었습니다.' + panel.getId());
 }, this);

 // beforedestory - 탭이 닫히기전에 일어나는 이벤트
 this.ThirdPanel.on('beforedestroy', function(panel) {
  alert(panel.title + '이 닫혔습니다.');
 }, this);
};
Ext.extend(CenterArea, Ext.TabPanel, {});

탭패널의 경우 한 화면에 여러개의 패널(Panel)이 필요할 경우 유용하며 연관된 다를 데이타를 사용할때 상당히 유용하다. 기본적인 event는 위에서는 3가지만 열거했지만 위 3가지 만으로도 충분히 다양한 효과를 낼수 있을 것이다. 또한 각 탭 마다 다양한 패널을 추가할수 있으며 기본 ContainerLayout도 추가할수 있으므로 여러가지형태의 레이아웃을 잡을때 상당히 유용하다.
예를 들어 이전장에서 기본레이아웃에 그리드를 추가하는 방법을 배웠는데 첫번째 탭패널에 그리드 레이아웃을 넣어 해당 레이아웃을 테스트 해보기 바란다. 또한 각 탭이 activate될때 그리드의 데이타스토어를 다시 로드할수 있도록 이벤트를 걸어주거나 다른 액션을 취할수 있다.

참고: Ext.TabPanel은 Ext.layout.CardLayout을 사용하므로 deferredRender의 경우 Ext.layout.CardLayout의 deferredRender를 참조한다.

사용자 삽입 이미지



위의 소스중 탭패널이 바로 borderLayout에 속한(region:center)일 경우에는 title 이 나타나지 않는데 기본 Layout에 Nested된 TabPanel일 경우는 기본 Layout의 region에 title을 넣으면 된다. 실제로 작업을 하다보면 위의 방법보다 아래방법을 많이 사용하게 된다. 탭패널안의 탭패널이 들어갈 경우도 발생할수 있으므로 Nested된 패널로 기본 cotainerLayout을 잡은후 넣는 방법이 좋다.

CenterArea = function(viewport) {
 this.viewport = viewport;
 CenterArea.superclass.constructor.call(this, {
  region : 'center',
  title : 'CENTER',
  layout : 'fit',
  margins : '5 0 5 0',
  items : [ // Nested된 TabPanel
   new Ext.TabPanel( {
    deferredRender : false,
    tabPosition : 'bottom',
    activeTab : 0,
    border : false,
    enableTabScroll : true,
    items : [this.FirstPanel = new Ext.Panel( {
     id : 'CENTER_TAB_01',
     title : '첫번째 탭',
     autoScroll : true,
     html : "첫번째"
    }), this.SecondPanel = new Ext.Panel( {
     id : 'CENTER_TAB_02',
     title : '두번째 탭',
     autoScroll : true,
     html : "두번째"

    }), this.ThirdPanel = new Ext.Panel( {
     id : 'CENTER_TAB_03',
     title : '세번째 탭',
     autoScroll : true,
     closable : true,
     html : "세번째"
    })]
   })]
  })
};
Ext.extend(CenterArea, Ext.Panel, {}); //기본 ContainerLayout

  위 소스를 응용하면 탭안의 탭패널, 그안의 탭패널을 넣는 방식으로 계단식 탭패널이 가능하다.

사용자 삽입 이미지








Step 3. Accordian Panel and Tree Panel - west regionEdit section

좌측패널(WestArea.js, west region)에 Accordian Panel을 붙여본다. 각각의 패널에는 tree Panel 및 여러 Panel이 올수 있다.

WestArea = function(viewport){
 this.viewport = viewport;
 WestArea.superclass.constructor.call(this,{
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed:false,
  width : 300,
  minSize:100,
  split : true,
  margins:'5 0 5 5',
  cmargins : '5 5 5 5',
  // Accordion Layout을 사용
  layout:'accordion',
  // Accordion Layout의 config option
  layoutConfig:{
   // 각각의 패널 처음 로드시 collapse먼저 하겠는지 확인
   collapseFirst:false,    
   // 패널 로드시 animated되게 할것인지
   animate:true,      
   // 타이틀을 눌렀을때 collapse할것인지
   titleCollapse: true,    
   // 각각의 패널중 activate될때 맨 처음으로 오게 할것인지
   activeOnTop: false,    
   // 패널의 내용에 상관없이 패널의 높이를 owerContainer의 높이와 맞출것인지 처리
   fill:true      
  },
  items: [
   this.FirstPanel = new Ext.Panel({
    title:'첫번째',
    border:false,
    html:'<p>트리가 들어갈 곳입니다.</p>'
   }),
   this.SecondPanel = new Ext.Panel({
    title:'두번째',
    border:false,
    html:'<p>두번째 패널의 내용이 들어갑니다.</p>'
   })
  ]
 })
};
Ext.extend(WestArea, Ext.Panel,{ });

accordion Layout으로 바꿨으니 이제 각각의 패널에 알맞은(?) 패널들을 넣어보자. 우선 첫번째 패널에 TreePanel을 넣어보자

WestArea = function(viewport) {
 this.viewport = viewport;
 WestArea.superclass.constructor.call(this, {
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  margins : '5 0 5 5',
  cmargins : '5 5 5 5',
  layout : 'accordion',
  layoutConfig : {
   collapseFirst : false,
   animate : true,
   titleCollapse : true,
   activeOnTop : false,
   fill : true
  },
  items : [this.FirstPanel = new Ext.tree.TreePanel( {
   title : '첫번째',
   border : false,
   layout : 'fit',
   loader : new Ext.tree.TreeLoader( {
    dataUrl : 'menu.js',
    baseParams : {}
   }),
   rootVisible : true,
   lines : true,
   autoScroll : true,
   root : new Ext.tree.AsyncTreeNode( {
    id : '_MENU_PANEL_',
    text : '트리메뉴의 Root',
    draggable : false,
    expanded : true
   })
  }), this.SecondPanel = new Ext.Panel( {
   title : '두번째',
   border : false,
   html : '<p>두번째 패널의 내용이 들어갑니다.</p>'
  })]
 })
};
Ext.extend(WestArea, Ext.Panel, {});

사용자 삽입 이미지







Step 4. Common Modules

이미 Step1,2,3 에서 접해봐서 알겠지만 각각의 패널에는 기본 ContainerLayout만 있으면 어떤 패널이던지 가져다 붙일수 있다는것을 알았을 것이다.
그럼 각각의 공통적으로 사용되는 패널의 경우 공통모듈화 하여 사용자가 정의한 모듈로써 사용하게 된다면 어느 패널에서든지 마음대로 붙였다 떼었다 할수 있을 것이다.


[basicLayout.html]

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="../../../resources/css/ext-all.css" />
<script type="text/javascript" src="../../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../../adapter/ext/localXHR.js"></script>
<script type="text/javascript" src="../../../ext-all.js"></script>
<script type="text/javascript" src="Modules.js"></script>
<script type="text/javascript" src="WestArea.js"></script>
<script type="text/javascript" src="EaseArea.js"></script>
<script type="text/javascript" src="CenterArea.js"></script>
<script type="text/javascript" src="basicLayout.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

[Modules.js]

Ext.namespace("Ext.techbug");

// 공통으로 사용되는 트리메뉴
Ext.techbug.TreeMenu = function(config) {
 Ext.apply(this, config);
 Ext.techbug.TreeMenu.superclass.constructor.call(this, {
  loader : new Ext.tree.TreeLoader( {
   dataUrl : 'menu.js',
   baseParams : {}
  }),
  rootVisible : true,
  lines : true,
  autoScroll : true,
  root : new Ext.tree.AsyncTreeNode( {
   id : '_MENU_PANEL_',
   text : '트리메뉴의 Root',
   draggable : false,
   expanded : true
  })
 })
};
Ext.extend(Ext.techbug.TreeMenu, Ext.tree.TreePanel, {});

[WestArea.js]

WestArea = function(viewport) {
 this.viewport = viewport;
 WestArea.superclass.constructor.call(this, {
  region : 'west',
  title : 'WEST',
  collapsible : true,
  collapsed : false,
  width : 300,
  minSize : 100,
  split : true,
  margins : '5 0 5 5',
  cmargins : '5 5 5 5',
  layout : 'accordion',
  layoutConfig : {
   collapseFirst : false,
   animate : true,
   titleCollapse : true,
   activeOnTop : false,
   fill : true
  },
  items : [
  // 공통모듈로 사용하는 트리메뉴로 변경
    this.FirstPanel = new Ext.techbug.TreeMenu( {
     title : '첫번째',
     border : false,
     layout : 'fit'
    }),
this.SecondPanel = new Ext.Panel( {
     title : '두번째',
     border : false,
     html : '<p>두번째 패널의 내용이 들어갑니다.</p>'
    })]
 })
};
Ext.extend(WestArea, Ext.Panel, {});







 

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

RESTFul 한 ExtJS  (0) 2008.04.16
Extjs Qtips 사용하기  (2) 2008.04.16
ExtJS를 이용한 EditorGrid 붙이기  (2) 2008.04.07
ExtJS 로드맵  (0) 2008.04.01
ExtJS를 이용한 Password Meter  (0) 2008.04.01
Post by 넥스트리소프트 데꾸벅(techbug)
, |
사내(넥스트리)위키에 올렸던 자료를 다시 정리하여 포스팅한다.

일반그리드와 달리 에디터그리드의 경우 해당 Cell에 해당하는 값을 변경할수 있다. 각각의 선택된 셀값과 추가하는 방법 및 해당 값들을 셋팅하는 방법, ActionComplete후 dirtyFlag를 삭제하는 방법등에 대해서 알아보자! 에디터 그리드는 이미 이전장에서 사용되었던 기본그리드를 사용하였으므로 반드시 이전장을 참조하도록 한다. 추가적으로 FormPanel에 대하여 미리 알아 보도록 한다.

최종소스 :

사용자 삽입 이미지


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>
</body>
</html>

basicGrid.js

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'
                }, this.myRecordObj = Ext.data.Record.create([ {
                    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.EditorGridPanel( {
                store : this.store,
                columns : [ {
                    id : 'company',
                    header : "Company",
                    width : 160,
                    sortable : true,
                    dataIndex : 'company',
                    editor : new Ext.form.TextField( {
                        allowBlank : true
                    })
                }, {
                    header : "Price",
                    width : 75,
                    sortable : true,
                    renderer : 'usMoney',
                    dataIndex : 'price',
                    editor : new Ext.form.NumberField( {
                        allowBlank : false,
                        allowNegative : false
                    })
                }, {
                    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',
                    editor : new Ext.form.DateField( {
                        allowBlank : false,
                        minValue : '08/21/00',
                        disabledDays : [0, 2, 6],
                        disabledDaysText : '이날짜는 선택안되요'
                    })
                }

                ],
                stripeRows : true,
                autoExpandColumn : 'company',
                loadMask : {
                    msg : '데이타 로드중'
                },
                clicksToEdit : 1, // Edit하기 위해 Cell을 클릭해야 하는 횟수 (최대2, 최소1)
                sm : new Ext.grid.RowSelectionModel( {
                    singleSelect : true
                }),
                view : new Ext.grid.GridView( {
                    forceFit : true,
                    enableRowBody : true,
                    emptyText : 'No Record found'
                }),
                height : 350,
                width : 1024,
                title : '기본 그리드',
                tbar : [
                        {
                            text : '저장',
                            scope : this,
                            handler : this.saveAllChanges
                        },
                        '-',
                        {
                            text : '추가',
                            scope : this,
                            handler : this.addRecord
                        },
                        '-',
                        {
                            text : '삭제',
                            scope : this,
                            handler : function() {
                                var selectedKeys = this.grid.selModel.selections.keys;
                                if (selectedKeys.length > 0) {
                                    Ext.MessageBox.confirm('확인',
                                            '정말 삭제할래요? 삭제하면 복구안돼요..',
                                            this.deleteRecord, this);
                                } else {
                                    alert('삭제할 Row를 선택하세요');
                                }

                            }
                        }, '-', {
                            text : '새로고침',
                            scope : this,
                            handler : function() {
                                this.store.reload()
                            }
                        }]
            });

            this.grid.render('DIV_GRID_HERE');
            this.store.load();
            this.grid.on('afteredit', this.afterCellEditEventHandler, this); // Cell을
            // 변경후
            // 일어나는
            // 이벤트
        },

        // CEll변경후 일어나는 이벤트
        afterCellEditEventHandler : function(editEvent) { // Cell을 변경후 일어나는
            // 이벤트
            var gridField = editEvent.field;
            var gridValue = editEvent.value;
            editEvent.record.set('company', editEvent.record.get('company')
                    + '변경필트:' + gridField + '변경된값' + gridValue);
            this.updateCell(editEvent);
        },

        // 삭제하는 루틴
        deleteRecord : function(btn) {
            if (btn == 'yes') {
                var selectedRows = this.grid.selModel.selections.items;
                var selectedKeys = this.grid.selModel.selections.keys;
                var selectedRecord = this.grid.selModel.getSelected();
                alert(selectedRecord.get('company') + '\n' + selectedRows
                        + '\n' + selectedKeys + '\n지우는 Ajax Call 후에 다시 리로드한다.');
                this.store.reload();
            }
        },

        // 새로운 Row를 추가하기
        addRecord : function(btn) {
            var r = new this.myRecordObj( {
                // this.myRecordObj는
                // this.store.reader의 Record Field를
                // 말함.
                company : '새 회사명',
                price : 0.00,
                change : 0.00,
                pctChange : 0.00,
                lastChange : new Date(),
                newRecord : 'yes', // Update할때 트리거로 이용하기 위해서
                id : 0
            });
            this.grid.stopEditing();
            this.store.insert(0, r);
            this.grid.startEditing(0, 0); // 추가할 rowIndex, colIndex 위치
        },

        // 수정된 Cell만 저장하기
        updateCell : function(editEvent) {
            var isNewRecord = editEvent.record.data.newRecord;
            // 업데이트할때 사용될
            // 트리거('yes'면 새로운 레코드)
            /** ** AJAX 통신 모듈 들어가는곳 ***** */

            // DB를 성공적으로 업데이트 후 처리할 루틴
            if (isNewRecord == 'yes') {
                // 저장한후 새로운 레코드가 아님을 표시
                editEvent.record.set('newRecord', 'no');
                // isDirty Flag 없애기
                this.store.commitChanges();
            } else {
                this.store.commitChanges();
            }

            // 잘못됐을경우는 this.sotre.rejectChanges(); 로 리복한다.
            this.store.rejectChanges();

        },

        //변경된것들 모두 저장하기
        saveAllChanges : function() {
            var modifyRecords = this.store.getModifiedRecords(); //수정된 레코드 모두 찾기
            var modifyLen = modifyRecords.length;
            if (modifyLen > 0) {
                for (var i = 0;i < modifyLen; i++) {
                    /* 각 Row마다 AJAX Call을 하거나 배열로 모두 넘기는 Ajax Call Method가 들어간다.*/
                    var modifyCompany = modifyRecords[i].get('company')
                }
            }

        }

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






 






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

Extjs Qtips 사용하기  (2) 2008.04.16
Extjs 기본레이아웃에 각종 패널붙이기  (12) 2008.04.16
ExtJS 로드맵  (0) 2008.04.01
ExtJS를 이용한 Password Meter  (0) 2008.04.01
Ext JS Ext.ux.YoutubePlayer  (0) 2008.03.29
Post by 넥스트리소프트 데꾸벅(techbug)
, |

ExtJS 로드맵

Scripter/EXTJS / 2008. 4. 1. 22:58
매일 프로젝트를 수행하면서 Ext의 새로운 버전이 나올때마다 현재 작업버전과 호환이 될까 노심초사하면서 테스팅하던 때가 있었는데 이번에 Extjs의 Roadmap이 공개가 되었다.

올 겨울에 나올 Ext3.0은 chart기능과 Comet기능까지 추가 된다니 기대가 되긴하는데 프레임웍 자체가 너무 방대해지는건 아닐까 걱정해 본다... (스크립트언어인데..방대해지면.. 문제가... ㅡ.,ㅡ;)


ExtJS의 로드맵 --------------------------------------------
2008년의 목표는 2.x 버전의 새로운 컴포넌트 추가와 현재 몇몇 기능을 향상하는 선이다.
앞으로 3.0에서 보는봐와 같이  챠트와, Comet(실시간 통신-Ajax는 Reqeust에 의한 Response방식인데 비해 Coment은 일반 어플리케이션처럼 릴레이션을 물고 있다 :데꾸벅 주) 을 추가하여 큰그림을 그리고 있다.... 이하 생략...원문참조


2.1 (2008 봄)

  • RESTFul 지원 (그리 RESTFul 하지 않던데.. ㅡ.,ㅡ;)
  • 원격컴포넌트 로딩
  • 전통적인 비Ajax적인 Submit 지원클래스
  • 다른 JS라이브러리 Adapter 업그레이드
  • AIR 플랫폼 지원추가
  • 그리드 필터링
  • 체크박스/라이오 그룹
  • 상태바

2.2 (2008 여름)

  • Ext.Ajax 기능향상
  • Ext컴포넌트만의 초점을 맞춘(scoped) reset style지원
  • XML을 포함한 추가적인 TreeLoader 데이타로딩
  • 슬라이더 컴포넌트
  • 파일업로드 필드

3.0 (2008 겨울/2009 초)

  • Ext 이벤트 등록모델 업데이트
  • 플래쉬 챠트 API ( Gwt-Ext에는 구글 챠트를 이용하던데..)
  • Comet/Bayeux 지원
  • 통합된 client-server 데이타 바인딩/marshaling for Ext components
  • Browser history support (흠.. window.event.hash값을 이용한걸까? 기대되네..)
  • ListView
  • Section 508(장애자 지원정책:웹접근성) 기능향상


여튼 3.0이 기대되는건 사실이자나...  ^^ 



Post by 넥스트리소프트 데꾸벅(techbug)
, |
YouTube API 와 Extjs를 이용한 Youtube player extension이 공개되었다.

데모페이지 : http://www.siteartwork.de/youtubeplayer_demo/
공식페이지 : http://www.siteartwork.de/youtubeplayer/
사용자 삽입 이미지

일전에 LiveGrid를 공개했던 Thorsten Suckow-Homberg가 공개한 YouTube Player는 개발자들이 쉽게 Youtube API를 이용하여 Ext application에 embed하도록 하였다.
간단한 기능으로는
  • 버퍼링 상태
  • 플레이백 슬라이더
  • 음소거
  • 볼륨컨트롤
등 기존 미디어플레이어의 기능을 많이 내포하고 있다.
당연히 오픈소스이므로 꽁짜다.. ^^

다운로드 :


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

ExtJS 로드맵  (0) 2008.04.01
ExtJS를 이용한 Password Meter  (0) 2008.04.01
Extjs를 이용한 간단한 form submit  (0) 2008.03.14
Extjs를 이용한 ExtPHP, PHP-Ext 프로젝트 오픈  (0) 2008.03.07
Extjs Grouping Header Grid Plugins  (13) 2008.03.06
Post by 넥스트리소프트 데꾸벅(techbug)
, |
네이버블로그에 올렸던 내용을 다시 정리하여 올리다.


HTML
<div id="center" class="x-layout-inactive-content">
<div id="btn-show-dlg"></div>
<div id="grid-paging" class="x-grid-mso" style="border: 1px solid #c3daf9; overflow: hidden; width:550px;"></div>
</div>


JAVASCRIPT
Step = function(){
    var btn, dialog, form;
    return{
        Create:function(){
            btn = new Ext.Button('btn-show-dlg', {
                text: "New Project",
                handler: Step.Init
            });
        },

        Init:function(){
            Ext.QuickTips.init();
            Ext.form.Field.prototype.msgTarget = 'side';

            dialog = new Ext.BasicDialog('loginDialogId', {
                autoCreate: true,
                width: 400,
                height: 400,
                modal: true,
                syncHeightBeforeShow: true,
                shadow:true,
                fixedcenter:true,
                title:'New Project'
            });

            dialog.body.dom.innerHTML="<div id='loginFormId' style='padding: 10px'></div>";

            form = new Ext.Form({
                labelAlign: 'left',
                labelWidth: 100,
                buttonAlign: 'center',
                url:'php/subProject.php',
                baseParams:{module:'login'}
            });
            form.add(
                new Ext.form.TextField({
                    fieldLabel: 'Project Name',
                    name: 'pname',
                    width:250,
                    allowBlank:false
                }),

                new Ext.form.TextField({
                    fieldLabel: 'Acronym',
                    name: 'pacy',
                    width:250,
                    allowBlank:false
                }),

                new Ext.form.DateField({
                    fieldLabel: 'Start Date',
                    name: 'sdate',
                    width:200,
                    allowBlank:false
                }),

                new Ext.form.DateField({
                    fieldLabel: 'End Date',
                    name: 'edate',
                    width:200
                    }),

                new Ext.form.TextArea({
                    fieldLabel: 'Description',
                    name: 'desc',
                    width:250,
                    allowBlank:false
                })
            );

            form.addButton('Login', function(){
                form.submit({
                    waitMsg:'Please Wait...',
                    reset:true,
                    success:Step.Success,
                    scope:Step
                });
            }, form);

            form.render('loginFormId');

            dialog.on('show', function(){form.items.item(0).focus();});
            // dialog.show(document.body.getElementsByTagName('di v')[0]);
            dialog.show(btn.getEl());

            dialog.on('hide', function(){this.destroy(true)}, dialog);
        },

        Success: function(f,a){
            dialog.destroy(true);
            if(a && a.result && typeof a.result.level == "number"){
                Step.level=a.result.level;
                alert("level = "+Test.level);
            }
        }
    }
}();


Ext.BasicForm.prototype.afterAction=function(actio n, success){
    this.activeAction = null;
    var o = action.options;
    if(o.waitMsg){
        Ext.MessageBox.updateProgress(1);
        Ext.MessageBox.hide();
    }
    if(success){
        if(o.reset){
            this.reset();
        }
        Ext.callback(o.success, o.scope, [this, action]);
        this.fireEvent('actioncompleted', this, action);
    }else{
        Ext.callback(o.failure, o.scope, [this, action]);
        this.fireEvent('actionfailed', this, action);
    }
}

Ext.onReady(Step.Create, Step, true);


PHP
<?php
    require "../../includes/nav_Header.php";
    if(!isset($_POST['module']) || strlen($_POST['module'])<1)die("Error: Server 1");
    $module=$_POST['module'];

    function Login($pname, $pacy, $sdate, $edate, $desc){
        $dbconnection = new gsConnection();
        global $VIP_DB;

        //Login Validation
        if(!isset($pname)||!strlen($pname))$pname = false;die("{errors:[{id:'pname', msg:'Required Field'}]}");
        if(!isset($pacy)||!strlen($pacy))$pacy = false;die("{errors:[{id:'pacy', msg:'Required Field'}]}");
        if(!isset($sdate)||!strlen($sdate))$sdate = false;die("{errors:[{id:'sdate', msg:'Required Field'}]}");
        if(!isset($desc)||!strlen($desc))$desc = false;die("{errors:[{id:'desc', msg:'Required Field'}]}");

        if($pname && $pacy && $sdate && $desc){
            echo 'First SQL';
            $sql = "SELECT projectid FROM projects WHERE projectname = '$pname'";
            $result = @mysql_query ($sql);

            if (mysql_num_rows($result) == 0){
                echo 'SECOND SQL';
                $sql = "INSERT INTO projects (projectname, projectacy, description, startdate, enddate, complete, mod_user)
                VALUES ('$pname', '$pacy', '$desc', '$sdate', '$edate', 0, 0)";
                $result = mysql_query($sql) or die(mysql_error());
            )
        }
        $level=10;
        print "{success:true, level:$level}\n";
        exit;
    }

    Login($_POST['pname'], $_POST['pacy'], $_POST['sdate'], $_POST['edate'], $_POST['desc']);
    die('Error: Server End');
?>









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

ExtJS를 이용한 Password Meter  (0) 2008.04.01
Ext JS Ext.ux.YoutubePlayer  (0) 2008.03.29
Extjs를 이용한 ExtPHP, PHP-Ext 프로젝트 오픈  (0) 2008.03.07
Extjs Grouping Header Grid Plugins  (13) 2008.03.06
Extjs 크로스 도메인 관련  (0) 2008.02.28
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)
, |
그리드를 그리다 보면 기존 HTML을 이용해서 <thead>를 colspan, rowspan을 이용해서 컬럼을 그룹핑 해줄수 있었다. 그러나 extjs gridpanel에서 방법을 찾다가 해당 플러그인을 찾았다.

실제작성예
사용자 삽입 이미지

소스다운로드



마크업
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</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="./GroupHeaderPlugin-HdMenu.js"></script>
<script type="text/javascript" src="./groupingHeader.js"></script>
<script type="text/javascript" src="./00_main.js"></script>
<style type="text/css">
    .ext-ie .x-grid3 table,.ext-safari .x-grid3 table {
          table-layout:auto;
    }
    .ux-grid-hd-group-cell {
          background: #f9f9f9 url(../../resources/images/default/grid/grid3-hrow.gif) repeat-x 0 bottom;
          height:24px;
          border-right: 1px solid rgb(208 208 208) !important;
    }
    .ux-grid-hd-group-nocolspan-cell {
          border-right: 1px solid rgb(208 208 208) !important;
    }
</style>
</head>
<body></body>
</html>


00_main.js
var techbugTestMain = function(){
    return {
        init: function(){

            //그리드판넬 그리기 ----------------------------------------
            this.gridPanel = new mainGridPanel();


            //메인화면 레이아웃 설정-----------------------------------
            this.mainViewPort = new Ext.Viewport({
                layout:'border',
                border:false,
                items:[     this.gridPanel ]
            });
            this.mainViewPort.doLayout();
            this.mainViewPort.syncSize();

        }// End of Init();


    }// End of return
}();
Ext.EventManager.onDocumentReady(techbugTestMain.init, techbugTestMain, true);


groupingHeader.js
mainGridPanel = function(){
    //실제 데이타 들어가기
    var cmm = new Ext.grid.ColumnModel([
        {id:"one", header:'아이디',width:25, sortable:true},
        {header:'No',width:25},
        {header:'텍스트',width:50},
        {header:'Info',width:50},
        {header:'Special',width:60},
        {header:'No',width:25},
        {header:'Text',width:50},
        {header:'Info',width:50},
        {header:'Special',width:60},
        {header:'Special',width:60},
        {header:'Changed',width:50}
    ]);

    //실제 만드는 부분
    cmm.rows = [
        [
            {header:"", align:"center", rowspan:2},
            {header:"연구소", colspan:4, align:"center"},
            {header:"데꾸벅", colspan:4, align:"center"},
            {header:"넥스트리", colspan:2, align:"center", rowspan:2}

        ],[
            {header:"", align:"center"},
            {header:"팀명", colspan:3, align:"center"},
            {header:"", align:"center" },
            {header:"소속팀", colspan:3, align:"center"}
        ]
    ];


    //그리드의 초기설정값을 저장한다.
     mainGridPanel.superclass.constructor.call(this, {
        region: 'center',
        border: false,
        margins:'0 0 0 0',
        cm: cmm,
        width: cmm.getTotalWidth(),
        store: new Ext.data.Store({
            reader: new Ext.data.JsonReader({ //데이타 들어가는 부분
                fields: []
            }),
            data: []
        }),
        enableColumnMove: false,
        enableDragDrop: false,
        enableHdMenu: false,
        height:100,
        view: new Ext.grid.GridView({forceFit:true}),
        title:'Array Grid',
        plugins: new Ext.ux.plugins.GroupHeaderGrid(),
        tbar: [
            {
                text:"저장",
                cls: 'x-btn-icon',
                scope:this,
                handler : function(){alert('저장')}
            }
        ]
    });
};
Ext.extend(mainGridPanel,Ext.grid.GridPanel, { });

GroupHeaderPlugin-HdMenu.js
/**
 *  Plugin for grouped column headers
 *  @class Ext.ux.plugins.GroupHeaderGrid
 *     @extends Ext.util.Observable
 *  @author ?vind Neshaug
 * 
 *  Based on the code in this thread: http://extjs.com/forum/showthread.php?t=12677
 *  Thanks to JEBriggs for starting up the code and resizing code from Clayton
 *  Thanks to JorisA for the idea of making this a plugin instead of an extension.
 * 
 *  Supports infinte number of grouped header rows
 *  Supports header group spanning of rows
 * 
 */
Ext.namespace("Ext.ux.plugins");

Ext.ux.plugins.GroupHeaderGrid = function(config) {
    Ext.apply(this, config);
};
Ext.extend(Ext.ux.plugins.GroupHeaderGrid, Ext.util.Observable, {
    // PLUGIN INIT
    init: function(grid){
        this.grid = grid;
        this.view = this.grid.getView();
        this.cm = this.grid.getColumnModel();
        this.initTemplates();
        this.view.renderHeaders.createInterceptor(this.renderHeaders(),this);
       
        // testing of linking bands up with headers control structure
        //this.linkBandsAndHeaders();
    },
    initTemplates: function(){
        this.view.initTemplates.call(this);
        if(!this.templates.groupedHeader){
            this.templates.groupedHeader = new Ext.Template(
                 '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
                 '<thead>{groupRows}',
                 '<tr class="x-grid3-hd-row">{cells}</tr></thead>',
                 '</table>'
            );
        }
        if (!this.templates.groupRow){
            this.templates.groupRow = new Ext.Template(
                '<tr class="x-grid3-hd-row">{groupCells}</tr>'
            );
        }    
        if(!this.templates.groupCell){
            this.templates.groupCell = new Ext.Template(
                 '<td id="{grId}" class="{cellClass}" rowspan="{rowspan}" colspan="{colspan}"><div class="x-grid3-hd-inner" unselectable="on" style="text-align:{align}">{header}</div></td>'
            );
        }
    },
    renderHeaders : function(cm){
        if (cm){
            this.cm = cm;
        }
        if(!this.cm.rows) {
            return true;
        }
        else {
            this.view.renderHeaders = this.renderGroupedHeaders.createDelegate(this);
           
            this.view.getHeaderCell = this.getHeaderCell.createDelegate(this);
            this.view.updateSortIcon = this.updateSortIcon.createDelegate(this);
            this.view.getColumnWidth = this.getColumnWidth.createDelegate(this);
            this.view.getColumnStyle = this.getColumnStyle.createDelegate(this);
            this.view.getTotalWidth = this.getTotalWidth.createDelegate(this);
            this.view.updateColumnHidden=this.updateColumnHidden.createDelegate(this);
           
            this.view.renderHeaders();
            this.colModel = this.cm;
            this.headerBandLinks = this.linkBandsAndHeaders();
           
            return false;
        }
    },
    renderGroupedHeaders : function() {
        var cm = this.cm;
        var rows = this.cm.rows;
        var groups;
       
        var cellTemplate = this.templates.hcell;

        var cellMarkup = [], sb = [], cellParams = {};
        var groupCellMarkup = [];
        var rowsMarkup = [];
       
        for (var i = 0; i < rows.length; i++) {//create markup for rows
            groups = rows[i];
            for (var j = 0; j < groups.length; j++) {//create markup for group cells
                groups[j].id="ext-ux-gen-"+i+"-"+j;
                groupCellMarkup[groupCellMarkup.length] = this.renderGroupCell(groups[j]);
            }
            rowsMarkup[rowsMarkup.length] = this.renderGroupRows(groupCellMarkup.join(""));
            var groupCellMarkup = [];
        }
        for(var i = 0, len = cm.getColumnCount(); i < len; i++){ // create markup for leaf cells
            cellMarkup[cellMarkup.length] = this.renderHeaderCell(cm, i);
        }
        // use a different template
        return this.templates.groupedHeader.apply({groupRows: rowsMarkup.join(""), cells: cellMarkup.join(""), tstyle:'width:'+this.view.getTotalWidth()+';'});
    },
    renderGroupCell : function(group,groupId) {
        var template = this.templates.groupCell;
        var cellClass;
        var rowspan;
        if (group.colspan < 2 || group.colspan === undefined){
            cellClass = "ux-grid-hd-group-nocolspan-cell";
        }
        else {
            cellClass = "ux-grid-hd-group-cell";
        }
        return template.apply({
            header: group.header,
            colspan: group.colspan,
            align: group.align,
            rowspan: group.rowspan,
            cellClass: cellClass,
            grId: group.id
        });
    },
    renderGroupRows : function(groupCellMarkup) {
        var template = this.templates.groupRow;
        return template.apply({groupCells: groupCellMarkup});
    },
    renderHeaderCell : function(cm,index){
        var template = this.templates.hcell;
        var params = {};
        params.id = cm.getColumnId(index);
        params.value = cm.getColumnHeader(index) || "";
        params.style = this.view.getColumnStyle(index, true);
        if(cm.config[index].align == 'right'){
            params.istyle = 'padding-right:16px';
        }
        return template.apply(params);
    },
    // from gridview, with minor fixes
    getHeaderCell : function(index){
        var hds = this.view.mainHd.select('.x-grid3-cell');
        return (hds.item(index).dom);
    },
    updateSortIcon : function(col, dir){
        var sc = this.view.sortClasses;
        var hds = this.view.mainHd.select('.x-grid3-cell').removeClass(sc);
        hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
    },
    getColumnWidth : function(col){
        var w = this.cm.getColumnWidth(col);
        if(typeof w == 'number'){
            return (Ext.isBorderBox ? w : (w-this.view.borderWidth > 0 ? w-this.view.borderWidth:0)) + 'px';
        }
        return w;
    },
    getColumnStyle : function(col, isHeader){
        var style = !isHeader ? (this.cm.config[col].css || '') : '';
        style += 'width:'+this.view.getColumnWidth(col)+';';
        if(this.cm.isHidden(col)){
            style += 'display:none;';
        }
        var align = this.cm.config[col].align;
        if(align){
            style += 'text-align:'+align+';';
        }
        return style;
    },
    getTotalWidth : function(){
        return this.cm.getTotalWidth()+'px';
    },
   
    updateColumnHidden : function(col, hidden){
        var tw = this.getTotalWidth();
        var bands = this.headerBandLinks[col]||[];
        this.view.innerHd.firstChild.firstChild.style.width = tw;

        var display = hidden ? 'none' : '';
       
       
        var hd = this.getHeaderCell(col);
        hd.style.display = display;

        var ns = this.view.getRows();
        for(var i = 0, len = ns.length; i < len; i++){
            ns[i].style.width = tw;
            ns[i].firstChild.style.width = tw;
            ns[i].firstChild.rows[0].childNodes[col].style.display = display;
        }

        for(var i = 0, len = bands.length; i < len; i++){
            var bandId = bands[i];
            var band = Ext.getDom(bandId);
           
            if (hidden){
                if (band.colSpan < 2 || band.colSpan === undefined){
                    band.style.display = 'none';
                }
                else{
                    band.colSpan += -1;
                }
            }
            else{
                if ((band.colSpan < 2 || band.colSpan === undefined)&&band.style.display === 'none'){
                    band.style.display = '';
                }
                else{
                    band.colSpan += 1;
                }
            }
           
           
        }
       
        this.view.onColumnHiddenUpdated(col, hidden, tw);

        delete this.view.lastViewWidth; // force recalc
        this.view.layout();
    },
   
    linkBandsAndHeaders : function(){
   
        var bandsWithHeader;
        var cm = this.grid.getColumnModel();
        var columnCount = this.cm.getColumnCount();
        var headers = new Array(columnCount);
        var headerBands = new Array(columnCount);
        for (var i = 0;i<columnCount;i++){
            headers[i] = i;
        }
        var nrOfRows = cm.rows.length;
        for (var i = 0;i<nrOfRows;i++){
            var headersInRow = cm.rows[i];
            var headersInRowCount = headersInRow.length;
            var headerCounter = 0;
            for (var j = 0;j<headersInRowCount;j++){
                while(headers[headerCounter] === null){
                    headerCounter++;
                }
                if (headersInRow[j].colspan < 2 || headersInRow[j].colspan === undefined){
                    headers[j] = null;
                    var bands = headerBands[headerCounter]||[];
                    bands[bands.length]=headersInRow[j].id;
                    headerBands[headerCounter]=bands;
                    headerCounter++;
                   
                }
                else {
                    for (var k = 0;k<headersInRow[j].colspan;k++){
                        var bands = headerBands[headerCounter]||[];
                        bands[bands.length]=headersInRow[j].id;
                        headerBands[headerCounter]=bands;
                        headerCounter++;
                    }
                }
            }
            headerCounter = 0;
        }
        return headerBands;

    }
});


출처 : extjs forum








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)
, |
한국어 로케일 설정파일을 수정할 일이 생겨 뜯어보다 보니
이번 2.0.2 버전에 추가된 부분은 빠져있는 것을 발견했다.
extjs의 locale 담당 개발자에게 메일을 보내 다음 패키징때 추가적으로 넣어달라고 부탁했는데 다음 패키징땐 소스에 데꾸벅이란 닉을 발견할수  있을까? 크크크

사용자 삽입 이미지













위와 같이 사용하려면 HTML에
    <script type="text/javascript" src="../../ext-all.js"></script>
    <script type="text/javascript" src="../../source/locale/ext-lang-ko.js" charset="utf-8"></script>
와 같이 넣으면 된다.


Post by 넥스트리소프트 데꾸벅(techbug)
, |
ExtJS에서 RESTful하게 HTTP status를 관리하게 생겼다. ㅠ.,ㅠ;
API 및 ExtJS의 코어을 뜯어본 결과 이넘들은 xhr.status >=200 && xhr.status <300 인경우만 처리하고 나머지는 모두 xhr.status 를 13030 으로 주고 있었다.

ext-base.js 에 보면 아래와 같이 처리 ㅠ.,ㅠ;

{
            if (!J) {
                this.releaseObject(I);
                return
            }
            var G,
            F;
            try {
                if (I.conn.status !== undefined && I.conn.status != 0) {
                    G = I.conn.status
                } else {
                    G = 13030
                }
            } catch(H) {
                G = 13030
            }
            if (G >= 200 && G < 300) {
                F = this.createResponseObject(I, J.argument);
                if (J.success) {
                    if (!J.scope) {
                        J.success(F)
                    } else {
                        J.success.apply(J.scope, [F])
                    }
                }
            } else {
                switch (G) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    F = this.createExceptionObject(I.tId, J.argument, (E ? E: false));
                    if (J.failure) {
                        if (!J.scope) {
                            J.failure(F)
                        } else {
                            J.failure.apply(J.scope, [F])
                        }
                    }
                    break;
                default:
                    F = this.createResponseObject(I, J.argument);
                    if (J.failure) {
                        if (!J.scope) {
                            J.failure(F)
                        } else {
                            J.failure.apply(J.scope, [F])
                        }
                    }
                }
            }
            this.releaseObject(I);
            F = null
        }



그리고 나서 모두 exception처리를 하고 있었는데

ScriptTagProxy의 경우 loadexception ( Object This, Object o, Object arg, Object e ) 에서 argument형태로 처리되고 있으며
Ext.Ajax의 경우는 requestexception : ( Connection conn, Object response, Object options ) 형태로 처리되고 있다.

기존의 예외처리를 위의 2개 이벤트를 사용해 처리해 줘야 하는데 아래와 같이 처리하면 되겠다..  ....

 Ext.Ajax.on('requestexception', function(c, r, o) {
            debugger;
            if (r.status == 402) {
              
                //alert('Your session has expired');
              
            }
            else{
                  var error = r.responseText;
                 if (!error) error = r.statusText;
                 App.aSendError.createWindow(error);
                
            }
        })


Ext.Ajax.on('beforerequest', function(conn, options) {
console.log(arguments);
} );

Ext.Ajax.on('requestcomplete', function(conn, response, options) {
console.log(arguments);
} );

Ext.Ajax.on('requestexception', function(conn, response, options) {
console.log(arguments);
} );




Ext.Ajax.request({
       url: '/techbug.json?method=saveBranch',
       success: function(response) {
               branchList_store.load();
               branchList.reconfigure(branchList_store, branchList_cm);
            Ext.MessageBox.alert('Status', 'Your changes were saved successfully.');
       },
       failure: function(response) {
               if(response.getResponseHeader.CustomHeader == "585")
                Ext.MessageBox.alert('Status', 'You do not have permission to do this.');
            else
               Ext.MessageBox.alert('Status', 'There was an unknown error saving your changes.<P>Please try again.<P>Detailed Error: ' + response.getResponseHeader.CustomHeaderMessage);
       },
       headers: {
          
       },
       params: {
           ....
       }
})



    Ext.data.HttpProxy.on("loadexception", function(options, response, e) {
        // do stuff for all load failures
    });
    Ext.data.HttpProxy.on("load", function(options, arg) {
        // do stuff for all load successes
    });


 
Ext.Ajax.on('requestexception', function(c, r, o) {
            debugger;
            if (r.status == 402) {
              
                //alert('Your session has expired');
              
            }
            else{
                  var error = r.responseText;
                 if (!error) error = r.statusText;
                 App.aSendError.createWindow(error);
                
            }
        })



// set up the connection to load the initial data
var loadConn = new Ext.data.Connection();
loadConn.request({method: 'POST', timeout: 120000, url: 'get_tasks_for_job.cfm' , params: { client_code: localClient, job: localJob, employee: localEmployee } });
// handle the response
loadConn.on('requestcomplete', function(sender, param) {
    try
    {
        var response = Ext.util.JSON.decode(param.responseText);
    } catch (e) {
        alert('There was a problem loading the tasks for this job.');
        return;
    }
    if (response.success == 'Y') {
        // the init function will run loadData() as needed
        localTaskData = response.data;
        Ext.taskEditApp.main.init();
    } else {
        alert('There was a problem loading the tasks for this job.');
    }
}, { scope: this });
loadConn.on('requestexception', function(conn, response, options) {
    alert('failed!!!');
    for (property in response) {
        alert(property);
        alert(response[property]);
    }       
}, { scope: this }); 

ds.load({params:{start:0, limit:pageCount},
    callback:function(record, options, success){
        if(!success){
            alert('error occure.');
            Ext.get('grid_id').unmask();
        }
    }
});




참고URL :
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)
, |
ExtJS 관련 IDE를 정리하다. 관련URL : http://ajaxian.com/archives/ext-js-ide-support-roundup
ExtJS 2.0 API탑재 Aptana plug-in download : http://orsox.mocis.at/download.php?view.1

사용자 삽입 이미지




There’s been a lot of talk lately about the different IDEs and the support they offer for the various JavaScript libraries. Ext’s uber-coder, Jack Slocum, has put up a blog entry explaining which IDEs support the Ext JS framework:

The Ext 2.0 API is very extensive and remembering all of the functions, properties or configs available is virtually impossible. The API documentation is very thorough, but it would be nice if IDEs would provide code assist options in JavaScript as they do in other languages such as Java and C#. Luckily, there are some IDEs and plugins available that do just that — and also have direct support for Ext 2.0.

Included in the mix are:

This great news and it shows that IDE vendors are taking JavaScript frameworks seriously. Anything that makes development easier is definitely welcome.



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)
, |
사용자 삽입 이미지

어도브사는 오늘 AIR v1.0과 Flex3.0 을 릴리즈화면서 RIA세계의 행진을 계속했다.
Ajax개발자로써 어도브의 애플리케이션 개발은 상당히 접근하기 어려운 분야였음을 감안하면 이번 릴리즈는 여타 다른 스크립트를 포용하는 정책을 써 개발자들의 많은 사랑을 받을것 같다는 생각이 드낟.







Adobe AIR
AIR runtime and SDK는 매우 긴 베타버전 기간을 걸쳐 이번에 선보이는 것으로써
구글기어를 써보신 개발자라는 AIR의 강력함을 느낄수 있을것이다.
(데꾸벅도 써보고 놀랐어요.. ^^)
먼저 이번에 릴리즈된 기능을 보자면

데스탑을 기능적으로 향상 :
드래그앤 드랍, 트레이 미니마이징 기능등..

데이타 액세스 :
로컬파일 액세스.. 상당히 강력한 기능이죠..

자바스크립트 라이브러리 지원 :
jQuery, Extjs(이것땜에 데꾸벅도 AIR를 써요..), Dojo, Spry를 지원

보안 :
어차피 인스톨되어 배포되는것이기 때문에 보안에는 문제가...
나름 생각한건데 패킷을 후킹하면?? 흠..UUID값을 써야하는 문제가 생길수 있다고 생각하는 데꾸벅.. ㅠ.,ㅠ;


Flex 3.0
플렉스는 여러분도 다 아시다시피 Flash기반의 RIA 개발 플랫폼입니다.
점점더 발전하고 있고 여러 블로그에서도 많은 기술관련 글들이 포스팅 되기도 하고 개발자에게도 상당히 인기가 있습니다.
비록 아직 써보진 않았지만 2008년 유망 기술중 하나에 꼽히기도 했습니다.
사용자 삽입 이미지


이 참에 한번 Flex를 함 써봐야 겠네요.. ^^






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

Extjs v2.02 릴리즈

Scripter/EXTJS / 2008. 2. 26. 09:29
Adobe AIR를 지원해주는 Extjs 2.02버전이 릴리즈 되었다.

Adobe AIR팀과 ExtJS개발팀의 협업으로 탄생한 2.02버전에서는 AIR API를 완벽지원하고 있다.
오래전 Jack Slocum의 블로그에서 봤던 간단한 태스크관리 샘플을 봤을때의 충격이 채가시지 않은채 발표된 이번 버전에서는 AIR API를 패키징하여 Ext.air 형태로 다음과 같이 배포된다.

- 네이티브 윈도우 관리하기
- AIR beta3에서 소개되었던 데이타베이스 동기화
- 네이티브 드래그엔 드롭과 클립보드 접근
- 사운드 새생
- 시스템 트레이에 AIR 애플리케이션 miniming하기(아래소스 참조)

var win = new Ext.air.NativeWindow({
    id: 'mainWindow',
    instance: window.nativeWindow,
 
    // System tray config
    minimizeToTray: true,
    trayIcon: 'ext-air/resources/icons/extlogo16.png',
    trayTip: 'Simple Tasks',
    trayMenu : [{
        text: 'Open Simple Tasks',
        handler: function(){
            win.activate();
        }
    }, '-', {
        text: 'Exit',
        handler: function(){
            air.NativeApplication.nativeApplication.exit();
        }
    }]
});

트리뷰(treeview) 콤보박스의 리스트트리 샘플
사용자 삽입 이미지

extjs listtree





















Custom Grid Columns
사용자 삽입 이미지



















Switch : 버튼 모음 테두리 (메타포어기능)
사용자 삽입 이미지









Ext V2.02 다운로드 : 다운로드하기(http://extjs.com/download)

데꾸벅 블로그에서 바로 다운로드 받기 :

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

Basic Concept

이번 장은 이전에 작성해놓은 기본레이아웃잡기 및 기본그리드기리기를 이용하여 레이아웃의 좌측영역(west region)에 그리드를 붙이는 작업을 진행한다.
이전장을 충분히 숙지하였다는 가정하에 Grid를 붙여보자

기본소스 :


<그리드 해부도>

사용자 삽입 이미지









Step 1.  왼쪽 기본 패널을 Grid패널로 변경

basicLayout_AttachedGrid.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_AttachedGrid.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

 

basicLayout_AttachedGrid.js

// 그리드 패널 클래스 시작
LeftArea = function(viewport){ //왼쪽 패널(LeftArea)을 정의한다.
this.viewport = viewport;

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'
}])
});
// 왼쪽 패널의 config옵션을 정의한다.
LeftArea.superclass.constructor.call(this, {
region: 'west', // 굵게 표시된 부분은 기본레이아웃을 잡는 config옵션이다.
title: 'WEST',
collapsible: true,
collapsed: false,
width: 250,
split: true,
layout: 'fit',
margins: '5 0 5 5',
cmargins: '5 5 5 5',
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,
emptyText: 'No Record found'
})
});
};

//기본레이아웃에서는 Ext.Panel을 상속받았지만
//여기서는 그리드패널이므로 Ext.grid.GridPanel을 상속받음.
Ext.extend(LeftArea, Ext.grid.GridPanel, {
});


// 그리드 패널 클래스 끝



// 메인 레이아웃 클래스 시작
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();

// 좌측그리드의 데이타 스토어를 화면을 최초 그릴때 데이타를 로드한다.
this.WestPanel.store.load();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);
// 메인 레이아웃 클래스 끝

 기본레이아웃에서 달라진 부분은 왼쪽 패널(LeftArea)이  기본패널(Ext.Panel)에서 그리드패널(Ext.grid.GridPanel)로 바뀌었을 뿐이다. 

위소스중 기본패널의 config옵션( 굵게 표현된 부분)에 Grid 옵션이 더 추가되었을 뿐이다. 대신 왼쪽 그리드패널의 데이타스토어를 메인레이아웃에서 호출(this.WestPanel.store.load();)하였다.
Step2에서는 좌측 그리드패널 클래스를 별도의 파일로 추가하여 정리해보자

 

Step 2. 좌측그리드패널 클래스 별도 파일로 관리하기

basicLayout_AttachedGrid.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>
//좌측 그리드 패널클래스 별도 파일로 분리, 반드시 main 레이아웃 클래스보다 먼저 호출
<script type="text/javascript" src="basicLayout_LeftGrid.js"></script>
<script type="text/javascript" src="basicLayout_AttachedGrid.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

basicLayout_AttachedGrid.js

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();
            //좌측 패널의 public method호출
            this.WestPanel.loadGridDataSet();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

basicLayout_LeftGrid.js

LeftArea = function(viewport){

    this.viewport = viewport;

    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'
        }])
    });

    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',
        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,
            emptyText: 'No Record found'
        })
    });
};

Ext.extend(LeftArea, Ext.grid.GridPanel, {
    //public Method 정의 부분
    loadGridDataSet: function(){
        this.store.load();
    }


});


위의 Step1과 변경된것이라고는 좌측그리드패널 클래스를 별도 파일로 분리하고 클래스내의 public 메쏘드를 메인레이아웃에서 호출한다는것이다.
그러나 이 러한 구조의 클래스는 항상 메인 레이아웃에서 특정 레이아웃일때만 사용된다 예를 들어  좌측그리드패널클래스(LeftArea)의 config option중에 레이아웃에 해당하는 값들이 이미 들어가 있기 때문에 다른 구조의 레이아웃에서는 이 클래스를 호출하여 사용할수 없다는 단점이 있다.

 

 

 

Step 3. 클래스 리팩토링

basicLayout_LeftGrid.js
LeftArea = function(viewport, config){

    this.viewport = viewport;
    Ext.apply(this, config);

    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'
        }])
    });
    // 레이아웃과 관련된 부분을 제외하고 순수 그리드 config option 부분만 사용
    LeftArea.superclass.constructor.call(this, {
        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,
            emptyText: 'No Record found'
        })
    });
};

Ext.extend(LeftArea, Ext.grid.GridPanel, {
    loadGridDataSet: function(){
        this.store.load();
    }
});



 

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();
//레이아웃과 관련된 내용을 메인레이아웃 클래스 밖으로 빼냄
this.WestPanel = new LeftArea(this, {
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
layout: 'fit',
margins: '5 0 5 5',
cmargins: '5 5 5 5'
}), this.viewport = new Ext.Viewport({
layout: 'border',
items: [this.WestPanel, 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();
this.WestPanel.loadGridDataSet();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

위 와 같이 작성되었을 경우는 별도 파일로 빼낸 BasicLayout_LeftGrid.js 파일은 다른 곳에서도 재사용할수 있다. 그러나 ExtJS Panel특성상 바로 Ext.apply()를 통하여 해당 config option을 LayoutContainer에 적용할수 없다. 위 소스를 실행 시켜 보면  왼쪽 판넬의 collapseMode가  잘못 적용되어 container 속성까지 변경되었다. 이럴 경우에는 NestedLayout Manager를 사용하여 다음과 같이 표현하면 된다.


basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
    return {
        init: function(){
            Ext.QuickTips.init();
            
            this.viewport = new Ext.Viewport({
                layout: 'border',
                items: [{
                    region: 'west', // 일반 Ext.panel로 설정하여 왼쪽
                    title: 'WEST',
                    collapsible: true,
                    collapsed: false,
                    width: 650,
                    split: true,
                    margins: '5 0 5 5',
                    cmargins: '5 5 5 5',
                    layout: 'border',
                    items: [ // 일반 Ext.panel에 Nested된 좌측패널
                        this.WestPanel = new LeftArea(this, {
                        region: 'center',
                        layout: 'fit',
                        border: false
                    })]
                }, 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();
            this.WestPanel.loadGridDataSet();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

 

Step 4. 그리드 판넬에 새로고침 메뉴넣기

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();

this.viewport = new Ext.Viewport({
layout: 'border',
items: [{
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
margins: '5 0 5 5',
cmargins: '5 5 5 5',
layout: 'border',
items: [this.WestPanel = new LeftArea(this, {
region: 'center',
layout: 'fit',
border: false,
tbar: [{
text: '새로고침',
scope: this,
handler: function(){
this.WestPanel.store.reload();
}
}]
})]
}, 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();
this.WestPanel.loadGridDataSet();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

 위의 Toolbar 를 basicLayout_LeftGrid.js를 이용하여 붙일수도 있으나 사용하는곳 마다 툴바가 틀릴수 있으므로 되도록이면 좌측 그리드패널 클래스를 호출하는 메인 클래스에 붙이는것이 재사용성에 좋다. 넥스트리소프트의 경우는 모두 좌측그리드패널 클래스 안에 private로 선언되어 있다. 어느것이 좋을지는 사용하는곳마다 틀리므로 해당 상황을 보면서 붙여보도록 한다.

넥스트리의 경우는 모두 클래스안에 private method로 해당 클래스내에서만 사용하는 메쏘드를 두었으며 필요에 따라 외부클래스에서 호출하는 식으로 되어 있다. 취향에 따라 맞춰 하면 될듯... ^^

 

 

 

Step 5. 선택시 우측컨텐츠 판넬(cente region Basic fit layout)의 body 업데이트하기

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();

this.viewport = new Ext.Viewport({
layout: 'border',
items: [{
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
margins: '5 0 5 5',
cmargins: '5 5 5 5',
layout: 'border',
items: [this.WestPanel = new LeftArea(this, {
region: 'center',
layout: 'fit',
border: false,
tbar: [{
text: '새로고침',
scope: this,
handler: function(){
this.WestPanel.store.reload();
}
}]
})]
}, 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();
this.WestPanel.loadGridDataSet();
//Row선택시 발생하는 이벤트를 this.updateCenter() 메쏘드에서 실행
this.WestPanel.getSelectionModel().on('rowselect', this.updateCenter, this);
},

// 선택된 Row의 데이타를 가져와서 오른쪽 center Region의 Body에 업데이트한다.
updateCenter: function(selectionModel, index, record){
// 선택된 Row의 데이타를 가져온다.
var data_company = record.get('company');
var data_price = record.get('price');
var data_change = record.get('change');
var data_pctChange = record.get('pctChange');
var data_lastChange = record.get('lastChange');
// update할 HTML생성
var updateHTML = "<table cellpadding=2 cellspacing=1 border=1 width='100%'>" +
"<tr><td>Company</td><td>" +
data_company +
"</td></tr>" +
"<tr><td>Price</td><td>" +
data_price +
"</td></tr>" +
"<tr><td>Change</td><td>" +
data_change +
"</td></tr>" +
"<tr><td>Percentage</td><td>" +
data_pctChange +
"</td></tr>" +
"<tr><td>Last</td><td>" +
data_lastChange +
"</td></tr>" +
"</table>";

Ext.get("_CONTENTS_AREA_").update(updateHTML); //update
},



//다른 방법으로 선택된 데이타 모두 가져오기
updateCenter1: function(selectionModel, index, record){
var updateHTML = "<table cellpadding=2 cellspacing=1 border=1 width='100%'>"
// 선택된 데이타의 컬럼을 모두 가져오기
var cmc = this.WestPanel.getColumnModel().getColumnCount();
for (var i = 0; i < cmc; i++) {
//선택된 데이타의 컬럼Head (그리드 헤더 가져오기)
var columnName = this.WestPanel.getColumnModel().getColumnHeader(i);
//그리드 헤더의 데이타 인덱스 코드 가져오기
var columnCode = this.WestPanel.getColumnModel().getDataIndex(i);
var updateHTML = updateHTML + "<tr><td>" + columnName + "</td><td>" + record.get(columnCode);
+"</td></tr>" //데이타 가져와서 업데이트할 HTML 셋팅하기
}
var updateHTML = updateHTML + "</table>";
Ext.get("_CONTENTS_AREA_").update(updateHTML);

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

위 의 소스에서 updateCenter() 메쏘드는 this.WestPanel의 데이타 및 데이타인덱스 코드값을 모두 알고 있을 경우(대부분 소스까보면 다 나옴.. ㅡ.,ㅡ;) 사용하기 편리하고 아래의 updateCenter1() 메쏘드의 경우는 데이타 인덱스값을 모르거나 동적으로 필드값들이 셋팅 되는 경우에 상당히 유용하게 사용할수 있다. 넥스트리에서는 용어사전에 등록된 용어필드들을 자동으로 가져올때 사용되었던 소스이다.

 

 

Post by 넥스트리소프트 데꾸벅(techbug)
, |
eclipse로 extjs2.0 버전을 사용하고 싶으면 다음과 같이 하면된다.

extjs2.0 버전 플러그인 다운로드 : http://support.aptana.com/asap/browse/STU-547

사용자 삽입 이미지

extjs2.01 최신버전은 아래에서 다운로드 받으세요!
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)
, |
Extjs를 사용하다가 각 그리드 row에 각각의 프로세스바를 구현하게 되었다.
각 grid의 node 마다 프로세스 클래스를 재선언하였더니 너무 페이지자체가 무거워져
taskMgr를 이용하여 CSS로 구현하기로 하였다.
아래 소스는 넥스트리 사내프로젝트에서 구현되었던 일부소스이다.


[Extjs 소스중 일부] 기밀이므로 요기 까지만... ^^
// 타스크매니저로 autorefresh한다.
this.showTask = Ext.TaskMgr.start({
    run: this.store.reload,
    scope: this.store,
    interval:1000,
    args:[{params:{'appId':appId, 'deployId':deployId}, add:false}]
});

// 배포상태를 가져오는것을 멈춘다.
 Ext.TaskMgr.stop(this.showTask);



// 데이타가 모두 로드되었을 경우 해당 데이타값을 비교하여 계속
// autoRefresh할것인가를 결정한다.
this.store.on('load',function(store,records,opts){
    this.loadCompleteStatus(store,records,opts);
},this);



/**
 * 데이타가 모두 로드되었을 경우 해당 데이타값을 비교하여 계속 autoRefresh할것인가를 결정한다.
 * this.store.on('load',fn) 에 해당하는 메쏘드 실제 그리드안의
 * record에 해당하는 내용들을 모두 update한다.
 */
loadCompleteStatus : function(store,records,opts){
    var totalSize = store.getCount();
    var totalStop = 0;


    for(var i=0; i < totalSize; i++){
        var record = store.getAt(i);
        var pid = '_PROGRESSING_'+record.data.instsId;
        var lastMessage = (record.data.lastMessage) ? record.data.lastMessage : '';
        var progressRate= (record.data.progressRate)? parseInt(record.data.progressRate) : 0;
        var curStop = (record.data.isStop) ? parseInt(record.data.isStop) : 0 ;
       
        totalStop = totalStop + curStop;
        // 성공+실패 횟수가 전체 갯수와 같을 경우 autorefresh를 멈춘다.
        if(totalStop == totalSize) this.stopDeployStatus();


    }// for end
}

/**
 * 각 인스턴스마다의 정보(인스턴스명, 인스턴스Id, IP,port)를 그리드Row에 모두 표현한다.
 */
rendererDeployStatus : function(value, metadata, record,rowIndex,colIndex,store) {
return String.format(
        '<div class="_DEPLOYSTATUS_ROW_"><b>{0}</b><span class="_INSTANCE_IP_">{1}</span>'
        +'<div class="_PROGRESSBAR_CLASS_">'
        +'<div class="_PROGRESSBAR_" style="background-position-x:{2}%">{2}% : {3}</div>'
        +'<div id="_PROGRESSING_{4}" class="_PROGRESSBAR_COM_" '
        +'style="top:0;width:{2}%">{2}% : {3}</div></div></div>',
        value
        ,'http://'+record.data.ip +':'+record.data.port
        , record.data.progressRate
        , record.data.lastMessage
        , record.data.instanceId
        );
},


스타일시트
/* Progress bar 만들기 시작*/
DIV._DEPLOYSTATUS_ROW_ { padding-left:20px; }
DIV._DEPLOYSTATUS_ROW_ B {
    display:block;
    color:#333;
}
DIV._DEPLOYSTATUS_ROW_ SPAN._INSTANCE_IP_ { color:#333; }
._PROGRESSBAR_CLASS_LEFT_ {text-align:left !important}
DIV._PROGRESSBAR_CLASS_ {
    text-align:left !important;
    vertical-align:middle !important;
    padding:0 !important;
    height:18px !important;
    overflow:hidden !important;
    margin:7px 20px 10px 0 !important;
    background : transparent url(../img/progressbgwhite.png) 0 0 repeat-x !important;
    border:#6593CF solid 1px !important;
    position:relative;
}

DIV._PROGRESSBAR_ {
    text-align:left !important;
    vertical-align:middle !important;
    padding:0 3px 0 10px !important;
    margin:0 !important;
    height:18px !important;
    line-height:18px !important;
    white-space:nowrap !important;
    overflow:hidden !important;
    font-family:'segoe ui',verdana,arial;
    color:#396095;
    background-image : url(../img/progressbgwhite.png) !important;
    background-repeat : no-repeat ;
    background-position-y : 0;
    border:0 !important;
}

DIV._PROGRESSBAR_COM_ {
    text-align:left !important;
    vertical-align:middle !important;
    padding:0 3px 0 10px !important;
    margin:0 !important;
    height:18px !important;
    line-height:18px !important;
    white-space:nowrap !important;
    overflow:hidden !important;
    font-family:'segoe ui',verdana,arial;
    color:white;
    background: url(../img/progressbarbg.png) 0 0 repeat-x !important;
    border:0 !important;
    position:absolute;left:0;
    z-index:10;
}




.failrow DIV._PROGRESSBAR_COM_ {
    background-image: url(../img/progressbarfail.png) !important;
    background-color:#EFF39B !important;
}

/*프로그레스바 만들기 끝 */








Post by 넥스트리소프트 데꾸벅(techbug)
, |
아래와 같은 상태진행(progress)바를 생성할때 사용합니다.

사용자 삽입 이미지














Ext.onReady(function() {

    var percent;
    var textfield;

    var testPanel = new Ext.Panel( {
        title : 'progressbar test',
        collapsible : true,
        renderTo : document.body,
        width : 600,
        height : 200,
        bodyStyle : 'padding:10px',
        html : '<div id="_progress_"></div>',
        tbar : [
                percent = new Ext.form.NumberField( {
                    id : '_1_',
                    maxValue : 100,
                    minValue : 0,
                    width : 50,
                    value : '20'
                }),
                textfield = new Ext.form.TextField( {
                    id : '_2_',
                    width : 400,
                    value : '다운로드: - Application already exists at path /bcf'
                }),
                '-',
                {
                    text : '변경',
                    handler : function() {
                        // 상태바의 값을 입력한 값으로 계산하여 뿌려준다.
                        pbar.updateProgress(parseInt(percent.getValue()) / 100,
                                percent.getValue() + '% - '
                                        + textfield.getValue());
                    }
                }]
    });
    // 실제 프로그래스바를 생성하는 컴포넌트
        var pbar = new Ext.ProgressBar( {
            text : 'Initializing...',
            autoWidth : false,
            width : '100%',
            cls : 'left-align',
            renderTo : '_progress_'
        });
    });
}

<body><div id="techbugbar" ></div>

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

특정 CSS Object 값에 이벤트를 걸어주는 방법에 대해서 소개한다.

아래의 소스에는 "menu"라는 id값을 가진 객체에 클릭이벤트를 걸어주는 소스이다.

 
        Ext.select('#menu').on('click', function(e){
            alert(e.target)
            text = Ext.get(e.target); //이벤트 타겟을 찾는다.
            text.puff(); //텍스트에 fade효과를 준다.
            e.stopEvent() //Event bubble효과를 막기위해서 event를 멈춘다.
        });

위의 소스는 어플리케이션 기본정보나 컴포넌트 기본정보에서 담당자 클릭시 나오는 컨텍스트 메뉴를 구현하는데 사용되었다.


사용자 삽입 이미지











사용자 삽입 이미지

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

와우~ 죽인다. Extjs와 AIR가 만나니 이런 종류의 윈도우 프로그램도 가능하구나..!!
Jack Slocum 이 작성한 AIR와 ExtJS를 이용한 Vista Gadget이다.

사용자 삽입 이미지

extjs.comAIR DOC설치후 아래 window.gadget파일을 다운로드 받은후 더블클릭한다.





AIR 설치하기 : http://extjs.com/playpen/air/docs.html
관련URL : http://jackslocum.com/blog/category/ext-js/
윈도우가젯 만들기 : MSDN

function changeDock(){
if(System.Gadget.docked){
System.Gadget.background = 'images/docked.png';
var bd = Ext.getBody();
bd.setSize(130, 250);
bd.addClass('docked');
cm.setHidden(1, true);
cm.setRenderer(0, renderTopicMini);
grid.setPosition(4, 4);
grid.setSize(121, 241);
toggleDetails(null, false);
}else{
System.Gadget.background = 'images/undocked2.png';
var bd = Ext.getBody();
bd.setSize(528, 383);
bd.removeClass('docked');
cm.setHidden(1, false);
cm.setRenderer(0, renderTopic);
grid.setPosition(14, 14);
grid.setSize(494, 349);
cm.setColumnWidth(0, 350);
toggleDetails(null, true);
}
}
 
System.Gadget.onUndock = changeDock;
System.Gadget.onDock = changeDock;



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