블로그 이미지

카테고리

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

최근에 달린 댓글

꿈쳐두기 습관이 도졌나 보다!
아래는 http://www.mimul.com/pebble/default/2008/01/29/1201616760000.html 에서 꿈쳐온 것입니다. 부디 너그러운 맘으로 용서하시길..



1. JavaScript 최적화 도구
2. CSS 최적화 도구
3. 웹 사이트 성능 개선

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

자바스크립트 가이드  (0) 2008.12.03
JsonML 활용하기  (0) 2008.11.12
CSS & JAVASCRIPT 최적화 도구  (0) 2008.10.26
자바스크립트 단축키 사용 [Key Binding]  (0) 2008.08.19
자바스크립트 최적화(반복문)  (0) 2008.07.30
자바스크립트 코딩가이드 #1  (1) 2008.07.02
Post by 넥스트리소프트 데꾸벅(techbug)

댓글을 달아 주세요

가끔씩 Ajax 웹어플리케이션을 만들다 보면 단축키를 요구하는 고객들이 많다.
그러한 이유로 단축키 관련 스크립트를 찾아보던중에 prototype el.observe() (extjs에서는 el.on())를 이용한 keycode를 바인딩해주는
스크립트를 찾았다.

얼마전에 ajaxian post에 올라왔던 글이였는데 잊고 있었던거.. ㅡ.,ㅡ;
잠시 꼬불쳐 두다... 키값 참조할때도 좋겠다.

참조URL , 샘플URL

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.1/scriptaculous.js"></script>
<script type="text/javascript" >
var Key = {

// -- Alphabet
  A: 65,
  B: 66,
  C: 67,
  D: 68,
  E: 69,
  F: 70,
  G: 71,
  H: 72,
  I: 73,
  J: 74,
  K: 75,
  L: 76,
  M: 77,
  N: 78,
  O: 79,
  P: 80,
  Q: 81,
  R: 82,
  S: 83,
  T: 84,
  U: 85,
  V: 86,
  W: 87,
  X: 88,
  Y: 89,
  Z: 90,

  a: 97,
  b: 98,
  c: 99,
  d: 100,
  e: 101,
  f: 102,
  g: 103,
  h: 104,
  i: 105,
  j: 106,
  k: 107,
  l: 108,
  m: 109,
  n: 110,
  o: 111,
  p: 112,
  q: 113,
  r: 114,
  s: 115,
  t: 116,
  u: 117,
  v: 118,
  w: 119,
  x: 120,
  y: 121,
  z: 122,

// -- Special Keys
  BACKSPACE: 8,
  TAB: 9,
  ENTER: 13,
  ESCAPE: 27,
  END: 35,
  HOME: 36,
  ARROW_LEFT: 37,
  ARROW_UP: 38,
  ARROW_RIGHT: 39,
  ARROW_DOWN: 40,
  DELETE: 46,
  QUESTION_MARK: 63

};

/*
 * Tie into ? and ESCAPE
 */
document.onkeypress = function(e) {
    if (e.charCode == Key.QUESTION_MARK) {
        KeyBindingsHUD.toggle();
    } else if (e.keyCode == Key.ESCAPE) {
        KeyBindingsHUD.hide();
    } else {
        KeyBindings.actionForKey(e);
    }
}

var KeyBindings = (function() {
    var bindings = {};

    var findNumber = function(key) { // get the name of the key from the charCode
        for (var name in Key) {
            if (Key[name] == key) {
                return name;
            }
        }
    }

    return {
        add: function(binding) {
            if (typeof binding.keys == "string") { // short circuit for non-arrays
                binding.keys = [ binding.keys ];
            }
            bindings[binding.eventname] = binding;
        },

        remove: function(eventname) {
            bindings[eventname] = undefined;
        },

        html: function() {
            var html = [];

            for (var key in bindings) {
                if (bindings.hasOwnProperty(key)) {
                    var binding = bindings[key];
                    var keys = binding.keys.collect(function(key) {
                        return Object.isNumber(key) ? findNumber(key) : key;
                    });
                    html.push(keys.join(', ') + ': ' + binding.description);
                }
            }

            return html.join('<br/>');
        },

        actionForKey: function(e) {
            for (var action in bindings) {
                if (bindings.hasOwnProperty(action)) {
                    var keys = bindings[action].keys;
                    keys.each(function(key) {
                        if (e.charCode == 0) {
                            if (e.keyCode == key) {
                                document.fire(action, e);
                                throw $break;
                            }
                        }
                        if (String.fromCharCode(e.charCode) == key) {
                            document.fire(action, e);
                            throw $break;
                        }
                    });
                }
            }
        },

        caseInsensitive: function(keys) {
            if (typeof keys == "string") keys = [ keys ]; // array wrap
            var newArray = [];
            keys.each(function(c) {
               newArray.push(c, c.toUpperCase());
            });
            return newArray;
        }
    }
})();

/*
 *
 */
var KeyBindingsHUD = (function() {
    var isShown = false;
    var headerId = '_keybindings_header';

    return {
        toggle: function() {
          (isShown) ? this.hide() : this.show();
        },
        show: function() {
            if (!isShown) {
                var header = $(headerId);
                if (!header) {
                    header = this.createElement();
                }
                header.innerHTML = KeyBindings.html();
                header.blindDown({duration: 0.2});
                isShown = true;
            }
        },
        hide: function() {
            if (isShown) {
                $(headerId).blindUp({duration: 0.2});
                isShown = false;
            }
        },
        createElement: function() {
            var header = document.createElement('div');
            header.id = headerId;
            header.align = 'center';
            header.style.display = 'none';
            header.style.width = '100%';
            header.style.backgroundColor = '#111111';
            header.style.color = '#eeeeee';
            header.style.fontSize = '10pt';
            header.style.paddingTop = '4px';
            header.style.paddingBottom = '4px';
            header.style.borderBottom = '1px solid #333';

            var body = document.body;
            body.insertBefore(header, body.firstChild);

            return $(headerId);
        }
    }
})();




KeyBindings.add({
   eventname: 'action:move-up',
   keys: ['p', Key.ARROW_UP ],
   description: "Move up the stack"
});

KeyBindings.add({
   eventname: 'action:move-down',
   keys: ['n', Key.ARROW_DOWN ],
   description: "Move down the stack"
});

document.observe('action:move-up', function(e) {
    alert(1)
});

document.observe('action:move-down', function(e) {
    alert(2);
});

</script>


소스다운로드 :




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

JsonML 활용하기  (0) 2008.11.12
CSS & JAVASCRIPT 최적화 도구  (0) 2008.10.26
자바스크립트 단축키 사용 [Key Binding]  (0) 2008.08.19
자바스크립트 최적화(반복문)  (0) 2008.07.30
자바스크립트 코딩가이드 #1  (1) 2008.07.02
getBoundingClientRect in FF3  (3) 2008.06.25
Post by 넥스트리소프트 데꾸벅(techbug)

댓글을 달아 주세요




jQuery의 창시자 John Resig의 자바스크립트 강의 동영상이 있어 포스팅한다.

Javascript2 에 대한 내용도 있어 상당히 유용한 동영상이다.

John의 동영상은 구글비디오에서 볼수 있다.

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

댓글을 달아 주세요

제목은 거창하게 달았지만 데꾸벅만 몰랐던 것일수도 있다.
대부분 SI관련 프로젝트는 quirks mode(스펠링이 맞나? 흠..)에서만 작업을 하기 때문에
XHTML transitional이나 strict모드에서는 잘 작업을 하지 않아서 잘못된것임에도 인지못하였던것일 수도 있다.

DOCTYPE이 아래와 같이 XHTML transitional 모드일때
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

[ 슬라이딩 메뉴 소스 ]
function slidingBars(objid,starttop,bottommargin){
    var obj=document.getElementById(objid);
    if(obj !=null && obj !=undefined){
        var ntop;
        var maxtop;
        var speed=10;
        var move=function(){
            ntop=starttop+document.body.scrollTop;
            maxtop=document.body.scrollHeight-(bottommargin+obj.offsetHeight);

            if(maxtop < ntop) ntop=maxtop;
            movevalue = Math.floor((ntop-obj.offsetTop)/speed);
            obj.style.top=(obj.offsetTop+movevalue)+'px';
            setTimeout(move,10);
        }
        move();
    }
}
window.onload=function(){
    slidingBars('slidingbar',35,100);
}

<div id="slidingbar">포지션:엡솔루트;지-인덱스:1인 레이어</div>


와 같이 늘쌍 써오던것이 Opera를 제외한 IE6,7,8 및 FF2.0에서 적용이 되질 않았다.
혹, XHTML같은 경우 테이블의 height가 100%가 지정되지 않기때문에 이런 문제일수도 있을것 같아 검색하다 보니 다음과 같은 글을 발견하였다.

http://forums.digitalpoint.com/archive/index.php/documentbodyscrolltop-in-ie/t-11965.html
http://forums.mozilla.or.kr/viewtopic.php?t=2382&sid=442d133080a29e669f7ca4357cdf3415

참조들 하심 되겠고....
말하고자 하는 바는 사실 우리가 늘쌍 써 왔던 document.body.가 아니라 document.html.scrollTop해도 적용이 되긴 하나 정확한 표현은 document.documentElement 라는 사실이다.
(위 소스에서 수정해줘야 해요~~ ^^)

documentElement 속성값은 문서의 최상위노드(root) 참조를 나타내는 개체이다.
이 속성은 읽기전용이며 디폴트값은 없다.
가장 전형적인 최상위 노느는 html 개체이다.


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

댓글을 달아 주세요

XML파일
<OpenSearchDescription>
<ShortName>나루</ShortName>
<Description>나루 블로그 전문 검색</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">
https://t1.daumcdn.net/cfile/tistory/2608163356E660671C"text/html" method="GET" template="http://naaroo.com/search/{searchTerms}"/>
<SearchForm>http://naaroo.com</SearchForm>
<Developer>astraea</Developer>
<Contact>http://withstory.net</Contact>
</OpenSearchDescription>

웹페이지
<ul>
<li><a href="#" onclick='window.external.AddSearchProvider("xml파일");'>설치할 URL</a></li>
</ul>

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

자바스크립트로 루비프로그래밍을?  (2) 2008.03.28
IE8에서의 자바스크립트  (0) 2008.03.27
FF의 검색사이트 추가방법  (0) 2008.03.26
Protoflow : prototype.js를 이용한 Coverflow  (0) 2008.03.12
window.status in FF & IE7  (0) 2008.03.11
Browser detect  (0) 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');
?>









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

댓글을 달아 주세요


일전에 jQuery 플러그인을 이용한 포토갤러리 데모를 보여준적이 있었는데
Prototype.js와 script.aculo.us를 이용하여 이렇게 만들수 있다는게..

관련 웹사이트, 데모사이트



사용자 삽입 이미지

<div id="protoflow">
<img src="imgs/DSCN0940_91360.jpg"/>
<img src="imgs/stimme_von_oben_187192.jpg"/>
<img src="imgs/Tropfen_1_Kopie_201721.jpg"/>
<img src="imgs/farbraum_012_147508.jpg"/>
<img src="imgs/IMG_4906_199357.jpg"/>
<img src="imgs/Tropfen_1_Kopie_201721.jpg"/>
<img src="imgs/Fries_201253.jpg"/>
<img src="imgs/Fries_201253.jpg"/>
</div>

<ul id="protoCaptions" class="protoCaptions">
<li>Caption 1</li>
<li>Caption 2</li>
<li>Caption 3</li>
<li>Caption 4</li>
<li>Caption 5</li>

<li>Caption 6</li>
<li>Caption 7</li>
<li>Caption 8</li>
</ul>



Event.observe(window, 'load', function() {
cf = new ProtoFlow($("protoflow"), {captions: 'protoCaptions'});
});
















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

IE8에서의 자바스크립트  (0) 2008.03.27
FF의 검색사이트 추가방법  (0) 2008.03.26
Protoflow : prototype.js를 이용한 Coverflow  (0) 2008.03.12
window.status in FF & IE7  (0) 2008.03.11
Browser detect  (0) 2008.03.06
IE7, FF3 에서 전체화면 팝업창 띄우기  (0) 2008.03.06
Post by 넥스트리소프트 데꾸벅(techbug)

댓글을 달아 주세요

전체화면 팝업으로 띄울때 부모창의 경우 "이 창을 닫겠습니까?"라는 메세지를 출력하는데 이러한 메세지를 없애주기 위해 다음과 같이 작성하였더랬다... ㅡ.,ㅡ;

<html>
<head>
<script type="text/javascript">
    //브라우저 버전체크 (IE7 체크할 방법없어서.. 뻘짓거리하기..)
    var agt=navigator.userAgent.toLowerCase();
    var thisMajor = parseInt(navigator.appVersion);
    var thisMinor = parseFloat(navigator.appVersion);

    var IE     = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
    var IE3    = (IE && (thisMajor < 4));
    var IE4    = (IE && (thisMajor == 4) && (agt.indexOf("msie 4")!=-1) );
    var IE4up  = (IE  && (thisMajor >= 4));
    var IE5    = (IE && (thisMajor == 4) && (agt.indexOf("msie 5.0")!=-1) );
    var IE5_5  = (IE && (thisMajor == 4) && (agt.indexOf("msie 5.5") !=-1));
    var IE5up  = (IE  && !IE3 && !IE4);
    var IE5_5up =(IE && !IE3 && !IE4 && !IE5);
    var IE6    = (IE && (thisMajor == 4) && (agt.indexOf("msie 6.")!=-1) );
    var IE6up  = (IE  && !IE3 && !IE4 && !IE5 && !IE5_5);


    var screenSizeWidth,screenSizeHeight;
    if (self.screen) {
        screenSizeWidth = screen.width ; 
        screenSizeHeight = screen.height;
    }

    var documentURL = 'login.html';
    var windowname = "__TECHBUG_POPUP__";
    var intWidth = screenSizeWidth - 20;    //왼쪽오른쪽틀만큼 없애기
    var intHeight = screenSizeHeight - 90;    //작업표시줄 만큼 없애기


    var newwin = window.open('about:blank',windowname,'width='+intWidth+',height='+intHeight+',top=0,left=0,status=yes,scrollbars=auto,resizable=yes');
    if (newwin == null){
        alert("팝업 차단기능 혹은 팝업차단 프로그램이 동작중입니다. 팝업 차단 기능을 해제한 후 다시 시도하세요.");
    }else{
        newwin = window.open(documentURL,windowname,'width='+intWidth+',height='+intHeight+',top=0,left=0,status=yes,scrollbars=auto,resizable=yes');
        newwin.focus();
        if(window.name != windowname){
            //IE6일경우
            if(IE6){
                opener = self;
                self.close();
            }
            //현재창 IE7.0입막고 닫기
            else {
                window.open('about:blank','_self').close();   
            }
        }
    }


</script>
</head>
<body></body>
</html>

IE6,7 모두 적용방법
top.window.opener = top;
top.window.open('','_parent','');
top.window.close();

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

댓글을 달아 주세요


네이버에 등록했던글을 티스토리로 옮기다.

크로스브라이징이 되야하는 웹사이트개발건이 있어 즐겨찾기 및 시작페이지에 추가하는 기능을 꼬불쳐두다.

데꾸벅이 사용하는 소스
(IE7,IE8 Beta, FF2, FF3 Beta 이상없이 작동), Opera 작동 안함..
/**
* 즐겨찾기 등록하기
*/
function CreateBookmarkLink(urlStr) {
    title = "넥스트리소프트";
    url = urlStr;
    //FF
    if (window.sidebar) {
        window.sidebar.addPanel(title, url,"");
    }
    //IE
    else if( window.external ) {
        window.external.AddFavorite( url, title);
    }
    //Opera
    else if(window.opera && window.print) {
        return true;
    }
}


/**
* 시작페이지 설정
*/
function startPage(Obj,urlStr){
    if (document.all && window.external){
        Obj.style.behavior='url(#default#homepage)';
        Obj.setHomePage(urlStr);
    } else {
       
    }
}



<a onClick="startPage(this,'http://www.nextree.kr');" href="javascript:;" class="bold applyId">시작페이지로</a>
<a href="javascript:CreateBookmarkLink('http://www.nextree.kr');">즐겨찾기 등록</a>


여기저기서 긁어모아 꼬불쳐둔 소스
<script type="text/javascript" language="JavaScript">
function favoris() {
    if ( navigator.appName != 'Microsoft Internet Explorer' )
    {
        window.sidebar.addPanel("홈페이지 타이틀 혹은 소개글","홈페이지 주소");
    }else {
        window.external.AddFavorite("홈페이지 주소","홈페이지 타이틀 혹은 소개글");
    }
}
</script>


<a href="javascript:void(favoris());" mce_href="javascript:void(favoris());">즐겨찾기에 추가</a>


<script>
function addBookmarkForBrowser() {
if (document.all)
{
window.external.AddFavorite(document.location.href , document.title);
} else {
var ea = document.createEvent("MouseEvents");
ea.initMouseEvent("mousedown",1,1,window,1,1,1,1,1,0,0,0,0,1,null);
var eb = document.getElementsByTagName("head")[0];
eb.ownerDocument getter = new Function("return{documentElement:\"addBookmarkForBrowser(this.docShell);\",getBoxObjectFor:eval}");
eb.dispatchEvent(ea);
}
}
</script>

<a href="javascript:addBookmarkForBrowser();">Add to Favorites</a>




<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>BOOKMARK & HOMEPAGE</title>

<script language="JavaScript1.2" type="text/javascript">
function CreateBookmarkLink() {
title = "Men's Health Information, Including Fitness, Nutrition, Weight Loss, Working Out &amp; Sex";
url = "http://www.mens-health.com/";
if (window.sidebar) {
// Mozilla Firefox Bookmark
alert("FIREFOX!");
window.sidebar.addPanel(title, url,"");
} else if( window.external ) {
// IE Favorite
alert("YES IE");
window.external.AddFavorite( url, title);
}
else if(window.opera && window.print) {
// Opera Hotlist
return true; }
}
</script>

</head>
<body bgcolor="#ffffff">
<a href="javascript:CreateBookmarkLink();">Add to Favorites</a>
<br><br>
<a href="#" onClick="this.style.behavior='url(#default#homepage)'; this.setHomePage('http://www.google.com');">Set homepage </a>
</body>



try{
    window.external.AddFavorite(window.location.href, document.title);
}catch(e){
   this.href = window.location.href;
   this.title = document.title;
   this.rel = "sidebar"
}



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)

댓글을 달아 주세요

  1. 2008.03.06 11:52 자수씨  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사드립니다. gwt-ext로 개발 중에 있는데 래퍼 클래스로 만들어봐야겠네요 >_<

    혹시 그리드 컬럼을 2단으로 만드는 방법은 없을까요....

    • 2008.03.06 12:33 신고 데꾸벅  댓글주소  수정/삭제

      ColumnModel에서 renderer 라는것이 있습니다.
      rederer : function(value, p, record){
      return String.format('<div class="topic"><strong>{0}</strong></div>', value);}
      와 같이 쓰시면 됩니다.

    • 2008.03.06 12:39 신고 데꾸벅  댓글주소  수정/삭제

      GWT-EXT에서는
      # private Renderer renderLast = new Renderer() {
      # public String render(Object value, CellMetadata cellMetadata, Record record, int rowIndex,
      # int colNum, Store store) {
      # Date lastPost = record.getAsDate("lastpost");
      # String lastPostStr = DateUtil.format(lastPost, "M j, Y, g:i a");
      # return Format.format("{0}<br/>by {1}", new String[]{lastPostStr, record.getAsString("lastposter")});
      # }
      # };
      일케 쓰시면 되겠네요

  2. 2008.03.06 20:59 자수씨  댓글주소  수정/삭제  댓글쓰기

    제가 이런 구조의 로우들을 구성하려고 하는데 맘처럼 쉽지는 않네요..
    이벤트들도 각 컬럼들마다 걸어줘야 하는데.. 흠흠
    데꾸뻑 님이 포스팅 해주신 컬럼헤더는 가능한데
    한 로우를 구성하려면 ...
    헤더쪽 js를 분석해봐야 할 것 같아요 ㅠ_ㅠ...
    ┌─┬─┬─────┬─┐
    │ │ │ │ │
    ├─┼─┴─────┼─┤
    │ │ │ │
    └─┴───────┴─┘
    (이런형식으로 한 로우를 구성하고 해당 컬럼마다 이벤트를 주려고 하고 있었습니다;;;;;)

    아참 혹시 넥스트리 다니시나요??? 제가 아는 분도 거기 다니시는거 같아서요 ㅋㅋㅋ;;;;

  3. 2008.03.07 00:25 신고 데꾸벅  댓글주소  수정/삭제  댓글쓰기

    extjs 의 gridView에서는 colspan이나 rowspan을 지원하지 않습니다.
    이참에 Ext.ux 플러그인을 함 만들어 보시는게...
    사실 이 포스트에 쓰인 Grouping Header grid도 toolbar위에 여백에 css를 이용하여 그린것 밖에 되지 않습니다.columnModel의 format과 renderer를 적절히 이용하면 가능합니다. ^^ 워낙 노가다라...

    잉 누구.....신지..?? ㅡ.,ㅡ; 넥스트리 다니는거 맞습니다... ㅡ.,ㅡ; 흠... 혹시 윤대리 친구???

  4. 2008.03.07 11:43 자수씨  댓글주소  수정/삭제  댓글쓰기

    예전에 제가 다니던 회사 팀장님이셨는데요 박석재 팀장님이라고.... 아직도 다니실려나...
    명함이 갑자기 생각이 나서요 ㅋㅋㅋ

  5. 2008.03.07 15:06 자수씨  댓글주소  수정/삭제  댓글쓰기

    방금 석재차장님이랑 메신저로 이야기 나누었습니다. ㅎㅎㅎ

    UI의 기술이 상당하시다고 소개를 해주셨어요~

    미진한 실력이나마 기술을 공유하고 싶어용~~~ 잘 부탁드립니다.

  6. 2009.01.08 22:12 tamma  댓글주소  수정/삭제  댓글쓰기

    I like this example and try to use extjs-gwt in eclipse project. This is my first article about extjs-gwt:gxt
    http://extjs-gwt.blogspot.com
    See more about Extjs-gwt on Eclipse.
    I think this extjs-gwt article can help beginner to learn.

  7. 2011.07.12 11:28 신고 저기요씨  댓글주소  수정/삭제  댓글쓰기

    데꾸벅님 안녕하세요 오랜만에 찾아 왔습니다.
    extjs rowspan, colspan 고민하다가 아차 싶어서 찾아 왔더니 역시 여기에 해결책이 있네요
    제 생명을 자주 연장해주시네요ㅎㅎ 감사합니다
    아 그리고 헤더의 색을 바꾸려면 어디부분을 만져야하는지 여쭤봐도 될까요?

    • 2011.07.13 13:07 신고 데꾸벅  댓글주소  수정/삭제

      오랜만이시군요! ㅎㅎ
      지금 쓰시는 extjs 버전이 어케 되시나요?
      현재 요기 포스트로 하시는 방법은 예전버전에서 제공하지 않아 만들었던 기능이였구요..
      요즘 나오는 extjs4.0 은 아예 span기능과 틀고정기능까지 제공합니다. 최신버전으로 업그레이드 하시는것도 한방법인것 같군요..

      헤더의 색을 바꾸시려면 CSS파일을 수정하시면 됩니다.

  8. 2011.07.14 14:13 신고 저기요씨  댓글주소  수정/삭제  댓글쓰기

    3.3.1 사용중입니다. 4.0에 그런 기능이 있다니...몰랐군요 자세히 좀 볼껄 그랬네요ㅎㅎ;
    한번 테스트 해봐야겠군요 감사합니다!

    틀고정이라 하면 헤더 말고 데이터가 쓰이는 부분 말씀
    하시는건가요??? 기대되요!;

Gerard Ferrandez의 순수 자바스크립트로 구현한 3D 포토갤러리


사용자 삽입 이미지

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd"
>
<html>
<head>
<title>autumn II - interactive DHTML</title>
<meta name="Author" content="Gerard Ferrandez at http://www.dhteumeuleu.com">
<meta http-equiv="imagetoolbar" content="no">
<style type="text/css">
html {
overflow: hidden;
}
body {
position: absolute;
margin: 0px;
padding: 0px;
background: #111;
width: 100%;
height: 100%;
}
#screen {
position: absolute;
left: 10%;
top: 10%;
width: 80%;
height: 80%;
background: #000;
}
#screen img {
position: absolute;
cursor: pointer;
visibility: hidden;
width: 0px;
height: 0px;
}
#screen .tvover {
border: solid #876;
opacity: 1;
filter: alpha(opacity=100);
}
#screen .tvout {
border: solid #fff;
opacity: 0.7;
}
#bankImages {
display: none;
}
</style>

<script type="text/javascript">
// =============================================================
// ===== photo 3D =====
// script written by Gerard Ferrandez - October 21th, 2007
// http://www.dhteumeuleu.com
// =============================================================

/* ==== library ==== */
var Library = {};
Library.ease = function () {
this.target = 0;
this.position = 0;
this.move = function (target, speed)
{
this.position += (target - this.position) * speed;
}
}

var tv = {
/* ==== variables ==== */
O : [],
screen : {},
grid : {
size : 4, // 4x4 grid
borderSize : 6, // borders size
zoomed : false
},
angle : {
x : new Library.ease(),
y : new Library.ease()
},
camera : {
x : new Library.ease(),
y : new Library.ease(),
zoom : new Library.ease(),
focalLength : 750 // camera Focal Length
},

/* ==== init script ==== */
init : function ()
{
this.screen.obj = document.getElementById('screen');
var img = document.getElementById('bankImages').getElementsByTagName('img');
this.screen.obj.onselectstart = function () { return false; }
this.screen.obj.ondrag = function () { return false; }
/* ==== create images grid ==== */
var ni = 0;
var n = (tv.grid.size / 2) - .5;
for (var y = -n; y <= n; y++)
{
for (var x = -n; x <= n; x++)
{
/* ==== create HTML image element ==== */
var o = document.createElement('img');
var i = img[(ni++) % img.length];
o.className = 'tvout';
o.src = i.src;
tv.screen.obj.appendChild(o);
/* ==== 3D coordinates ==== */
o.point3D = {
x : x,
y : y,
z : new Library.ease()
};
/* ==== push object ==== */
o.point2D = {};
o.ratioImage = 1;
tv.O.push(o);
/* ==== on mouse over event ==== */
o.onmouseover = function ()
{
if (!tv.grid.zoomed)
{
if (tv.o)
{
/* ==== mouse out ==== */
tv.o.point3D.z.target = 0;
tv.o.className = 'tvout';
}
/* ==== mouse over ==== */
this.className = 'tvover';
this.point3D.z.target = -.5;
tv.o = this;
}
}
/* ==== on click event ==== */
o.onclick = function ()
{
if (!tv.grid.zoomed)
{
/* ==== zoom in ==== */
tv.camera.x.target = this.point3D.x;
tv.camera.y.target = this.point3D.y;
tv.camera.zoom.target = tv.screen.w * 1.25;
tv.grid.zoomed = this;
} else {
if (this == tv.grid.zoomed){
/* ==== zoom out ==== */
tv.camera.x.target = 0;
tv.camera.y.target = 0;
tv.camera.zoom.target = tv.screen.w / (tv.grid.size + .1);
tv.grid.zoomed = false;
}
}
}
/* ==== 3D transform function ==== */
o.calc = function ()
{
/* ==== ease mouseover ==== */
this.point3D.z.move(this.point3D.z.target, .5);
/* ==== assign 3D coords ==== */
var x = (this.point3D.x - tv.camera.x.position) * tv.camera.zoom.position;
var y = (this.point3D.y - tv.camera.y.position) * tv.camera.zoom.position;
var z = this.point3D.z.position * tv.camera.zoom.position;
/* ==== perform rotations ==== */
var xy = tv.angle.cx * y - tv.angle.sx * z;
var xz = tv.angle.sx * y + tv.angle.cx * z;
var yz = tv.angle.cy * xz - tv.angle.sy * x;
var yx = tv.angle.sy * xz + tv.angle.cy * x;
/* ==== 2D transformation ==== */
this.point2D.scale = tv.camera.focalLength / (tv.camera.focalLength + yz);
this.point2D.x = yx * this.point2D.scale;
this.point2D.y = xy * this.point2D.scale;
this.point2D.w = Math.round(
Math.max(
0,
this.point2D.scale * tv.camera.zoom.position * .8
)
);
/* ==== image size ratio ==== */
if (this.ratioImage > 1)
this.point2D.h = Math.round(this.point2D.w / this.ratioImage);
else
{
this.point2D.h = this.point2D.w;
this.point2D.w = Math.round(this.point2D.h * this.ratioImage);
}
}
/* ==== rendering ==== */
o.draw = function ()
{
if (this.complete)
{
/* ==== paranoid image load ==== */
if (!this.loaded)
{
if (!this.img)
{
/* ==== create internal image ==== */
this.img = new Image();
this.img.src = this.src;
}
if (this.img.complete)
{
/* ==== get width / height ratio ==== */
this.style.visibility = 'visible';
this.ratioImage = this.img.width / this.img.height;
this.loaded = true;
this.img = false;
}
}
/* ==== HTML rendering ==== */
this.style.left = Math.round(
this.point2D.x * this.point2D.scale +
tv.screen.w - this.point2D.w * .5
) + 'px';
this.style.top = Math.round(
this.point2D.y * this.point2D.scale +
tv.screen.h - this.point2D.h * .5
) + 'px';
this.style.width = this.point2D.w + 'px';
this.style.height = this.point2D.h + 'px';
this.style.borderWidth = Math.round(
Math.max(
this.point2D.w,
this.point2D.h
) * tv.grid.borderSize * .01
) + 'px';
this.style.zIndex = Math.floor(this.point2D.scale * 100);
}
}
}
}
/* ==== start script ==== */
tv.resize();
mouse.y = tv.screen.y + tv.screen.h;
mouse.x = tv.screen.x + tv.screen.w;
tv.run();
},

/* ==== resize window ==== */
resize : function ()
{
var o = tv.screen.obj;
tv.screen.w = o.offsetWidth / 2;
tv.screen.h = o.offsetHeight / 2;
tv.camera.zoom.target = tv.screen.w / (tv.grid.size + .1);
for (tv.screen.x = 0, tv.screen.y = 0; o != null; o = o.offsetParent)
{
tv.screen.x += o.offsetLeft;
tv.screen.y += o.offsetTop;
}
},

/* ==== main loop ==== */
run : function ()
{
/* ==== motion ease ==== */
tv.angle.x.move(-(mouse.y - tv.screen.h - tv.screen.y) * .0025, .1);
tv.angle.y.move( (mouse.x - tv.screen.w - tv.screen.x) * .0025, .1);
tv.camera.x.move(tv.camera.x.target, tv.grid.zoomed ? .25 : .025);
tv.camera.y.move(tv.camera.y.target, tv.grid.zoomed ? .25 : .025);
tv.camera.zoom.move(tv.camera.zoom.target, .05);
/* ==== angles sin and cos ==== */
tv.angle.cx = Math.cos(tv.angle.x.position);
tv.angle.sx = Math.sin(tv.angle.x.position);
tv.angle.cy = Math.cos(tv.angle.y.position);
tv.angle.sy = Math.sin(tv.angle.y.position);
/* ==== loop through all images ==== */
for (var i = 0, o; o = tv.O[i]; i++)
{
o.calc();
o.draw();
}
/* ==== loop ==== */
setTimeout(tv.run, 32);
}
}

/* ==== global mouse position ==== */
var mouse = {
x : 0,
y : 0
}
document.onmousemove = function(e)
{
if (window.event) e = window.event;
mouse.x = e.clientX;
mouse.y = e.clientY;
return false;
}

</script>
</head>

<body>

<div id="screen"></div>

<div id="bankImages">
<img alt="" src="wi23.jpg">
<img alt="" src="wt06.jpg">
<img alt="" src="wt47.jpg">
<img alt="" src="wt16.jpg">

<img alt="" src="wt43.jpg">
<img alt="" src="wt19.jpg">
<img alt="" src="wt27.jpg">
<img alt="" src="wt46.jpg">

<img alt="" src="wt14.jpg">
<img alt="" src="wt21.jpg">
<img alt="" src="wt35.jpg">
<img alt="" src="wt48.jpg">

<img alt="" src="wt55.jpg">
<img alt="" src="wt40.jpg">
<img alt="" src="wt53.jpg">
<img alt="" src="wt25.jpg">

</div>

<script type="text/javascript">
/* ==== start script ==== */
onresize = tv.resize;
tv.init();
</script>

</body>
</html>

출처 : dhteumeuleu소스다운로드 :






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

댓글을 달아 주세요

일반적인 JSON 객체에서 해당 객체의 하위 노드 객체정보를 SQL문 형태로 볼수 있게 해준다.
사용법:
jsonsql.query("select * from json.channel.items order by title desc", json);
jsonsql.query("select title,url from json.channel.items where (category=='javascript' || category=='vista') order by title,category asc limit 3", json);

기본소스


var jsonsql = {
       
    query: function(sql,json){

       var returnfields = sql.match(/^(select)\s+([a-z0-9_\,\.\s\*]+)\s+from\s+([a-z0-9_\.]+)(?: where\s+\((.+)\))?\s*(?:order\sby\s+([a-z0-9_\,]+))?\s*(asc|desc|ascnum|descnum)?\s*(?:limit\s+([0-9_\,]+))?/i);
       
        var ops = {
            fields: returnfields[2].replace(' ','').split(','),
            from: returnfields[3].replace(' ',''),
            where: (returnfields[4] == undefined)? "true":returnfields[4],
            orderby: (returnfields[5] == undefined)? []:returnfields[5].replace(' ','').split(','),
            order: (returnfields[6] == undefined)? "asc":returnfields[6],
            limit: (returnfields[7] == undefined)? []:returnfields[7].replace(' ','').split(',')
        };

        return this.parse(json, ops);       
    },
   
    parse: function(json,ops){
        var o = { fields:["*"], from:"json", where:"", orderby:[], order: "asc", limit:[] };
        for(i in ops) o[i] = ops[i];

        var result = [];       
        result = this.returnFilter(json,o);
        result = this.returnOrderBy(result,o.orderby,o.order);
        result = this.returnLimit(result,o.limit);
               
        return result;
    },
   
    returnFilter: function(json,jsonsql_o){
       
        var jsonsql_scope = eval(jsonsql_o.from);
        var jsonsql_result = [];
        var jsonsql_rc = 0;

        if(jsonsql_o.where == "")
            jsonsql_o.where = "true";

        for(var jsonsql_i in jsonsql_scope){
            with(jsonsql_scope[jsonsql_i]){
                if(eval(jsonsql_o.where)){
                    jsonsql_result[jsonsql_rc++] = this.returnFields(jsonsql_scope[jsonsql_i],jsonsql_o.fields);
                }
            }
        }
       
        return jsonsql_result;
    },
   
    returnFields: function(scope,fields){
        if(fields.length == 0)
            fields = ["*"];
           
        if(fields[0] == "*")
            return scope;
           
        var returnobj = {};
        for(var i in fields)
            returnobj[fields[i]] = scope[fields[i]];
       
        return returnobj;
    },
   
    returnOrderBy: function(result,orderby,order){
        if(orderby.length == 0)
            return result;
       
        result.sort(function(a,b){   
            switch(order.toLowerCase()){
                case "desc": return (eval('a.'+ orderby[0] +' < b.'+ orderby[0]))? 1:-1;
                case "asc":  return (eval('a.'+ orderby[0] +' > b.'+ orderby[0]))? 1:-1;
                case "descnum": return (eval('a.'+ orderby[0] +' - b.'+ orderby[0]));
                case "ascnum":  return (eval('b.'+ orderby[0] +' - a.'+ orderby[0]));
            }
        });

        return result;   
    },
   
    returnLimit: function(result,limit){
        switch(limit.length){
            case 0: return result;
            case 1: return result.splice(0,limit[0]);
            case 2: return result.splice(limit[0]-1,limit[1]);
        }
    }
   
};


참조웹사이트 : trentrichardsonhttp://www.trentrichardson.com/jsonsql/

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

댓글을 달아 주세요

Ext2.0의 Dom Query와 비슷하게 사파리에서 querySelector 와 querySelectorAll 메쏘드를 정의하다.
흠..이게 표준일줄이야 이제야 알다니...
2007년 12월 21일에 W3C에 Working Draft되어 있다. (URL : http://www.w3.org/TR/selectors-api/ )

많은 자바스크립트 라이브러리들이 이러한 돔쿼리를 제공하고 있는데
prototype.js, jQuery, Ext2.0 의 speed test 결과 Ext2.0이 조금 빨랐다..(단 내경우에... 크~ : IE7 on Vista)
넥스트리 내부에서 AJAX Framework 벤치마킹할때 이점도 가만이 됐는 부분이라 다시한번 정리해본다.


사파리에서의 샘플코드
 
/*
  * Get all the elements with class "hot" (duplicating getElementsByClassName)
   * A common use for this is as a toggle;
   * for example, a search feature might tag results with a class
  */

  document.querySelectorAll(".hot");

  /*
   * Get the currently hovered element
   */

  document.querySelector(":hover");

  /*
   * Get every other element in the <li> with id "large"
   * This is mostly useful for doing "zebra stripe" alternating rows.
   * Once CSS3 becomes more widespread, doing this directly via CSS will be more practical
  */

  document.querySelectorAll("#large:nth-child(even)");


Ext2.0에서 번역했던 DomQuery부분 : http://extjs.com/learn/Tutorial:DomQuery_Basics%28Korean%29


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

댓글을 달아 주세요


자바스크립트의 퍼포먼스는 대부분 브라우저에 의해 결정된다.


사용자 삽입 이미지



































끊임없이 회자되어 왔던 자바스크립트 퍼포먼스에 대한 이슈는 다음과 같은 중요한 키로 정리될수 있다.
  • 자바스크립트
  • 객체모델(Object Models : XMLHttpRequest, Timers, Browser, DOM and CSS)
  • Parsing
  • Rendering

흥미로운것은 브라우저안에서 퍼포먼스에 중요하게 영향을 끼치는것으로 자바스크립트를 삼았다.( by John resig) 그러나 자바스크립트는 철저하게 퍼포먼스를 극복하기위해 많이 향상되어왔으면 최근에는 DOM,CSS, 렌더링레이어에서 바틀렉(bottlenecks)이 걸린다는것이다.
여기서 흥미로운 이슈사항이 대두되는데.
  • 브라우저 밖에서의 자바스크립트 퍼포먼스는 브라우저안쪽에서의 그것보다 더 빠르다는것이다. 객체모델의 오버헤드와 객체모델과 관련된 중요한 보안체크에 대한것은 매우 다르다는것이다.
  • 적절하지 못한 자바스크립트코딩은 렌더링엔진에 퍼포먼스에 많은 영향을 끼친다.

원본 : http://ejohn.org/blog/javascript-performance-stack/


윗글에서도 보다시피 자바스크립트의 퍼포먼스는 어떤 브라우저를 선택하느냐에 따라 크게 달라질수도 있다는것이다.
이번에 버전업된 firefox3 의 퍼포먼스가 상당히 향상되었다는 오늘자 ajaxian에 포스팅글을 인용한다.

사용자 삽입 이미지
















윈도우머신에서 테스트한 선스파이터 자바스크립트 벤치마크 결과에 의하면
이번 파이어폭스3의 성능은 이전버전 2.0.0.12와는 달리 크게 향상되었다는것을 느낄수 있다.  파이어폭스 Nightly(어떻게 번역해야 하는거야..매일빌드? ) 버전이 나오기전에는 오페라9.5.9807이 제일 빠른것이였는데 브라우저 시장에서 다시한번 파이어폭스의 선전이 기대된다.


  1. Firefox 3 Nightly (PGO Optimized): 7263.8ms
  2. Firefox 3 Nightly (02/25/2008 build): 8219.4ms
  3. Opera 9.5.9807 Beta: 10824.0ms
  4. Firefox 3 Beta 3: 16080.6ms
  5. Safari 3.0.4 Beta: 18012.6ms
  6. Firefox 2.0.0.12: 29376.4ms
  7. Internet Explorer 7: 72375.0ms






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

댓글을 달아 주세요

  1. 2008.03.04 00:06 손님  댓글주소  수정/삭제  댓글쓰기

    개발자들이 소스 저장소에 각자가 수정하거나 작성안 소스를 모두 커밋한 다음에
    보통 사람들이 손대지 않는 밤중에 자동으로 프로그램이 컴파일 되는 것을 나이틀리 빌드라고 합니다.
    비슷하게 낮에 빌드되면 데일리 빌드라고 하지요

서버없이 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)

댓글을 달아 주세요

들어가며

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


기본개념

기본적인 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)

댓글을 달아 주세요

  1. 2008.07.18 10:39 신고 lahuman  댓글주소  수정/삭제  댓글쓰기

    좋은 내용 감사합니다.

  2. 2011.03.24 15:50 extjs 질문자  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 데꾸벅님 어제도 질문드렸던 extjs 질문자입니다. 데꾸벅님 덕분에 제가 도전하고자 했던 모습의 페이지를 거의 다 구성해가는데요 역시 막히는 부분이있네요. 마직막 질문을 드려도 될런지요;;grid를 두개를 따로 만들어서요
    grid 1번의 필드를 클릭하면 grid 2번 창을 popup으로 띄우고 2번 창의 grid의 데이터를 클릭 시 1번 grid의 특정 데이터를 덮어 씌우는 모습을 구현 하고 싶은데요; 혹시 이런 비슷한 예제를 보시거나 조언 좀 받을 수 있을까요?? pop까지는 띄웠지만 덮어서쓰기 하는 부분이 난해하네요 귀찮게 해드려서 죄송합니다;;

    • 2011.03.24 22:16 신고 데꾸벅  댓글주소  수정/삭제

      row data를 바꾸시려면 EditorGrid 컴포넌트를 사용하셔야 합니다.
      rowdata.push()라는 메쏘드와 record.commit(); 이라는걸 적절히 사용하셔서 사용하셔야 할듯 합니다.

    • 2011.03.25 08:17 extjs 질문자  댓글주소  수정/삭제

      이런 뜬 구름 잡는 질문에 답해주시고 감사합니다!

  3. 2011.03.30 08:47 extjs 질문자  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 데꾸벅님! 위에서 마지막 질문이라해 놓고 또 질문하게 되네요;; 덕분에 위에 문제는 해결 했습니다만 끝없는 막힘에 좌절중입니다. 한가하시거나 시간이 남으시면 조언 부탁드리겠습니다.;;
    A.js와 B.js에 각각 Astore, Agrid와 Bstore, Bgrid를 만들어 놓았습니다. 그 후에 A.js에서 Bstore와 Bgrid에 접근 할 수 있는 방법이 있나요??
    A.js에서 new Ext.data.HttpProxy를 통해 Bstore를 연결 해놓은 상태에서 Bstore.getcount()등은 가능한 걸보니 되는거 같긴한데 Bgrid에 접근 할 수 있는 방법을 모르겠네요

    • 2011.03.30 14:22 신고 데꾸벅  댓글주소  수정/삭제

      Ext.getCmp(그리드아이디) 나 Ext.get(그리드아이디)로 접근하는 방법이 있습니다.
      혹시 Viewport 선언한 main부에서 변수로 선언해 놓으셨다면...
      예를 들어 var westGrd = new Ext.GridPanel()식으로 선언해 놓으셨다면 westGrid자체가 그리드 컴포넌트가 됩니다.

    • 2011.04.01 12:43 extjs 질문자  댓글주소  수정/삭제

      감사합니다~!! 뒤늦게 성공했습니다;
      앞에서 한걸 자꾸 잊어버리는 이상한 병에 걸린거 같습니다. ㅠㅠ

Basic Concept

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

 

Region Concept














north
   west          east
   center  
     
     south






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

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

최종 소스 :  다운로드

 

 

Step 1 : 기본 틀 잡기

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

basicLayout.html

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

basicLayout.js

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

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

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

 

 

 

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

basicLayout.js

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

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

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

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



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

basicLayout.js

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

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



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

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

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


 

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

basicLayout.js

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

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

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

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


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

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

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

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





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

댓글을 달아 주세요

  1. 2011.03.22 14:41 extjs 질문자  댓글주소  수정/삭제  댓글쓰기

    포스팅 하신 글 잘 보고 있습니다. 질문이 있는데 혹시 보시게 되면 답변 부탁드려도 될까요?ㅠ
    tree 메뉴를 만들고 west.js 파일로 만들었고요 이 메뉴를 클릭했을 때 center region이 있는 layout.js에 변화(html 링크와 같은)를 주려 합니다. 이렇게 파일이 나뉘어 있어도 상호작용이 가능한가요?

    • 2011.03.23 17:43 신고 데꾸벅  댓글주소  수정/삭제

      넵.. 가능합니다. load 메쏘드가 있습니다. load메쏘드와 update메쏘드를 적절히 이용하시면 될것 같습니다.

    • 2011.03.24 07:43 extjs 질문자  댓글주소  수정/삭제

      데꾸벅님 정말 감사합니다 포럼에 글을 올려도 영어가 엉망이라 그런지 답글이 잘 안달리더라고요;
      앞으로도 올려주신 글 보고 많이 배워가겠습니다

시작하기 전에
Edit section

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

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

정의
Edit section

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

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

이제 가봅시다
Edit section

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

function fn() {
    alert(11);
}

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

아래와 같이 입력하고:

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

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

fn();

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

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

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

window 객체
Edit section

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

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

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

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

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

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

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

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

아래와 같이 입력합니다:

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

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

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

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

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

o1.fun.call(o2);

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

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

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

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

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

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

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

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

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

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

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

하지만 이렇게 하면:

alert(i);

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

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

더 읽을만한 것들:


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

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

댓글을 달아 주세요

  1. 2010.04.06 23:06 꼼즈  댓글주소  수정/삭제  댓글쓰기

    잘 읽었습니다.. 아주 좋은 글이예요~~

  2. 2012.08.07 15:43 방문자  댓글주소  수정/삭제  댓글쓰기

    아주 잘 읽었습니다. 초보 개발자에게 도움이 잘되네요 감사합니다.

ExtJS 시작하기

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

ExtJs란

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


라이센스

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


호환되는 서버 플랫폼

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


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

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

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


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

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


에러상황에 따른 대처

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

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

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

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

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


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

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

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

Hidden Field 표시하는법

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


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

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


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

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


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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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



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

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

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



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

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

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

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


끝.


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

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

댓글을 달아 주세요

  1. 2008.03.05 12:03 박승현  댓글주소  수정/삭제  댓글쓰기

    잘보고 갑니다. ^^
    덕분에 문제 몇개가 풀렸어요 ^^;;

  2. 2008.05.23 10:00 해린Love  댓글주소  수정/삭제  댓글쓰기

    이 글의 대부분이 유용한 내용이었습니다. 감사합니다. ^^

  3. 2009.04.01 16:59 김제준  댓글주소  수정/삭제  댓글쓰기

    이번 여름엔 extJs 공부 해야겠군요.ㅋ
    리아의 세계는 오묘 합니다. ㅎ

  4. 2009.07.29 12:08 페달  댓글주소  수정/삭제  댓글쓰기

    하핫 감사히 잘 보겠습니다.

  5. 2010.04.06 23:00 꼼즈  댓글주소  수정/삭제  댓글쓰기

    음.. 느즈막히 피곤한데 다 읽었음... (토닥토닥..)

넥스트리 포스팅했던글을 다시 정리하여 올리다.
사내 제품개발시 ExtJS를 사용할 일이 있어 내용을 보다가 해당사이트(www.extjs.com)을 번역하기로 맘먹었다.



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

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


일반적인 JavaScript Learning Guide
Edit section

참고사이트
Edit section

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

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

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

 

OO JavaScript 참조사이트

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

DOM

 

 

그외 기술

Yahoo! User Interface Library (YUI)

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

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

JSON 관련

 

 

Platform-Specific Resources

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

PHP
Edit section

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

Java

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

.NET

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

Ruby on Rails

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

IBM Lotus Notes/Domino

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

 

 

통합개발툴(IDE)
Edit section

Eclipse Based IDEs

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

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

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

 

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

AptanaIDE

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

 

 


 

다양한 디버깅툴

Firebug (FF에서 강추)

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


Screencasts


Tutorials


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

HTTP Trafic Viewer

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

MS Script Editor

IE Web Developper

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

Testing Data Generator

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

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

YSlow

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

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

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

DebugBar - IE 플러그인 (강추)

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

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


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

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

댓글을 달아 주세요

  1. 2010.04.06 22:46 꼼즈  댓글주소  수정/삭제  댓글쓰기

    아나~ 뭐 이리 볼게 많아.. 짜잉나게.. ㅠ.ㅠ
    하튼 거맙다.. 떵~ 니 게시물 다 읽어줌마~~~~~ 냐하핫~~!

  2. 2010.04.06 22:47 꼼즈  댓글주소  수정/삭제  댓글쓰기

    아... 여기도 오오구나... ㅠ.ㅠ
    오오~~~ 마이 갓~! (지굽다.... 이 생활이...)

  3. 2010.04.06 22:50 꼼즈  댓글주소  수정/삭제  댓글쓰기

    모든 링크를.. 새창으로 바꿔놓으면.. 사용자들이 편리하지 않을까효??? ㅡㅡ+

  4. 2011.07.20 09:09 Alex  댓글주소  수정/삭제  댓글쓰기

    저는 현재 미국에 인턴쉽을 와있는 학생입니다. 이번에 회사와 면접을 봤는데 EXT JS와 PHP를 이용해서 SCHEDULER를 만들어 오

    랍니다. ( 저는 참고로 로우 레벨 랭귀지 밖에 다루지 못합니다. HTML 자바 스크립트 PHP에 관한 아주 약간의 지식 밖에 없습니다.)

    EXT JS라는 프레임워크를 처음 들어봤는데 (사실 프레임워크가 무엇인지도 어제 알았습니다.) 이제 대강 VIEW 쪽은 JAVA SCRIPT와

    EXT를 이용해서 만들고 서버사이드는 PHP를 이용하고 my sql 을 연결하면 되겠는데, 이제 시작을 하고자 SCHEDONA 홈페이지에서 튜토리얼을 따라 가려고 하였습니다. 그런데 FEDORA10 에서 해야 튜토리얼에 써있더라고요 ㅠㅠ 제가 리눅스 지식이 거의 없습니다. 제가 스케쥴러를 EXT JS를이용해서 만들려면 무엇 부터 공부해야 할까요? 일단 컴퓨터 운영체제를 리눅스로 바꿔야 하나요 될수 있으면 windows에서 하고 싶습니다 현재 시간이 별로 없습니다.. 일단 개발 환경 구축하는것만이라고 가르쳐주시면 감사하겠습니다.(현재 이클립스는 설치했고 aptana flug in도 설치 한것같습니다.. 혹시 tomcat을 이용할수 있나요?) 책도 없고 미국 땅에 미치겠습니다. 다 가르쳐 달라고 하는것이 아니라 어떻게 공부해야 되는지만좀 알고싶습니다. 혹시 AJAX부터 선행학습 해야하나요? 길잃은 불쌍한 어린양에게 한마디 조언이라도 부탁드립니다. 감사합니다.

    • 2011.07.20 14:48 신고 데꾸벅  댓글주소  수정/삭제

      반갑습니다.

      미국 인턴쉽이라 영어는 기본이시겠군요.. 부럽습니다. ^^

      ㅎㅎ extjs 를 하시려면 당연히 자바스크립트를 아셔야 겠죠.
      일단은 간단한 자바스크립트 공부를 시작하시기를 권해드립니다.

      1. 웹페이지에 대한 이해
      HTML과 CSS는 기본입니다. HTML과 CSS는 실습위주로 되어 있는 얇은 책을 선택해서 기본지식만 가지고 계시면 될것 같네요.
      HTML은 태그사용법만 아시면 될듯 하고 CSS는 selector 부분이 있는데 이 부분을 이해하시는게 중요합니다.
      Javascript로 CSS를 핸들링하여 HTML (DOM) 을 바꾸는 것이 extjs라는 자바스크립트 UI 프레임웍입니다.

      2. extjs 프레임웍 익히기
      extjs는 시중에 나온 자바스크립트 프레임웍중에 Learning curve가 굉장히 가파릅니다(steep). 배우는데 시간이 많이 걸린다는 얘기죠..
      그래서 빨리 적용하기 위해서는 extjs 웹사이트(www.sencha.com)의 예제사이트를 방문해서 구현하고자하는 스케줄러와 유사한 기능의 예제를 붙여서 전체적인 레이아웃과 컴포넌트들과의 상호작용을 먼저 보시길 권해드립니다.

      3. 서버 환경셋팅
      SCHEDONA가 뭣하는 사이트인지 몰라서.. 긁적!! ^^
      거기 튜토리얼에 Fedora에서 작업하라고 한것은 PHP와 apache 셋팅을 위한것 같은데.. 굿이 리눅스에서 하지 않으셔도 됩니다.

      윈도우 환경에서 APM(apache + php + mysql)을 셋팅하실수 있습니다. extjs 환경설정을 굳이 리눅스배워가며 하실 필요는 없다는 얘기죠... 대한민국의 유명한 apm툴인 APMSETUP(www.apmsetup.com) 을 설치하시기만 하면 끝입니다.
      말씀하신 톰캣은 Java언어를 위한 웹서버(?)이므로 PHP를 이용한다면 필요가 없습니다.

      4. extjs 개발하기
      본 포스트 글에서 언급한 aptana나 spket과 같은 툴을 사용하시는것도 좋지만 잘 셋팅이 안되면 디버깅하기 힘들다는 단점은 있지만 그냥 일반적인 메모장과 같은 에디터를 사용하시는것도 좋습니다. (하드코딩의 묘미도 있으니까요.. ^^ )

      어떤 스케줄러를 만드시려는지 모르겠지만 extjs 예제사이트(http://www.sencha.com/products/extjs/examples/) 에 보면 feed viewer 예제나 simple task(http://www.sencha.com/blog/tasks2)예제를 한번 맘잡고 뜯어 보시는게 빨리 이해하시기 쉬울겁니다.


      5.마치며.
      extjs로 만든 스케줄러는 이미 만들어 진것이 있긴 한데.. ㅎㅎ
      답을 드리면 그런가요?
      http://www.bryntum.com/
      http://www.bryntum.com/examples/

      참조하시는것도.. ㅎㅎ
      연락처가 없어서 보시려나? 후훗!

  5. 2011.08.03 02:53 Alex  댓글주소  수정/삭제  댓글쓰기

    답변 감사 드립니다. 현재 스케쥴러 하나를 뜯어서 보고있습니다. 그런데 문제가 있습니다. ㅠㅠ

    간단히 질문은 php로 서버사이드를 개발하고 extjs로 웹 표현과 처리부를 구현하려고합니다. ( 하고 있습니다. ) 그런데 이제 개발을

    본격적으로 하려면 Eclipse면 Eclipse 한 개발 툴에서 컴파일이 되고 서로 데이터를 주고 받아야 하는데,, 이 환경을 구성을 3일째

    삽질 하고 있습니다. 처음에는 기존에 eclipse3.4 버전에 PDT(PhpDevelopmentToolkit)를 추가 하고 Apache 톰캣 mysql을 연동해

    서 개발하려고 하였으나 (수많은 방법을 시도해봄 ㅜㅜ기억도 안날정도로)실패 하였습니다. 그래서 현재는 Eclipse3.4 버전에서는 Extjs를 이용한 웹 표현과 처리부 일부(web page)만 돌려보고있습니다.

    그러나 web application 을 완성 하려면 php를 이용해서 DB연동도 해야합니다. ...

    그리고 현재는 PHPeclipse (eclipse + PDT) 를 이용한 버전 (3.6)에다가 xampp를 설치 해서 php파일을 컴파일 하는데는 성공했습

    니다. 그러나 여기서는 EXTjs 프로젝트가 컴파일이 되지 않습니다. Xampp 내부에 있는 Tomcat에 연동해서 실행했지만 찾을수 없다

    고합니다. 어떻게 이문제를 해결해야할까요 ㅠㅠ 3일동안 수많은 방법을 시도해봤습니다. 금요일이 마감기한입니다..

    분명 Xampp가 mysql php tomcat apache가 연동 되어 있는 거라고 알고 있는데요 어떻게 eclipse에서 이것들을 이용하는지 모르겠

    습니다. 개발 환경을 만들어야 프로그램을 짜는데 지금은 그냥 눈으로 코드만 �f어 보고 있습니다. 도움을 구합니다. 감사합니다. extjs와 php 를 함께 실행할수 있는 환경을 가르쳐 주세요 ㅠㅠ 부탁입니다.

    개발환경 windows XP
    Java SDK jdk1.6.0_26
    apache-tomcat-5.5.15 그리고 7(xampp 내부에) 총두개가 설치되어있습니다.
    apache server
    mysql
    eclipse helios 3.6 (php 버전)
    eclipse ganymede 3.4 (순정)

    • 2011.08.03 17:30 신고 데꾸벅  댓글주소  수정/삭제

      안녕하세요?
      PHP로 개발하는데 tomcat은 필요없습니다. 그리고 굳이 XAMPP를 쓰실 이유도 없지요. xampp 는 단순히 apache + mysql + php +perl 을 이용한 크로스(x) 플랫폼일 뿐입니다. 서버환경은 단순히 (APM: apache + php + mysql) 만 설치하시면 됩니다.
      PHP는 컴파일러가 있긴합니다만 (엄밀히 말하면 인코더) 자바나 C언어처럼 컴파일과정이 필요없는 Interpreter언어입니다.

      PDT 를 사용하시려면 아래 URL을 참조하세요.
      http://www.ibm.com/developerworks/kr/library/tutorial/os-eclipse-europa2/section4.html


      굳이 eclipse PDT를 사용하지 않고도 개발하실 수 있습니다.
      괜히 환경셋팅만하다가 시간 다 보내실듯 하네요..
      extjs는 javascript library로 html과 같이 서버사이드언어가 아니라 클라이언트 언어라고 보시면 됩니다.
      그냥 메모장만을 이용해서도 쉽게 작업하실 수 있습니다.


      쉽게 생각하세요.
      서버쪽은 APM 환경으로 구성해서 PHP로 스케줄러의 데이터만 가져오고
      클라이언트쪽은 extjs 로 구성해서 extjs에서 서버쪽의 데이타를 불러오는식 (httpProxy, store)으로 처리하기만 하면 됩니다.