블로그 이미지

카테고리

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

최근에 올라온 글

최근에 달린 댓글

'attach panel'에 해당되는 글 1건

  1. 2008.02.25 Extjs 기본레이아웃에 grid panel 붙이기

Basic Concept

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

기본소스 :


<그리드 해부도>

사용자 삽입 이미지









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

basicLayout_AttachedGrid.html

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

 

basicLayout_AttachedGrid.js

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

this.store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'basicGrid.json'
}),
sortInfo: {
field: 'price',
direction: "DESC"
},
reader: new Ext.data.JsonReader({
root: 'testData'
}, [{
name: 'company',
type: 'string',
mapping: 'company'
}, {
name: 'price',
type: 'float',
mapping: 'price'
}, {
name: 'change',
type: 'float',
mapping: 'change'
}, {
name: 'pctChange',
type: 'float',
mapping: 'pctChange'
}, {
name: 'lastChange',
type: 'date',
dateFormat: 'n/j h:ia',
mapping: 'lastChange'
}])
});
// 왼쪽 패널의 config옵션을 정의한다.
LeftArea.superclass.constructor.call(this, {
region: 'west', // 굵게 표시된 부분은 기본레이아웃을 잡는 config옵션이다.
title: 'WEST',
collapsible: true,
collapsed: false,
width: 250,
split: true,
layout: 'fit',
margins: '5 0 5 5',
cmargins: '5 5 5 5',
store: this.store,
columns: [{
id: 'company',
header: "Company",
width: 160,
sortable: true,
dataIndex: 'company'
}, {
header: "Price",
width: 75,
sortable: true,
renderer: 'usMoney',
dataIndex: 'price'
}, {
header: "Change",
width: 75,
sortable: true,
dataIndex: 'change'
}, {
header: "% Change",
width: 75,
sortable: true,
dataIndex: 'pctChange'
}, {
header: "Last Updated",
width: 85,
sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'
}],
stripeRows: true,
autoExpandColumn: 'company',
loadMask: {
msg: '데이타 로드중'
},
sm: new Ext.grid.RowSelectionModel({
singleSelect: true
}),
view: new Ext.grid.GridView({
forceFit: true,
enableRowBody: true,
emptyText: 'No Record found'
})
});
};

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


// 그리드 패널 클래스 끝



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

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

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

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

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

 

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

basicLayout_AttachedGrid.html

<html>
<head>
<title>Basic Layout</title>
<link rel="stylesheet" type="text/css" href="http://techbug.tistory.com/resources/css/ext-all.css" />
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all.js"></script>
//좌측 그리드 패널클래스 별도 파일로 분리, 반드시 main 레이아웃 클래스보다 먼저 호출
<script type="text/javascript" src="basicLayout_LeftGrid.js"></script>
<script type="text/javascript" src="basicLayout_AttachedGrid.js"></script>
</head>
<body id="basicLayoutBody"></body>
</html>

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
    return {
        init: function(){
            Ext.QuickTips.init();
            this.viewport = new Ext.Viewport({
                layout: 'border',
                items: [this.WestPanel = new LeftArea(this),
                //좌측 그리드 클래스 호출
                this.CenterPanel = new Ext.Panel({
                    region: 'center',
                    title: 'CENTER',
                    layout: 'fit',
                    margins: '5 5 5 0',
                    html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
                })]
            });
            this.viewport.doLayout();
            this.viewport.syncSize();
            //좌측 패널의 public method호출
            this.WestPanel.loadGridDataSet();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

basicLayout_LeftGrid.js

LeftArea = function(viewport){

    this.viewport = viewport;

    this.store = new Ext.data.Store({
        proxy: new Ext.data.HttpProxy({
            url: 'basicGrid.json'
        }),
        sortInfo: {
            field: 'price',
            direction: "DESC"
        },
        reader: new Ext.data.JsonReader({
            root: 'testData'
        }, [{
            name: 'company',
            type: 'string',
            mapping: 'company'
        }, {
            name: 'price',
            type: 'float',
            mapping: 'price'
        }, {
            name: 'change',
            type: 'float',
            mapping: 'change'
        }, {
            name: 'pctChange',
            type: 'float',
            mapping: 'pctChange'
        }, {
            name: 'lastChange',
            type: 'date',
            dateFormat: 'n/j h:ia',
            mapping: 'lastChange'
        }])
    });

    LeftArea.superclass.constructor.call(this, {
        region: 'west',
        title: 'WEST',
        collapsible: true,
        collapsed: false,
        width: 250,
        split: true,
        layout: 'fit',
        margins: '5 0 5 5',
        cmargins: '5 5 5 5',
        store: this.store,
        columns: [{
            id: 'company',
            header: "Company",
            width: 160,
            sortable: true,
            dataIndex: 'company'
        }, {
            header: "Price",
            width: 75,
            sortable: true,
            renderer: 'usMoney',
            dataIndex: 'price'
        }, {
            header: "Change",
            width: 75,
            sortable: true,
            dataIndex: 'change'
        }, {
            header: "% Change",
            width: 75,
            sortable: true,
            dataIndex: 'pctChange'
        }, {
            header: "Last Updated",
            width: 85,
            sortable: true,
            renderer: Ext.util.Format.dateRenderer('m/d/Y'),
            dataIndex: 'lastChange'
        }],
        stripeRows: true,
        autoExpandColumn: 'company',
        loadMask: {
            msg: '데이타 로드중'
        },
        sm: new Ext.grid.RowSelectionModel({
            singleSelect: true
        }),
        view: new Ext.grid.GridView({
            forceFit: true,
            enableRowBody: true,
            emptyText: 'No Record found'
        })
    });
};

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


});


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

 

 

 

Step 3. 클래스 리팩토링

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

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

    this.store = new Ext.data.Store({
        proxy: new Ext.data.HttpProxy({
            url: 'basicGrid.json'
        }),
        sortInfo: {
            field: 'price',
            direction: "DESC"
        },
        reader: new Ext.data.JsonReader({
            root: 'testData'
        }, [{
            name: 'company',
            type: 'string',
            mapping: 'company'
        }, {
            name: 'price',
            type: 'float',
            mapping: 'price'
        }, {
            name: 'change',
            type: 'float',
            mapping: 'change'
        }, {
            name: 'pctChange',
            type: 'float',
            mapping: 'pctChange'
        }, {
            name: 'lastChange',
            type: 'date',
            dateFormat: 'n/j h:ia',
            mapping: 'lastChange'
        }])
    });
    // 레이아웃과 관련된 부분을 제외하고 순수 그리드 config option 부분만 사용
    LeftArea.superclass.constructor.call(this, {
        store: this.store,
        columns: [{
            id: 'company',
            header: "Company",
            width: 160,
            sortable: true,
            dataIndex: 'company'
        }, {
            header: "Price",
            width: 75,
            sortable: true,
            renderer: 'usMoney',
            dataIndex: 'price'
        }, {
            header: "Change",
            width: 75,
            sortable: true,
            dataIndex: 'change'
        }, {
            header: "% Change",
            width: 75,
            sortable: true,
            dataIndex: 'pctChange'
        }, {
            header: "Last Updated",
            width: 85,
            sortable: true,
            renderer: Ext.util.Format.dateRenderer('m/d/Y'),
            dataIndex: 'lastChange'
        }],
        stripeRows: true,
        autoExpandColumn: 'company',
        loadMask: {
            msg: '데이타 로드중'
        },
        sm: new Ext.grid.RowSelectionModel({
            singleSelect: true
        }),
        view: new Ext.grid.GridView({
            forceFit: true,
            enableRowBody: true,
            emptyText: 'No Record found'
        })
    });
};

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



 

basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
return {
init: function(){
Ext.QuickTips.init();
//레이아웃과 관련된 내용을 메인레이아웃 클래스 밖으로 빼냄
this.WestPanel = new LeftArea(this, {
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
layout: 'fit',
margins: '5 0 5 5',
cmargins: '5 5 5 5'
}), this.viewport = new Ext.Viewport({
layout: 'border',
items: [this.WestPanel, this.CenterPanel = new Ext.Panel({
region: 'center',
title: 'CENTER',
layout: 'fit',
margins: '5 5 5 0',
html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})]
});
this.viewport.doLayout();
this.viewport.syncSize();
this.WestPanel.loadGridDataSet();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

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


basicLayout_AttachedGrid.js

BasicLayoutClass = function(){
    return {
        init: function(){
            Ext.QuickTips.init();
            
            this.viewport = new Ext.Viewport({
                layout: 'border',
                items: [{
                    region: 'west', // 일반 Ext.panel로 설정하여 왼쪽
                    title: 'WEST',
                    collapsible: true,
                    collapsed: false,
                    width: 650,
                    split: true,
                    margins: '5 0 5 5',
                    cmargins: '5 5 5 5',
                    layout: 'border',
                    items: [ // 일반 Ext.panel에 Nested된 좌측패널
                        this.WestPanel = new LeftArea(this, {
                        region: 'center',
                        layout: 'fit',
                        border: false
                    })]
                }, this.CenterPanel = new Ext.Panel({
                    region: 'center',
                    title: 'CENTER',
                    layout: 'fit',
                    margins: '5 5 5 0',
                    html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
                })]
            });
            this.viewport.doLayout();
            this.viewport.syncSize();
            this.WestPanel.loadGridDataSet();
        }
    }
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

 

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

basicLayout_AttachedGrid.js

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

this.viewport = new Ext.Viewport({
layout: 'border',
items: [{
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
margins: '5 0 5 5',
cmargins: '5 5 5 5',
layout: 'border',
items: [this.WestPanel = new LeftArea(this, {
region: 'center',
layout: 'fit',
border: false,
tbar: [{
text: '새로고침',
scope: this,
handler: function(){
this.WestPanel.store.reload();
}
}]
})]
}, this.CenterPanel = new Ext.Panel({
region: 'center',
title: 'CENTER',
layout: 'fit',
margins: '5 5 5 0',
html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})]
});
this.viewport.doLayout();
this.viewport.syncSize();
this.WestPanel.loadGridDataSet();
}
}
}
();
Ext.EventManager.onDocumentReady(BasicLayoutClass.init, BasicLayoutClass, true);

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

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

 

 

 

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

basicLayout_AttachedGrid.js

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

this.viewport = new Ext.Viewport({
layout: 'border',
items: [{
region: 'west',
title: 'WEST',
collapsible: true,
collapsed: false,
width: 650,
split: true,
margins: '5 0 5 5',
cmargins: '5 5 5 5',
layout: 'border',
items: [this.WestPanel = new LeftArea(this, {
region: 'center',
layout: 'fit',
border: false,
tbar: [{
text: '새로고침',
scope: this,
handler: function(){
this.WestPanel.store.reload();
}
}]
})]
}, this.CenterPanel = new Ext.Panel({
region: 'center',
title: 'CENTER',
layout: 'fit',
margins: '5 5 5 0',
html: '<div id="_CONTENTS_AREA_">컨텐츠 영역입니다.</div>'
})]
});
this.viewport.doLayout();
this.viewport.syncSize();
this.WestPanel.loadGridDataSet();
//Row선택시 발생하는 이벤트를 this.updateCenter() 메쏘드에서 실행
this.WestPanel.getSelectionModel().on('rowselect', this.updateCenter, this);
},

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

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



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

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

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

 

 

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