블로그 이미지

카테고리

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

최근에 올라온 글

최근에 달린 댓글

GWT-EXT2.0에 이어 .NET용 Ext버전이 출시됐다.

Ext Framework을 이용한 .NET 웹컨트롤 유닛인 Coolite Studio 는 Ext의 대부분의 기능을 GWT-EXT와 마찬가지로 Server-Side Ajax를 지원한다.

사용자 삽입 이미지



Post by 넥스트리소프트 데꾸벅(techbug)
, |
기본적인 에러핸들링에 대해서 설명한다.

GridPanel 의 loadexception을 이용한 ErrorHandling 방법

기본 그르드 패널에서 데이타 로드시 loadexception이벤트에 대해서 설명한다.

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,
                    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);
            this.grid.on('rowclick', function(grid, index, e) {
                alert('클릭');
            }, this);
            this.grid.on('rowdblclick', function(grid, index, e) {
                alert('더블클릭');
            }, this);
            this.gsm = this.grid.getSelectionModel();
            this.store.on('load', this.gsm.selectFirstRow, this.gsm);

            this.store.on('load', function(store, records, options) {

            }, this);

            this.store.on('loadexception',
                    function(a, conn, resp) {
                        if (resp.status == 200) {  ============> 혹은 resp.statusText를 사용
                            var jsonData = resp.responseText;
                            var result = (jsonData != undefined && jsonData
                                    .trim() != "") ? Ext.decode('(' + jsonData
                                    + ')') : null;
                            if (result != null && result.code != undefined
                                    && result.code.trim() != ''
                                    && result.message != undefined) {
                                this.showErrorStackHandler(result);
                            }
                        }
                    }, this);

            // 커넥션에서 문제가 일어났을 경우
            this.store.proxy.getConnection()
                    .on(
                            'requestcomplete',
                            function(conn, resp, options) {
                                var jsonData = resp.responseText;
                                var result = (jsonData != undefined && jsonData
                                        .trim() != "") ? Ext.decode('('
                                        + jsonData + ')') : null;
                                if (result != null && result.code != undefined
                                        && result.code.trim() != ''
                                        && result.message != undefined) {
                                    this.showErrorStackHandler(result);
                                }
                            }, this);

            this.store.on('load', function(store, records, opt) {
            });

        },

        showErrorStackHandler : function(result) {
            var code = (result.code != undefined) ? result.code : '';
            var mesg = (result.message != undefined) ? result.message : '';
            var insNm = (result.instanceName != undefined)
                    ? result.instanceName
                    : '';
            var errStack = (result.stack != undefined) ? result.stack : '';
            var occDate = (result.occurDateTime != undefined)
                    ? result.occurDateTime
                    : '';

            var markup = "<div><ul>" + "<li>에러코드 :  " + code + "</li>"
                    + "<li>에러메세지 : " + mesg + "</li>" + "<li>인스턴스명:  " + insNm
                    + "</li>" + "<li>발생시각:  " + occDate + "</li>"
                    + "<li>에러스택 :<br /> " + errStack + "</li>"
            "</ul></div>";

            var win = new Ext.Window( {
                title : 'Error',
                closable : true,
                width : 600,
                height : 350,
                layout : 'border',
                modal : true,
                items : [{
                    region : 'center',
                    title : '에러메세지',
                    border : false,
                    autoScroll : true,
                    html : markup
                }]
            });
            win.show(this);
        }

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


관련소스다운로드


실제 위의 소스중 loadexception에서 발생하는 resp.status=="200"이 FF에서는 잘 작동하나 IE에서 처리가 안될때는 다음과 같이 해준다.
그리고 옵션중 deferRowRender:false 로 설정하고
resp.status=="200" || resp.status=="12030" 로 처리해준다.
12030 ===> 'The connection with the server has been terminated'뜻으로 internal server error와 같다...
IE에서는 xhr.status 코드값이 틀리게 넘어온다.. ㅡ.,ㅡ;
ext-base.js 를 수정해야 하는 경우도 생길듯..

(참조URL : http://support.microsoft.com/default.aspx?scid=kb;EN-US;193625 )


   
Code Error Message and Description
----- -----------------------------
12001 ERROR_INTERNET_OUT_OF_HANDLES
No more handles could be generated at this time.

12002 ERROR_INTERNET_TIMEOUT
The request has timed out.

12003 ERROR_INTERNET_EXTENDED_ERROR
An extended error was returned from the server. This is
typically a string or buffer containing a verbose error
message. Call InternetGetLastResponseInfo to retrieve the
error text.

12004 ERROR_INTERNET_INTERNAL_ERROR
An internal error has occurred.

12005 ERROR_INTERNET_INVALID_URL
The URL is invalid.

12006 ERROR_INTERNET_UNRECOGNIZED_SCHEME
The URL scheme could not be recognized or is not supported.

12007 ERROR_INTERNET_NAME_NOT_RESOLVED
The server name could not be resolved.

12008 ERROR_INTERNET_PROTOCOL_NOT_FOUND
The requested protocol could not be located.

12009 ERROR_INTERNET_INVALID_OPTION
A request to InternetQueryOption or InternetSetOption
specified an invalid option value.

12010 ERROR_INTERNET_BAD_OPTION_LENGTH
The length of an option supplied to InternetQueryOption or
InternetSetOption is incorrect for the type of option
specified.

12011 ERROR_INTERNET_OPTION_NOT_SETTABLE
The request option cannot be set, only queried.

12012 ERROR_INTERNET_SHUTDOWN
The Win32 Internet function support is being shut down or
unloaded.

12013 ERROR_INTERNET_INCORRECT_USER_NAME
The request to connect and log on to an FTP server could
not be completed because the supplied user name is
incorrect.

12014 ERROR_INTERNET_INCORRECT_PASSWORD
The request to connect and log on to an FTP server could
not be completed because the supplied password is
incorrect.

12015 ERROR_INTERNET_LOGIN_FAILURE
The request to connect to and log on to an FTP server
failed.

12016 ERROR_INTERNET_INVALID_OPERATION
The requested operation is invalid.

12017 ERROR_INTERNET_OPERATION_CANCELLED
The operation was canceled, usually because the handle on
which the request was operating was closed before the
operation completed.

12018 ERROR_INTERNET_INCORRECT_HANDLE_TYPE
The type of handle supplied is incorrect for this
operation.

12019 ERROR_INTERNET_INCORRECT_HANDLE_STATE
The requested operation cannot be carried out because the
handle supplied is not in the correct state.

12020 ERROR_INTERNET_NOT_PROXY_REQUEST
The request cannot be made via a proxy.

12021 ERROR_INTERNET_REGISTRY_VALUE_NOT_FOUND
A required registry value could not be located.

12022 ERROR_INTERNET_BAD_REGISTRY_PARAMETER
A required registry value was located but is an incorrect
type or has an invalid value.

12023 ERROR_INTERNET_NO_DIRECT_ACCESS
Direct network access cannot be made at this time.

12024 ERROR_INTERNET_NO_CONTEXT
An asynchronous request could not be made because a zero
context value was supplied.

12025 ERROR_INTERNET_NO_CALLBACK
An asynchronous request could not be made because a
callback function has not been set.

12026 ERROR_INTERNET_REQUEST_PENDING
The required operation could not be completed because one
or more requests are pending.

12027 ERROR_INTERNET_INCORRECT_FORMAT
The format of the request is invalid.

12028 ERROR_INTERNET_ITEM_NOT_FOUND
The requested item could not be located.

12029 ERROR_INTERNET_CANNOT_CONNECT
The attempt to connect to the server failed.

12030 ERROR_INTERNET_CONNECTION_ABORTED
The connection with the server has been terminated.

12031 ERROR_INTERNET_CONNECTION_RESET
The connection with the server has been reset.

12032 ERROR_INTERNET_FORCE_RETRY
Calls for the Win32 Internet function to redo the request.

12033 ERROR_INTERNET_INVALID_PROXY_REQUEST
The request to the proxy was invalid.

12036 ERROR_INTERNET_HANDLE_EXISTS
The request failed because the handle already exists.

12037 ERROR_INTERNET_SEC_CERT_DATE_INVALID
SSL certificate date that was received from the server is
bad. The certificate is expired.

12038 ERROR_INTERNET_SEC_CERT_CN_INVALID
SSL certificate common name (host name field) is incorrect.
For example, if you entered www.server.com and the common
name on the certificate says www.different.com.

12039 ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR
The application is moving from a non-SSL to an SSL
connection because of a redirect.

12040 ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR
The application is moving from an SSL to an non-SSL
connection because of a redirect.

12041 ERROR_INTERNET_MIXED_SECURITY
Indicates that the content is not entirely secure. Some of
the content being viewed may have come from unsecured
servers.

12042 ERROR_INTERNET_CHG_POST_IS_NON_SECURE
The application is posting and attempting to change
multiple lines of text on a server that is not secure.

12043 ERROR_INTERNET_POST_IS_NON_SECURE
The application is posting data to a server that is not
secure.

12110 ERROR_FTP_TRANSFER_IN_PROGRESS
The requested operation cannot be made on the FTP session
handle because an operation is already in progress.

12111 ERROR_FTP_DROPPED
The FTP operation was not completed because the session was
aborted.

12130 ERROR_GOPHER_PROTOCOL_ERROR
An error was detected while parsing data returned from the
gopher server.

12131 ERROR_GOPHER_NOT_FILE
The request must be made for a file locator.

12132 ERROR_GOPHER_DATA_ERROR
An error was detected while receiving data from the gopher
server.

12133 ERROR_GOPHER_END_OF_DATA
The end of the data has been reached.

12134 ERROR_GOPHER_INVALID_LOCATOR
The supplied locator is not valid.

12135 ERROR_GOPHER_INCORRECT_LOCATOR_TYPE
The type of the locator is not correct for this operation.

12136 ERROR_GOPHER_NOT_GOPHER_PLUS
The requested operation can only be made against a Gopher+
server or with a locator that specifies a Gopher+
operation.

12137 ERROR_GOPHER_ATTRIBUTE_NOT_FOUND
The requested attribute could not be located.

12138 ERROR_GOPHER_UNKNOWN_LOCATOR
The locator type is unknown.

12150 ERROR_HTTP_HEADER_NOT_FOUND
The requested header could not be located.

12151 ERROR_HTTP_DOWNLEVEL_SERVER
The server did not return any headers.

12152 ERROR_HTTP_INVALID_SERVER_RESPONSE
The server response could not be parsed.

12153 ERROR_HTTP_INVALID_HEADER
The supplied header is invalid.

12154 ERROR_HTTP_INVALID_QUERY_REQUEST
The request made to HttpQueryInfo is invalid.

12155 ERROR_HTTP_HEADER_ALREADY_EXISTS
The header could not be added because it already exists.

12156 ERROR_HTTP_REDIRECT_FAILED
The redirection failed because either the scheme changed
(for example, HTTP to FTP) or all attempts made to redirect

failed (default is five attempts).


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

ExtJS에서는(비단, Extjs뿐만 아니라 기존 XHR과 마찬가지로)  같은 도메인일 경우는 HttpProxy를 사용하면 아무런 문제가 되지 않으나 서로 다른 도메인일 경우 ScriptTagProxy로 해당 URL의 Ajax통신을 할수 있게 잘(?) 만들어져 있다.기존 XHR(XMLHttpRequest)를 이용할 시에는 서버쪽에서 Proxy script를 만들어 사용해야 하나 jQuery나 ExtJS의 경우는 제공하는 Proxy를 사용하면 된다.

ScriptTagProxy로 가져온 Datastore파일을 JsonReader로 읽을 때에는 기존의 HttpProxy를 사용할때와 달리 다음과 같이 서버측에 callback 파라미터값을 던지고 아래와 같은 구조로된 Json을 받게 된다.

stcCallback1013(
{
Json형식
}
)

 ExtJS에서  ScriptTagProxy는 아래와 같은 로직으로 구현되어 있다.

boolean scriptTag = false;
String cb = request.getParameter("callback");
if (cb != null) {
scriptTag = true;
response.setContentType("text/javascript");
} else {
response.setContentType("application/x-json");
}
Writer out = response.getWriter();
if (scriptTag) {
out.write(cb + "(");
}
out.print(dataBlock.toJsonString());
if (scriptTag) {
out.write(");");
}


scriptTagProxy 와 Form Submit은  callback 이라는 파라미터 값을 함께 던지므로 서버상에서 callback 파라미터를 받게 만들어 줘야 한다. 그렇게 때문에 서버에서 별도의 작업을 해줘야 하는데 PHP와 JSP의 경우를 비교해서 기술한다.

 

String callback = request.getParameter("callback");
String jsonData = getJsonData();
if (callback != null) {
out.write(callback + "(");
}
out.write(jsonData);
if (callback != null) {
out.write(");");
}
 
$callBack = isset($_REQUEST["callback"]) ? $_REQUEST["callback"] : '';

if($callBack) {
echo $callBack . "(";
}
echo getJsonData();
if($callBack) {
echo ");";
}

Post by 넥스트리소프트 데꾸벅(techbug)
, |
스크롤바에 자바스크립트로 스킨을 입힌 사이트를 발견하다.
CSS로 색깔만 바꾸는것만 가능한줄 알았던 것이 이미지를 입힐수 있게 해주는 스크립트이다

사용자 삽입 이미지



















관련소스(flexcrolljs.zip)를 첨부하다.


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

자바스크립트로 구현한 3D 포토갤러리  (0) 2008.03.06
JsonSQL: JSON parser, SQL style  (0) 2008.03.06
Native CSS selectors with querySelector  (0) 2008.03.06
IE minimize  (2) 2008.03.06
Javascript Performance Stack  (2) 2008.03.03
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Basic Concept

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

 

Region Concept














north
   west          east
   center  
     
     south






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

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

최종 소스 :  다운로드

 

 

Step 1 : 기본 틀 잡기

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

basicLayout.html

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

basicLayout.js

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

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

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

 

 

 

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

basicLayout.js

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

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

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

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



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

basicLayout.js

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

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



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

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

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


 

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

basicLayout.js

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

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

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

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


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

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

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

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





Post by 넥스트리소프트 데꾸벅(techbug)
, |
이 포스팅은 넥스트리에서 내부프로젝트를 진행할때 표준문서로 삼았던 자료입니다.
현재 내용이 표준이 아니므로 참조하실때 유의하시기 바랍니다.

기본 문서구조 만들기

아래 내용은 [1원팁]윈도우 로딩시 스크립트 실행에 잠시 소개했던 내용이다.

방법 1 : 클래스 상속 및 복잡한 페이지에 용이하다.

BasicInfo_Main = function(){
   return {
      init: {
          내용~
      }
   }

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

ext에서 클래스 상속은 다음과 같이 정의한다.
Ext.MyClass = function() { Ext.MyClass.superclass.constructor.call(this); ..... }; Ext.extend(Ext.validProd, Ext.util.Observable, { functionName:function (v) { ............... return v; } }); var valid = new Ext.MyClass();

방법 2 : 간단한 페이지에 사용하기

Ext.onReady(function(){
내용~
}

현재 BCF(factoryware:가칭) Project에서는 방법1을 사용하기로 한다.

 

문서 구조틀

기본문서구조

//메인 클래스에서 사용된 서브 클래스를 정의한다.
왼쪽판넬클래스 = function(){
this.변수;

this.public메쏘드 = functon(){
내용~~
};

//==== 서브클래스 옵션을 상속받은 Ext.panel 옵션을 호출한다.
왼쪽판넬클래스.superclass.constructor.call(this, {
왼쪽판넬클래스의 config 옵션모음~
});

왼쪽판넬클래스 이벤트 걸기~

};

//서브클래스의 속성을 Ext.panel 을 상속받아 사용한다.
Ext.extend(왼쪽판넬클래스, Ext.panel, {
왼쪽판넬 클래스의_private메쏘드_01(){
내용~
}
});


//기본 클래스를 정의한다. 메인 클래스
기본클래스 = function(){
var 로컬변수1;
var 로컬변수2;
return{
//기본클래스가 호출될때 실행되는 첫번째 메쏘드
init:function(){
this.변수,this.변수;

로컬변수1 = new 왼쪽클래스(); //왼쪽 클래스 호출

기본레이아웃 그리기 viewport설정;

(function(){ 페이지 로딩시 사용될 메쏘드 호출 })();
객체.on('이벤트',페이지로딩시 이벤트걸 메쏘드호출);

private메쏘드_01(); //메쏘드 실행

},


private메쏘드_01 : function(){
내용~
},

private메쏘드_02 : function(){
function 내장메쏘드(){
내장메쏘드내용
}
}

};
}();
Ext.EventManager.onDocumentReady(기본클래스.init, 기본클래스,true);
//화면이 로딩될때 기본클래스.init 메쏘드를 먼저 호출한다.

 

 현재 BCF(factoryware)의 기본구조의 위와 같이 사용될 Class와 메인클래스를 별도의 파일로 관리하여 해당 클래스를 호출한다.
 메인클래스를 제외환 대부분의 클래스(위에서는 "왼쪽클래스")에서는 Private메쏘드를 사용하려면 확장클래스 (왼쪽클래스.superclass.constructor.call(this,{});)를 사용하여야 하며,
 확장을 위해서 Ext.extend(왼쪽클래스,Ext.Panel)을 이용하여 "왼쪽클래스" Ext.Panel을 상속받는것임을 알수 있다.

 


Post by 넥스트리소프트 데꾸벅(techbug)
, |
이 포스팅은 넥스트리에서 내부프로젝트를 진행할때 표준문서로 삼았던 자료입니다.
현재 내용이 표준이 아니므로 참조하실때 유의하시기 바랍니ㅏㄷ.

1. HTML 구조


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Component Basic Information</title>
<link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css" />        <!-- extjs stylesheet    -->
<link rel="stylesheet" type="text/css" href="../common/style/css-all.css" />            <!-- bcf stylesheet      -->
<link rel="stylesheet" type="text/css" href="../common/style/component.css" />            <!-- bcf stylesheet      -->
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>            <!-- extjs script        -->
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script>                <!-- extjs script        -->
<script type="text/javascript" src="../extjs/adapter/ext/localXHR.js"></script>            <!-- extjs Extended      -->
<script type="text/javascript" src="../extjs/adapter/ext/RowExpander.js"></script>        <!-- extjs Extended      -->
<script type="text/javascript" src="../scripts/global.js"></script>                        <!-- bcf global variable -->
<script type="text/javascript" src="../scripts/requestUrl.js"></script>                    <!-- Url Handling        -->
<script type="text/javascript" src="./scripts/versionRegisterWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/versionModifyWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/artifactRegisterWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/componentModifyWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/artifactListGrid.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/artifactModifyWin.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/basicInfoTopPanel.js"></script>            <!-- current page script -->
<script type="text/javascript" src="./scripts/basicInfo.js"></script>                    <!-- current page script -->
</head>
<body>
<!-- Real Body Container Start -->
<div id="div_basicInformation_Container">

기본 내용이 들어간다.

</div>
</body>
</html>


 

HTML 작성규칙 :

  1. 반드시 웹표준에 맞게 구조화 되게 작성한다.
  2. DOCTYPE을 반드시 명기할것. (XHTML1.0 Trasitional DTD)
  3. 모든 HTML Element(TAG)들은 소문자로 표현한다. (XHTML1.0 Spec)
  4. HTML Element들의 properties와 attribute들은 최소화한다.
  5. ExtJS의 스타일은 <link /> 태그로 작성한다.
    예외 : factoryware(가칭)의 스타일은 @import를 이용한 include방식으로 작성한다 ( 일괄적인 수정용이)
  6. <script></script>태그의 type은 반드시 작성해 주며 위와 같은 순서대로 작성한다.
    ExtJS Framework JS > ExtJS Extension Module > Global Variables > 현재페이지에 필요한 Javascript resources

 

2. StyleSheet의 구조


@import url("layout.css");    /* Page Layout */
@import url("openpage.css"); /* OpenPage Layout */
/* Elements */
@import url("font.css"); /* Font */
@import url("form.css"); /* Form */
@import url("link.css"); /* Link */
@import url("table.css"); /* table */


 body {
    padding:0;
    margin:0;
    width:100%;
    height:100%;
}


DIV#_DIV_SEARCH_RESULT_ {
    font-family: segoe ui,tahoma,arial;
    font-size:12px;
    width:100%;
    height:100%;
    overflow:auto;
}

DIV#_DIV_SEARCH_RESULT_ H3{
    border:#DDD dotted 1px;
    padding:5px;
    margin:5px;
    height:200px;
    text-align:center;
    font-weight:normal;
    font-family:segoe ui,Dotumn,tahoma

}

 


 

 

 

CSS 작성규칙 :

  1. 기본적으로 ExtJS의 모든 화면 구성은 <div></div> <table></table>을 사용하여 작성되며 모두 x-prefix로 시작된다.

  2. inline Style은 최대한 자제한다.
    예외사항 : ExtJS의 component들은 대부분 position:relative로 작성되기 때문에 IE6, IE7 Bug에 의한 부분은 배제된다.
    또한 Javascript로 ExtJS의 모듈로 들어가는 부분은 제외된다.

  3. ExtJS의 스타일을 제외한 부분은 되도록이면 @import를 이용하여 작성한다.
    (예외 : eclipse css validator에서는 @import를 invalid로 인식하여 예외적으로 사용, 그러나 사용상, 웹표준에 어긋나는 표현은 아님!)

  4. Block Label Element와 Inline Lable Element는 적절히 조화롭게 사용하되 ExtJS의 특징상 Block Lable 요소중에서 부득이하게 네비게이션을 Table로 사용하는 경우가 발생하므로 이경우는 예외로 처리한다.

  5. factoryware는 IE6이상, Firefox2.0 을 기본 브라우저로 하며 IE7.0 , Firefox3.0, Opera9.0 호환모드로 작성한다.
    IE6 Hack, IE7 Hack 작성시 ExtJS의 기본 스타일을 반드시 지켜야 한다.

  6. selector를 사용하려면 Id 값은 해당 화면에서 반드시 하나만 존재해야 하며 같은 스타일을 유지하려면 되도록 class selector를 사용한다.
    (사용예 : <div id="_DIV_SEARCH_RESULT_" class="link_color  x-btn-test"></div> )

  7. Id selector를 사용할 경우 반드시 HTML element를 붙여준다. (예외적으로 전체적으로 적용할때는 제외한다.)
    (사용예 : DIV#_DIV_SEARCH_RESULT_ {내용} )

  8. class selector는 쪼개어 multiple로 작성한다.
    (사용예 : <div class="first_class  second_class third_class"></div> )

  9. 명명규칙 :
    - camel 표기법이 아닌 _ (under-line)을 이용한 명명법을 쓴다. (Javascript에서 사용시 한눈에 보기 쉽게 작성
    - Id 값은 되도록 대문자로 작성한다.
    - class값은 소문자로 작성한다.


 

 

 

Javascript 명명규칙
Edit section

주석처리


XHTML validaion처리를 위하여 주석은 아래와 같이 작성한다.

 <style type="text/css">
/*<![CDATA[*/

/*]]>*/
</style>

<script type="text/javascript">
//<![CDATA[

//]]>
</script>

 

 


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

Extjs Sample :: Viewport를 이용한 기본레이아웃잡기  (3) 2008.02.18
Extjs Sample :: 기본문서구조만들기  (0) 2008.02.18
Extjs Qtips 사용하기  (0) 2008.02.18
Extjs Template 사용하기  (0) 2008.02.18
Extjs 단축키 지정  (0) 2008.02.18
Post by 넥스트리소프트 데꾸벅(techbug)
, |

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

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

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

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


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

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

 MARKUP

<div id="아이디" ext:qtitle="툴팀 타이틀" ext:qtip="내용HTML">

 JAVAScript (ExtJS)

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

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

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

Extjs Sample :: 기본문서구조만들기  (0) 2008.02.18
Extjs Sample :: 기본문서구조  (0) 2008.02.18
Extjs Template 사용하기  (0) 2008.02.18
Extjs 단축키 지정  (0) 2008.02.18
Extjs 기본 DomQuery  (0) 2008.02.18
Post by 넥스트리소프트 데꾸벅(techbug)
, |

머리글
Edit section

Ext UI 를 다루다 보면 중간 중간 객체에 문자열을 입력을 할 때 {0}, {id} 와 같은 형태로 중간에 들어가 있는 것을 볼 수 있다.
이런 것은 대부분 내부적으로 Ext.Template 클래스를 사용한다. 그래서 실제 템플릿은 두고 변하는 부분만 변수화 시켜서 대입하는 방식으로 많은 문자열이 쓰일 때 편하게 작업 할 수 있다.
이번에는 Ext.Template 클래스에 대해서 알아보자..


기본적인 Template 사용 방법

var str = "<html><head>{head}</head><body>{body}</body></html>";
var tp = new Ext.Template(str);

위와 같은 형태로 template 객 체를 생성한다.
template에 적용될 변수는 {변수} 와 같은 형태로 입력된다.
나중에 다른 문자열로 대체될 부분이다.

속도를 좀 더 빠르게 하고 싶다면

tp.compile();  


해서 미리 컴파일 해두면 파싱 작업 없이 바로 바로 텍스트를 수정 할 수 있다.

Element에 템플릿 텍스트 넣기

그렇다면 이제 실제 element에 template로 작성한 문자열을 넣어보도록 하자 .

var tp = new Ext.Template(
     "<div id='{div_id}'>",
         "<span id='{span_id}' class='{cls}'>{content}</span>",
     "</div>"
);
tp.append('아이디', {div_id : 'top', span_id : 'content',
cls : 'icon', content : '안녕'});



위와 같은 코드를 실행하면

<div id='top'><span id='content' class='icon'>안녕</span></div>


과 같은 형태로 문자열이 만들어 지고 이것은 '아이디' element의 내용에 추가되어진다.
추가되어질때는 html 형태 그대로 추가된다.



Template 테스트 바로 얻어오기

    var tp = new Ext.Template('텍스트{name}');
    var str = tp.applyTempate({name:'홍길동'});

변환된 값을 바로 문자열로 리턴해줍니다.





Template 텍스트의 몇가지 속성들

숫자 형태로 위치 지정하기

위의 예제들은 단순히 {id} 와 같은 형태로 문자열 형태로  변수를 지정하였습니다.
하지만 {0}, {1} 과 같은 숫자 형태로 변환할 수 있습니다.

var str = "안녕하세요, 전 {0} 입니다. 그동안 안녕하셨죠? {1}씨? ";
var tp = new Ext.Template(str);
tp.append('아이디', { name1: '꺽정', name2 : '길동' });


옵션에 대해서 0, 1 과 같은 순서 형태로도 지원을 할 수 있습니다.

변수 변환할 때 함수 사용하기

var str = "안녕하세요, 전 {name1:stripTags} 입니다. 그동안 안녕하셨죠? {name2:trim}씨? ";
var tp = new Ext.Template(str);
tp.append('아이디', { name1: '꺽정', name2 : '길동' });   

변수명 뒤에 실행할 함수들이 : 를 구분으로 지정이 됩니다.
그러면 각각의 변수에 해당하는 값은 함수에 적용이 되어진 값으로 변환이 되서 실제 텍스트로 바뀝니다.  여기서 사용되는 함수들은 모두  Ext.util.Format 에 있는 함수들입니다.

       {변수명[:함수명(매개변수,...)]}


 위의 형식과 같은 형태로 지정 해주시면 됩니다. 
Ext.util.Format 을 보시면 각각의 메소드들이 처음 매개변수로 string을 포함하고 있는데요 이것은 템플릿으로 변환되는 텍스트 이기 때문에 실제로 템플릿 상에서 함수를 사용 할 때는 두번째 매개변수 부터 적어주시면 됩니다. 만약 두번째 매개 변수가 없다면 적지 않으셔도 됩니다.

 
ex){name1:ellipsis(2)}
- Ext.util.Format.ellipsis(String value, Number length) ...
{name1:trim}
- Ext.util.Format.trim(Strint value)  ....






Template로 변환된 텍스트 넣는 위치 지정하기

현재까지 알아 본 것은 append  메소드로만 넣는 것을 봤습니다.

 

append

:지정된 element 안의 내용에서 가장 뒤에 추가합니다. 

ex)  <div id='test'>aaa</div>
tp.append('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이 aaa 뒤에 들어가게 됩니다.

 

insertAfter

: 지정된 element 의 뒤에 추가합니다.

ex)  <div id='test'>aaa</div>
tp.insertAfter('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이 <div id='test'>aaa</div>~ 뒤에 들어갑니다.

 

insertBefore

: 지정된 element 의 뒤에 추가합니다.

ex)  <div id='test'>aaa</div>
tp.insertBefore('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이 ~ <div id='test'>aaa</div> 앞에 들어갑니다.

 

insertFirst

: 지정된 element의 하위 노드들 중에 첫번째 요소로 들어갑니다.

ex)
<div id='test'>
<span id='a'></span>
<span id='b'></span>
<span id='c'></span>
</div>
tp.insertFirst('test', ...) 

와 같은 형태로 하면 tp에 있는 내용들이

<div id='test'>
~ // 여기에 들어갑니다.
<span id='a'></span>
<span id='b'></span>
<span id='c'></span>
</div>

 

overwrite
Edit section

: 지정된 element의 내용을 전부 교체합니다.

ex)
<div id='test'>aaa</div>
tp.overwrite('test', ...) 
와 같은 형태로 하면 tp에 있는 내용들이 <div id='test'>~</div> 안에 있는 내용이 모두 바뀝니다.

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

Extjs Sample :: 기본문서구조  (0) 2008.02.18
Extjs Qtips 사용하기  (0) 2008.02.18
Extjs 단축키 지정  (0) 2008.02.18
Extjs 기본 DomQuery  (0) 2008.02.18
Extjs 초보자를 위한 애플리케이션 레이아웃(2.02)  (8) 2008.02.17
Post by 넥스트리소프트 데꾸벅(techbug)
, |

Extjs 단축키 지정

Scripter/EXTJS / 2008. 2. 18. 19:21
기본적인 DOMQuery에 대해 알아봤으므로 쿼리해온 DOM에 단축키를 지정해 봅니다!

 

KeyMap이라는 클래스를 사용해서 각각의 요소에 단축키를 적용 할 수 있습니다.

1. 사용방법
Edit section

var map = new Ext.KeyMap('오브젝트아이디', {
      key : 13,   // 13은 Enter 키입니다....
      fn : handler,   // 이벤트가 적용되었을 때 실행될 함수
      scope : obj    // 여긴 적용될 객체 영역입니다.
});



위와 같은 형태로 객체를 생성하면  '아이디' 를 가지고 있는 element에서 Enter키(13번) 이 눌러지면 handler를 실행합니다..



2. key 이벤트 속성 지정하기
Edit section


위에서 사용 되었던 key 속성과 몇가지 키이벤트에 관련된 속성을 더 알아보겠습니다.

   
key : 적용되어질 키 문자열 입니다.
           ex),   특수키로 적용 할 경우    : [13, 10]      - Enter키와 Esc 키 둘 중 하나입니다.
                    문자로 적용 할 경우       :  "abc"        - a, b, c 키 중에 하나 입니다.
    ctrl  : control 키 적용 여부 입니다.
             true이면 control 키를 누른 상태이고 false이면 누르지 않은 상태입니다.
    alt  :  alter 키 적용 여부입니다. 사용은 ctrl과 같습니다.
    shift : shift 키 적용 여부입니다. 사용은 ctrl과 같습니다.




3. 여러개의 단축키 동시 지정
Edit section

var map = new Ext.KeyMap('아이디', []);
위와 같은 형태로 1에서 했던 이벤트 속성들을 배열로 넣어 줍니다.

var map = new Ext.KeyMap('아이디', [
    {
         key : 13,
         fn : function () { alert('Enter키를 눌렀습니다.'); },
         scope : this
     },
    {
         key : "abc",
         fn : function () { alert('a, b, c 중 하나의 키를 눌렀네요'); },
         scope : this
     },
    {
         key : "a",
         shift : true,
         alt : true,
         fn : function () {  alert('shift + alt + a 키를 동시에 누르셨습니다. '); } ,
         scope : this
    }
]);



이와 같이 배열 형태로 이벤트를 여러개를 지정하면 '아이디' 객체에 저런 이벤트가 발생할 때마다 함수들을 실행하게 될 것입니다.

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

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

Extjs 기본 DomQuery

Scripter/EXTJS / 2008. 2. 18. 19:18

extjs.com에 번역했던 글을 다시 올린다.
번역을 션찮게 해서 늘 맘에 걸려 언제 다시 가다듬어야 할텐데
원문사이트 : http://extjs.com/learn/Tutorial:DomQuery_v1.1_Basics


DomQuery 기본

DomQuery의 select 함수는 2개의 파라미터값을 갖는다. 첫번째는 선택자의 문자열이고 두번째는 만들어질 요소(query)의 id값이다. 현 튜토리얼에서는 Ext.DomQuery.select()를 Ext.query()메쏘드 축약명으로 사용할것이다.


사용하게될 html:

<html>
<head>
<script type="text/javascript" src="../js/firebug/firebug.js"></script>
</head>
<body>
<script type="text/javascript" src="../ext/ext-base.js"></script>
<script type="text/javascript" src="../ext/ext-core.js"></script>
<div id="bar" class="foo">
I'm a div ==> my id: bar, my class: foo
<span class="bar">I'm a span within the div with a foo class</span>
<a href="http://www.extjs.com" target="_blank">An ExtJs link</a>
</div>
<div id="foo" class="bar">
my id: foo, my class: bar
<p>I'm a P tag within the foo div</p>
<span class="bar">I'm a span within the div with a bar class</span>
<a href="#">An internal link</a>
</div>
</body>
</html>

Part 1: Element selectors

문서안에 모든 span 태그를 가져온다고 가정하자

// 이 쿼리는 문서내의 전체 spane태그를 체크하여 2개의 엘리먼트 배열값을 리턴한다.
Ext.query("span"); // [span.bar, span.bar]
// 이 쿼리는 foo라는 id값을 가진 span태그의 배열값을 리턴한다.
Ext.query("span", "foo"); // [span.bar]

첫번째 파라미터로서 일반적인 문자열값이 어떻게 사용되는지 주목하자

아이디(id) 값으로 가져오기 위해서는 "#"을 붙인다.:

// foo값을 아이디값으로 하는 div 배열값을 리턴한다.
Ext.query("#foo"); // [div#foo.bar]

클래스로 가져오기 위해서는 "."을 붙인다.:

/* 두번째 div 를 반환한다. */
Ext.query(".foo"); // [div#bar.foo]

모든 요소값을 가져오기 위해서는 "*"를 사용해도 된다:

// 현재 문서의 모든 엘리먼트들을 리턴한다..
Ext.query("*"); //
//[html, head, script firebug.js, link,
//body#ext-gen2.ext-gecko, script ext-base.js, script ext-core.js,
//div#bar.foo, span.bar, a www.extjs.com, div#foo.bar, p, span.bar,
//a test.html#]

자식 태그를 가져오기 위해서는 2개의 선택자 사이에 공백을 삽입하면 된다:

// div태그안에 p태크를 가진 엘리먼트를 리턴한다.
Ext.query("div p"); // [p]
// div태그안의 span태그를 가진 2개의 엘리먼트 배열값을 리턴한다.
Ext.query("div span"); // [span.bar, span.bar]


3개 이상의 엘리먼트 선택자를 가진것들은 다음 매뉴얼을 참고하기 바란다. DomQuery docs  :)

Part 2: Attributes selectors

엘리먼트요소의 속성값으로 해당 객체를 가져오는 방법은 아래 같다. 속성은 HTML요소에서 href, id또는 class와 같은 것이다.


// class속성을 가지는 어떤 엘리먼트가 있는지 체크하자.
// 이 쿼리는 5개의 엘리먼트 배열을 리턴한다..
Ext.query("*[class]"); // [body#ext-gen2.ext-gecko, div#bar.foo, span.bar,
//div#foo.bar, span.bar]

특정 클래스 속성을 가진것들을 검색해 보자.

// 클래스값이 "bar"와 같은 엘리먼트를 리턴한다.
Ext.query("*[class=bar]"); // [span.bar, div#foo.bar, span.bar]
 
// 클래스값이 "bar"와 같지 않은 모든 엘리먼트를 리턴한다.
Ext.query("*[class!=bar]"); // [html, head, script firebug.js, link,
//body#ext-gen2.ext-gecko, script ext-base.js, script ext-core.js,
//div#bar.foo, a www.extjs.com, p, a test.html#]

 
// 클래스값이 "b"로 시작되는 엘리먼트를 리턴한다.
Ext.query("*[class^=b]"); // [span.bar, div#foo.bar, span.bar]
 
// 클래스 값이 "r"로 끝나는 엘리먼트를 리턴한다.
Ext.query("*[class$=r]"); // [span.bar, div#foo.bar, span.bar]
 
// this will give all elements that has a class with the "a" substring
Ext.query("*[class*=a]"); // [span.bar, div#foo.bar, span.bar]

Part 3: CSS Value selectors

이 선택자는 DOM 엘리먼트의 style속성과 같이 사용된다.

Let's add some color into that html:

<html>
<head>
<script type="text/javascript" src="../js/firebug/firebug.js"></script>
</head>
<body>
<script type="text/javascript" src="../ext/ext-base.js"></script>
<script type="text/javascript" src="../ext/ext-core.js"></script>
<div id="bar" class="foo" style="color:red;">
I'm a div ==> my id: bar, my class: foo
<span class="bar">I'
m a span within the div with a foo class</span>
<a href="http://www.extjs.com" target="_blank" style="color:yellow;">
An ExtJs link with a blank target!</a>
</div>
<div id="foo" class="bar" style="color:fushia;">
my id: foo, my class: bar
<p>I'm a P tag within the foo div</p>
<span class="bar">I'
m a span within the div with a bar class</span>
<a href="#" style="color:green;">An internal link</a>
</div>
</body>
</html>

칼라를 만드는 CSS값을 쿼리해볼것이다. 이것이 전부가 아니다. 구조는 다음과 같다:

element{attribute operator value}


// get all red text element
Ext.query("*{color=red}"); // [div#bar.foo]
 
// get all pink colored element that is a child of a red colored element
Ext.query("*{color=red} *{color=pink}"); // [span.bar]
 
// get everything except for the red text element
Ext.query("*{color!=red}"); // [html, head, script firebug.js, link,
//body#ext-gen2.ext-gecko, script ext-base.js, script ext-core.js, span.bar,
//a www.extjs.com, div#foo.bar, p, span.bar, a test.html#]

 
// get all element that has a color property starting with "yel"
Ext.query("*{color^=yel}"); // [a www.extjs.com]
 
// get all element that has a color property ending with "ow"
Ext.query("*{color$=ow}"); // [a www.extjs.com]
 
// get all element that has the "ow" substring
Ext.query("*{color*=ow}"); // [a www.extjs.com, span.bar]

Part 4: 가상클래스 선택자

We are now going to fetch nodes using this improved web page based on what I did earlier. I merely added some styling in there plus an UL element, a TABLE element and a FORM element to make use of every pseudo classes selectors.

<html>
<head>
<script type="text/javascript" src="../js/firebug/firebug.js"></script>
</head>
<body>
<script type="text/javascript" src="../ext/ext-base.js"></script>
<script type="text/javascript" src="../ext/ext-core.js"></script>
<div id="bar" class="foo" style="color:red; border: 2px dotted red;
margin:5px; padding:5px;"
>
I'm a div ==> my id: bar, my class: foo
<span class="bar">I'
m a span within the div with a foo class</span>
<a href="http://www.extjs.com" target="_blank" style="color:yellow;">
An ExtJs link with a blank target!</a>
</div>
<div id="foo" class="bar" style="color:fushia; border: 2px dotted black;
margin:5px; padding:5px;"
>
my id: foo, my class: bar
<p>I'm a P tag within the foo div</p>
<span class="bar">I'
m a span within the div with a bar class</span>
<a href="#" style="color:green;">An internal link</a>
</div>
<div style="border:2px dotted pink; margin:5px; padding:5px;">
<ul>
<li>Some choice #1</li>
<li>Some choice #2</li>
<li>Some choice #3</li>
<li>Some choice #4 with a <a href="#">link</a></li>
</ul>
<table style="border:1px dotted black;">
<tr style="color:pink">
<td>1st row, 1st column</td>
<td>1st row, 2nd column</td>
</tr>
<tr style="color:brown">
<td colspan="2">2nd row, colspanned! </td>
</tr>
<tr>
<td>3rd row, 1st column</td>
<td>3rd row, 2nd column</td>
</tr>
</table>
</div>
<div style="border:2px dotted red; margin:5px; padding:5px;">
<form>
<input id="chked" type="checkbox" checked/><label for="chked">I'm checked
</label>
<br /><br />
<input id="notChked" type="checkbox" /><label for="notChked">not me brotha!
</label>
</form>
</div>
</body>
</html>

off we go:

//span태그의 첫번째 자식노드를 가져온다.
Ext.query("span:first-child"); // [span.bar]
 
//A태그의 마지막 자식노드를 가져온다.
Ext.query("a:last-child") // [a www.extjs.com, a test.html#]
 
//Span태그의 두번째 자식노드를 가져온다.
Ext.query("span:nth-child(2)") // [span.bar]
 
//홀수번째 TR를 가져온다. (아주 유용한듯 ^^)
Ext.query("tr:nth-child(odd)") // [tr, tr]
 
//짝수번째 LI 를 가져온다.
Ext.query("li:nth-child(even)") // [li, li]
 
//자식노드를 가진 A태크만 가져온다.
 
Ext.query("a:only-child") // [a test.html#]
 
//체크된 INPUT 태그만 가져온다.
Ext.query("input:checked") // [input#chked on]
 
//첫번째 TR
Ext.query("tr:first") // [tr]
 
//마지막 INPUT
Ext.query("input:last") // [input#notChked on]
 
//2번째 TD
Ext.query("td:nth(2)") // [td]
 
 
// within이라는 문자열을 포함한 div태크
Ext.query("div:contains(within)") // [div#bar.foo, div#foo.bar]
 
//자식노드로 form을 포함하지 않은 div태크
Ext.query("div:not(form)") [div#bar.foo, div#foo.bar, div]
 
//A태그를 가진 div태크
Ext.query("div:has(a)") // [div#bar.foo, div#foo.bar, div]
 
//다음 TD값을 가져온다.(colspan은 무시)
Ext.query("td:next(td)") // [td, td]
 
//INPUT의 선행되는 label요소를 가져온다.
Ext.query("label:prev(input)") //[label, label]

결론

본튜토리얼은 API문서를 이용하여 만들어졌다. 단순히 실제 웹페이지에 근거한 결과를 보여주길 원했다.

DomQuery문서를 읽어보셨던 분들은 이 장을 건너띄고 다음 DomQuery advanced tutorial 를 읽어보시기 바란다.!

참고적으로 영어가 미천한 관계로 태글은 정중히 사양한다.


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

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

본 튜토리얼은 여러분이 이미 ExtjS(자바스크립트 라이브러리)를 설치 했다고 가정합니다. 우리가 만들 어플리케이션 디렉터리의 하위에 extjs 라는 이름의 디렉터리에 설치되어 있어야 합니다. 만약 Ext JS를 이미 다른 곳에 설치했다면 예제 파일내의 스크립트 태그 속성인 src 속성값을 알맞은 경로로 수정합니다.

우리에게 필요한 것

ExtJS외에 2개의 파일이 더 필요합니다:

  • applayout.html
  • applayout.js


이제 최소한의 내용과 파일만으로 진행해봅시다. 여기에 상세한 설명이 있습니다:

applayout.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css">
    <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="applayout.js"></script>
    <!-- 현지화 스크립트 파일은 여기에 위치시킵니다. -->
    <script type="text/javascript">
        Ext.onReady(myNameSpace.app.init, myNameSpace.app);
    </script>
    <title>Application Layout Tutorial</title>
</head>
<body>
</body>
</html>


첫 번째 두 줄은 문서 타입을 지정하고 있습니다. 어플리케이션은 문서타입을 지정하지 않아도 실행될 수 있지만 이 경우 브라우저는 작성된 어플리케이션에게 브라우저간 호환성을 제공하기 위해 기능이 떨어지는 하위호환 모드로 작동될 것입니다.

본 튜토리얼에서는 전통적인 HTML 4.01을 사용합니다. 왜냐하면 이 문서 타입은 대부분의 메이저 브라우저들이 아주 잘 지원하기 때문입니다. 물론 여러분의 기분이나 필요에 따라 다른 문서 타입을 사용 할 수도 있습니다. 하지만 반드시 항상 문서 타입을 지정해야 한다 라는 사실을 기억해두세요.

html과 head를 지나면 Content-Type을 지정하는 부분이 나옵니다. 아까와 마찬가지로 이 부분이 없어도 어플리케이션은 실행 됩니다만 여기에 있어야 합니다.

이제 우리는 Ext 스타일과 어댑터 그리고 ExtJS 자체를 인클루드 합니다. 여기에 두 가지 버전의 ExtJS가 있습니다:

  • ext-all.js - 사람이 읽기 어렵고, 로딩이 빠르며, 출시용
  • ext-all-debug.js - 사람이 읽기 쉽고, 개발용

우리는 개발을 해야 하니까 디버그 버전을 인클루드 합니다.

계속 내려가면 우리의 어플리케이션인 applayout.js 이 나옵니다. 그 다음에는 어플리케이션이 초기화가 되기 전에 applayout.js와 ExtJS내의 모든 영어 문자를 변환 시켜주는 현지화 스크립트가 나옵니다.

이제 우리는 문서가 모두 로드 되고 나서 초기화(실행) 할 수 있는 이벤트 핸들러를 설치 할 준비가 되었습니다.

Line

Ext.onReady(myNameSpace.app.init, myNameSpace.app);

읽기 : 문서가 모두 로드 되고 나면 myNameSpace.app 스코프(scope) 내에서 myNameSpace.app의 init 메소드를 실행시켜라.

계속해서 타이틀, head의 끝과 body(지금은 비어 있음) 그리고 닫는 태그가 나옵니다.

여기까지가 우리의 가장 작은 html 파일의 전부 입니다.

applayout.js

/**
  * Application Layout
  * by Jozef Sakalos, aka Saki
  * http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
  */
 
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
 
// create namespace
Ext.namespace('myNameSpace');
 
// create application
myNameSpace.app = function() {
    // do NOT access DOM from here; elements don't exist yet
 
    // private variables
 
    // private functions
 
    // public space
    return {
        // public properties, e.g. strings to translate
 
        // public methods
        init: function() {
            alert('Application successfully initialized');
        }
    };
}(); // end of app
 
// end of file

이 파일의 첫 번째 줄은 주석인데 파일의 내용, 저자와 관련 정보를 설명합니다. 뭐, 어플리케이션은 이 한 줄의 주석이 없어도 실행이 됩니다만 다음을 기억해 두세요: 항상 다른 사람을 위해 작성하듯이 당신의 어플리케이션을 작성해라. 몇 달이 지나서 자신의 코드를 다시 보면 이전 규칙에 대한 기억이 나지 않을 것이고 여러분은 자신의 코딩 습관을 바꾸게 될 겁니다.

그리고 공백 이미지의 기본 참조 값이 extjs.com쪽으로 지정된 것을 여러분의 서버 쪽으로 변경합니다. 각각 어플리케이션이 로드 될 때마다 extjs.com에 접근하는 것을 원치 않으시겠죠?

이제 우리들만의 네임스페이스를 만들어 봅시다. 이렇게 하는 이유는 하나의 전역 객체 안에 모든 변수와 메소드를 담아서 변수 명 충돌을 피하고 여러 전역 함수들에 의해 변수가 예측할 수 없는 값으로 변경되는 것을 막기 위해서입니다. 이름은 여러분이 적당히 선정하면 됩니다.

이게 핵심인데, 마지막으로 우리는 리턴 값으로 즉시 실행되는 함수를 제공하는 myNameSpaceapp 프로퍼티를 만들었습니다.


만약 아래의 코드를 실행 시키면

var o = function() {
    return {p1:11, p2:22};
}();

파싱 후에 즉시 실행될 수 있는 익명 함수(이름이 없는 함수)를 만든 겁니다.(함수 선언 뒤에 ()가 붙는 것에 주의 하세요). 이 함수는 객체를 리턴 하며 변수 o에 할당 되어 있습니다. 우리의 어플리케이션은 같은 로직을 사용합니다.

초기화나 private 변수 혹은 함수 선언구문을 함수의 시작부터 return 구문 사이에 넣을 수 있습니다. 하지만 다음을 기억하세요:이 코드는 head의 일부분으로 실행이 되고 이때는 html 엘리먼트들이 준비 안되어 있으므로 접근을 시도하려고 하면 에러가 발생됩니다.

이와는 반대로, init 함수는 단지 익명함수에 의해서 리턴 되는 하나의 메소드 인데 문서가 모두 로드 되었을 때 실행이 되며 모든 DOM 구조를 사용할 수 있습니다.

여기까지 잘 따라오셨습니다. 만약 실수를 하지 않았다면 http://여러분서버.com/applayout/applayout.html에 접근 했을 때 경고 창이 보일 겁니다(다른 말로 표현하면 어플리케이션을 실행시켰을 때).

이제 가장 어려운 부분입니다:이 비어 있는 템플릿 안에 먼가 쓸만한걸 집어 넣기.

Public, Private, Privileged?

우리의 어플리케이션에 생명을 불어 넣어 봅시다. 아래 부분을 applayout.html 파일 내의 body 태그에 추가 시킵니다:

<div id="btn1-ct"></div>

이것은 비어 있는 div 인데 버튼의 컨테이너로 이용 될 겁니다. 그리고 아래의 몇 줄을 applayout.js 파일에 추가 시킵니다:

/**
  * Application Layout
  * by Jozef Sakalos, aka Saki
  * http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
  */
 
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
 
// create namespace
Ext.namespace('myNameSpace');
 
// Just to allow this tutorial to work for 1.1 and 2.
Ext.Ext2 = (Ext.version && (Ext.version.indexOf("2") == 0));
 
// create application
myNameSpace.app = function() {
    // do NOT access DOM from here; elements don't exist yet
 
    // private variables
    var btn1;
    var privVar1 = 11;
 
    // private functions
    var btn1Handler = function(button, event) {
        alert('privVar1=' + privVar1);
        alert('this.btn1Text=' + this.btn1Text);
    };
 
    // public space
    return {
        // public properties, e.g. strings to translate
        btn1Text: 'Button 1',
 
        // public methods
        init: function() {
            if (Ext.version == "2.0") {
                btn1 = new Ext.Button({
                    renderTo: 'btn1-ct',
                    text: this.btn1Text,
                    handler: btn1Handler
                });
            } else {
                btn1 = new Ext.Button('btn1-ct', {
                    text: this.btn1Text,
                    handler: btn1Handler
                });
            }
        }
    };
}(); // end of app
 
// end of file

변수인 btn1privVar1private 인데, 이것이 뜻하는 바는 이들은 어플리케이션의 밖에서는 접근이 안되고 절대 보이지 않는다는 겁니다. priavate 함수인 btn1Handler 도 역시 마찬가지 입니다.

정반대로 btn1Text는 public 변수이며 어플리케이션의 밖에서 보이고 접근이 가능합니다.(잠시 후 시연을 할겁니다.)

함수 initprivileged 이며 이것은 private 와 public 양쪽의 변수 혹은 함수에 접근이 가능합니다. 우리의 예제에서 public 변수인 this.btn1Text 와 private 함수인 btn1Handler 에 접근이 가능합니다. 함수 initpublic 이기도 하며 외부에서 호출 될 수 있습니다.

자 이제 실행시켜 봅시다! 좌측 상단 코너에 버튼이 보이나요? 좋습니다. 클릭해봅시다. privVar1=11 이 적혀 있는 경고 창이 보일 겁니다. 그렇죠? private 함수는 private 변수에 접근 할 수 있습니다.

하지만 두 번째 경고 창은 뭔가가 잘못되어 this.btn1Text=undefined 라고 보일 건데, 이건 우리가 기대하던 게 아니죠. 그 이유는 변수 this가 우리의 어플리케이션이 아니라 버튼의 핸들러 내부를 지정하고 있기 때문입니다. 아래 보이는 것처럼 버튼 설정부분에 scope:this 를 추가합니다:

if (Ext.Ext2) { //Ext.version.indexOf("2.0") == 0 와 같음.
    btn1 = new Ext.Button({
        renderTo: 'btn1-ct',
        text: this.btn1Text,
        handler: btn1Handler,
        scope:this
    });
} else {
    btn1 = new Ext.Button('btn1-ct', {
        text: this.btn1Text,
        handler: btn1Handler,
        scope:this
    });
}


그리고 페이지를 새로 고칩니다. 잘 작동되지요?

public 변수 오버라이드(override)하기

아래의 코드를 applayout.js 의 마지막 부분에 추가 시킵니다:

Ext.apply(myNameSpace.app, {
    btn1Text:'Taste 1'
});
 
// end of file

이 코드들은 무엇을 할까요? 처음으로 우리의 어플리케이션 객체를 생성하고 public 변수인 btn1text의 값을 오버라이드 합니다. 실행시키면 버튼의 글자가 변경되는 것을 볼 수 있습니다.

원본 어플리케이션 코드를 건드리지 않고도 쉽게 다른 언어로 변역 하는 것처럼 이것은 어플리케이션 내의 public 변수에 값을 입력하는 좋은 코딩 습관입니다.

물론 치수나 스타일 등등 모든 설정 가능한 옵션의 설정 값을 변경 할 수 있습니다. 여러분은 어딘가의 색상을 변경하기 위해 수천 라인의 코드를 뒤져 보는 것을 원치 않을 겁니다.

public 함수 오버라이드 하기

아래 보이는 것처럼 우리의 코드를 변경해봅시다:

Ext.apply(myNameSpace.app, {
    btn1Text:'Taste 1',
    init: function() {
        try {
            btn1 = new Ext.Button('btn1-ct', {
                text: this.btn1Text,
                handler: btn1Handler,
                scope: this
            });
        }
        catch(e) {
            alert('Error: "' + e.message + '" at line: ' + e.lineNumber);
        }
    }
});
 
// end of file

우리가 여기서 무엇을 하고 있는 중 일가요? 단지 이전에 있던 동일한 코드에 에러를 처리하기 위한 try/catch 블록만 추가 시켜서 init 함수를 오버라이드 하는 중 입니다. 수정 이전과 동일한 실행 결과를 보일 것으로 기대 되지만 혹시 다를 수도 있을 까요? 실행시켜서 직접 확인해 봅시다.

이런! btn1Handler이 선언되어 있지 않아 에러 상태가 됩니다. 이유는 수정된 함수가 private 변수에 접근 하려고 했기 때문입니다. 이렇게는 실행될 수 가 없습니다. 하지만 아까 보았죠? 원본 initprivileged 라서 private 공간에 접근 할 수 있다는 걸, 하지만 새로운 init는 그렇게 할 수 가 없는데 여기서 안전성에 대한 관점을 다시 볼 수 있습니다:외부의 코드는 private 공간에 접근 할 수 없다. 오버라이드된 privileged들 까지도

계속 진행 중...

제가 생각 중인 유용한 튜토리얼의 일부로 처음 부분을 이렇게 릴리스 하였으며 그 외에도 다른 예제에 대한 계획 중 입니다.

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

Extjs 단축키 지정  (0) 2008.02.18
Extjs 기본 DomQuery  (0) 2008.02.18
Extjs scope에 대한 고찰  (2) 2008.02.17
ExtJS Tutorial  (1) 2008.02.17
ExtJS 시작하기  (8) 2008.02.17
Post by 넥스트리소프트 데꾸벅(techbug)
, |

시작하기 전에
Edit section

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

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

정의
Edit section

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

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

이제 가봅시다
Edit section

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

function fn() {
    alert(11);
}

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

아래와 같이 입력하고:

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

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

fn();

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

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

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

window 객체
Edit section

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

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

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

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

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

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

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

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

아래와 같이 입력합니다:

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

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

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

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

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

o1.fun.call(o2);

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

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

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

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

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

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

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

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

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

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

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

하지만 이렇게 하면:

alert(i);

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

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

더 읽을만한 것들:


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

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

ExtJS Tutorial

Scripter/EXTJS / 2008. 2. 17. 14:52
초보자
Edit section

Ext를 알고있고, 온라인 샘플들을 보았으며, 심지어 API문서를 보기 시작했다면 Ext를 쓰기 위해 근질근질 할것이다. 그런데 가지고 놀 테스트 서버가 없다.

시나리오 2 - 프로
Edit section

이미 API문서 를 파고 있고 지금 당장 사용할 메쏘드를 찾아보는걸 좋아한다. 그러나 하나의 메쏘드로 테스트하기 위해 적당한 리소스를 포함한 테스트 HTML 페이지를 만들기를 좋아한다.

이러한 이유로 이 문서에서 당장 Ext를 사용할수 있는 방법을 제시한다. 먼저 파이어폭스Firefox with Firebug installed를 설치할 필요가 있다. 파이어버그 플러그인을 설치하도록 하낟. 디버깅 툴이 있는 다른 브라우저라도 상관이 없다.

시작하기
Edit section

  • Ext API 문서를 많이 참조하라 . 기회는 언제나 당신곁에 있다.
  • F12키를 눌러 Firebug console을 열어라 (Firebug를 바로 실행했는가?)
  • 만일 파이어버그 콘솔이 '>>>'와 같이 한줄이 되어 있다면 오른쪽 하단의 빨간색 화살표를 클릭하여 멀티라인 버전으로 바꿔라 (자바스크립트 콘솔처럼 쓸수있다.)
  • 첫번째 라인에 아래와 같이 입력하고 Ctrl-Enter로 실행시켜라:
Ext.get(document.body).update('<div id="test"></div>');

위의 코드가 뜨하는 것은 DOM Body에 id가 test인 div 엘리먼트를 생성시킨다. body의 내용이 사라졌다. 그러나 Ext Library가 로드되어 있고 가지고 놀 준비가 되어있다. (HTML페이지에 Extjs 스크립트가 포함되어 있어야 한다. 그렇지 않다면 Ext is not defined 라는 에러를 볼것이다.)

이제 Ext.Panel API를 이용하여 새로운 Panel 객체를 만들어 보자. 아래 코드를 파이어 버그 콘솔에 추가한다:

Ext.get(document.body).update('<div id="test"></div>');
new Ext.Panel({
	renderTo: 'test',
	width: '200px',
	title: 'My Title',
	html: 'My HTML content'
});

Ctrl-Enter로 다시 실행하면(아래 Run을 클릭해도 된다.) 화면에 새로운 패널이 생긴걸 확인할수 있다.

그럼 여기에 Collapsible 을 가능하게 하기 위해 다른 옵션을 추가여 어떤 현상이 벌어지는지 확인하자. 파이어버그에서 아래 라인에 새로운 configuration 옵션을 추가한다.

Ext.get(document.body).update('<div id="test"></div>');
new Ext.Panel({
	renderTo: 'test',
	width: '200px',
	title: 'My Title',
	html: 'My HTML content',
	collapsible: true
});

다시 실행시키면 패널의 오른쪽 부분에 토글아이콘이 나타난것을 확인할수 있다.

매번 Ctrl-Enter를 치면서 옵션들을 추가, 삭제 하면서 작업을 해라. 과도한 configuration 옵션들은 별로 좋지 않다.(원문과는 좀 다른 느낌인데.. ) 첫번째 라인의 update()메쏘드를 이용하여 표현하고 하는 많은 HTML를 추가할수 있다.

자 그럼 아래 API 문서를 이용해 스스로 해보자!

(번역이 매끄럽지 못하고 중간중간 생략된 부분이 있으므로 다음분이 더 자세히 설명해 주실것입니다. - 김동완)

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

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

ExtJS 시작하기

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

ExtJs란

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


라이센스

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


호환되는 서버 플랫폼

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


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

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

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


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

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


에러상황에 따른 대처

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

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

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

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

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


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

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

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

Hidden Field 표시하는법

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


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

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


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

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


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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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



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

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

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



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

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

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

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


끝.


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

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



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

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


일반적인 JavaScript Learning Guide
Edit section

참고사이트
Edit section

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

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

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

 

OO JavaScript 참조사이트

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

DOM

 

 

그외 기술

Yahoo! User Interface Library (YUI)

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

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

JSON 관련

 

 

Platform-Specific Resources

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

PHP
Edit section

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

Java

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

.NET

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

Ruby on Rails

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

IBM Lotus Notes/Domino

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

 

 

통합개발툴(IDE)
Edit section

Eclipse Based IDEs

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

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

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

 

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

AptanaIDE

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

 

 


 

다양한 디버깅툴

Firebug (FF에서 강추)

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


Screencasts


Tutorials


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

HTTP Trafic Viewer

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

MS Script Editor

IE Web Developper

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

Testing Data Generator

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

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

YSlow

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

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

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

DebugBar - IE 플러그인 (강추)

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

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


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

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