자바스크립트/Ext JS2013. 2. 6. 23:10

ExtJS 오프라인 강좌 진행합니다.


일시 : 2/23 토요일 오후 7~9시

장소 : 토즈강남타워점


많은 참석 부탁드립니다.


아래에서 참석 신청해주세요

http://onoffmix.com/event/12445




Posted by 베니94
자바스크립트/Ext JS2013. 1. 18. 14:24

강좌 프로젝트 올립니다. 내려받고 실행해보세요.

이제는 강좌 진행과 함께 소스도 함께 올리도록 하겠습니다.



benney.tistory.com.zip


Posted by 베니94
자바스크립트/Ext JS2013. 1. 18. 00:19

오프라인 강의를 하려고 하는데 관심있으신 분들 댓글 달아주시면 감사하겠습니다.


사실 블로그에 올려야할 강좌는 쌓여있습니다. 일에 치이며 먹고살려니 블로그에


글올리는게 쉽지않습니다.


해서 이럴게 아니라 오프라인에서 강의를 해보는 것도 나쁘지 않다는 생각이


듭니다.


장소 : 강남 토즈(아는 곳이 여기밖에 없어서 전 상암인데 멀어도 오시는 분들에 맞출수 있음)

일시 : 미정 단지 시간은 평일저녁7시에서 9시까지(전 주말도 좋긴합니다만..)

내용 : ExtJS가 실무에서 어떻게 사용되는지 소스위주의 시연강의(실습하기에는 시간이 부족할 것 같음)

   MVC모델 중심의 시작부터 배포까지.

인원 : 최소 15명 정도는 되야 하지 않을까요?

비용 : 인당 5만원


댓글로 반응을 보고 해야할지 판단하려고 합니다.


어떠신지요?


Posted by 베니94
자바스크립트/Ext JS2012. 12. 16. 21:18

이전 강좌에서 우리는 좌측 panel안에 davaview를 삽입하여 최초 실행 시 dataview내부 첫번째 data가

선택 되도록 코딩하였습니다.

이번에는 선택 된 데이터뷰가 우측 영역에 어플리케이션으로 나오도록 하고 이 어플리케이션에 그리드와

form을 배치하여 보겠습니다.

우선 컨트롤러가 dataview에서 select한 이벤트를 케치할 수 있도록 아래와 같이 구현합니다.

Ext.define('ria.view.Viewport', {
    extend: 'Ext.container.Viewport',
    layout : 'border',
    items: [
    {
        region: 'center',
        // 기존 panel에서 여러개의 패널을 
        // 붙여야 하므로 tabpanel로 변경합니다.
        xtype: 'tabpanel'
    },{
        region: 'west',
        xtype : 'WestTabPanel',
        width: 200
    }]
});


Ext.define('ria.controller.FrameController', {
.
.
.

   init: function() {
        this.control({
            // step1. 좌측 매뉴에 추가된 패널 즉 시스템 패널에 대한 이벤트 추가.
            'WestMenuPanel > WestMenuDataViewPanel': {
                afterrender: this.firstSelect,
                expand: this.onItemClicked
            }, // select이벤트 잡기.
            'WestMenuDataViewPanel > dataview': {
                select: this.onProgramSelect
            }
         });
    },
    onProgramSelect: function(dataview, record) {
        this.onProgramSelect2(record.get('pgm_class'), record.get('title'));
    },
    /***
     * 최종 클래스명과 탭패널에 추가시 추가할 탭이름을 받는다.
     */
    onProgramSelect2: function(pgm_class, title) {
        var centerpanel =  Ext.ComponentQuery.query('viewport container[region="center"]')[0];
        var tab = centerpanel.down('[tabuniqid=' + pgm_class + title + ']');
        if (!tab) {
            // 아래 클래스를 통해 공통 기능을 구현하고 이 클래스의 item으로 인자로 받아온
            // 클래스를 추가하도록 한다.
            tab = Ext.create('ria.view.pgms.CommonPanel',{
            });
            tab.add(Ext.create(pgm_class,{ region : 'center'}));
            tab.title = title;
            tab.tabuniqid = pgm_class+title;
            centerpanel.add(tab);
        }

        centerpanel.setActiveTab(tab);
    },

위에서 ‘ria.view.pgms.CommonPanel은 모든 프로그램들이 공통적으로 동일한 공통 기능을 갖도록 하기 위해 이 클래스를 부모로 사용하도록 하였다. 이 클래스는 로그아웃, 도움말, 검색 등의 공통적인 기능 가지고 있고 구현해야한다.

Ext.define('ria.view.pgms.CommonPanel',{
    extend : 'Ext.panel.Panel',
    alias : 'widget.CommonPanel',
    layout : 'border',
    initComponent: function() {
        var me = this;

        Ext.apply(this, {
            dockedItems: [{
                xtype: 'toolbar',
                dock: 'top',
                items: [
                    { 
                        xtype: 'button', 
                        text: '도움말',
                        action : 'common.help',
                        listeners : {
                            click : function(a){
                                console.log('도움말 호출', me.uniqid);
                            }
                        }
                    },'-',
                    { 
                        xtype: 'button', 
                        text: 'MyPage',
                        action : 'common.help',
                        listeners : {
                            click : function(a){
                                console.log('도움말 호출', me.uniqid);
                            }
                        }
                    },'-',
                    { 
                        xtype: 'button', 
                        text: '로그아웃',
                        action : 'common.help',
                        listeners : {
                            click : function(a){
                                Ext.Msg.confirm('확인', '로그아웃하시겠습니까?', function(btn) {
                                    if(btn == 'yes') {
                                        console.log('도움말 호출', me.uniqid);
                                        Ext.create('Ext.ux.window.Notification', {
                                            position: 'tr',
                                            useXAxis: true,
                                            cls: 'ux-notification-light',
                                            iconCls: 'ux-notification-icon-information',
                                            closable: false,
                                            title: '확인',
                                            html: '로그아웃되었습니다.',
                                            slideInDuration: 800,
                                            slideBackDuration: 1500,
                                            autoCloseDelay: 4000,
                                            slideInAnimation: 'elasticIn',
                                            slideBackAnimation: 'elasticIn'
                                        }).show();
                                    }
                                });
                            }
                        }
                    },'-',
                    {
                        xtype: 'combo',
                        store: Ext.create('ria.store.system.Programs'),
                        displayField: 'title',
                        typeAhead: false,
                        hideLabel: false,
                        fieldLabel: '검색',
                        labelWidth: 40,
                        hideTrigger:true,
                        width: '400',
                        enableKeyEvents: true,
                        minChars: 2,
                        listConfig: {
                            loadingText: '검색 중입니다....',
                            emptyText: '검색어에 맞는 검색 결과가 없습니다.',

                            // Custom rendering template for each item
                            getInnerTpl: function() {
                                return '< h3 >< span >{[Ext.Date.format(values.input_date, "M j, Y")]}< br />by {input_user_name}< /span >{title}< /h3 >' +
                                    '{excerpt}';
                            }
                        },
                        pageSize: 10,
                        listeners: {
                            select: function(a, b) {
                                console.log(a,b)
                            }

                        }

                    }
                ]
            }]
        });
        me.callParent(arguments);
    }
});
이제 위의 CommonPanel안에 들어갈 즉 선택 되어 보여줘야할 어플리케이션을 구현해 보겠습니다.
위의 코드대로 실행하면 에러가 발생할 것입니다. 우선 아래의 json파일을 보겠습니다.
아래 파일은 dataview에 보여지고 있는 프로그램 리스트입니다. 그 중 첫번째 라인을 아래 소스와 같이 
수정합니다.
ria.view.pgms.project.ProjectMgrPanel은 우리가 구현할 프로그램으로 사내 프로젝트를 심플하게 관리하는
프로그램입니다.
/**
 ** Date : 2012.11.30
 ** Desc : 시스템이하 프로그램 리스트 제공
 ** Posn : /json/programlist.json
 **/
{"entitys":[{"pgm_class":"ria.view.pgms.project.ProjectMgrPanel","pgm_icon":"grid","pgm_nm":"나의 과제","title":"나의 과제"},
            {"pgm_class":"ria.view.pgms.RiaAppMain02","pgm_icon":"grid","pgm_nm":"하위 프로그램2","title":"프로그램2"},
            {"pgm_class":"ria.view.pgms.RiaAppMain03","pgm_icon":"grid","pgm_nm":"하위 프로그램3","title":"커뮤니케이션팀"},
            {"pgm_class":"ria.view.pgms.RiaAppMain04","pgm_icon":"grid","pgm_nm":"하위 프로그램4","title":"커뮤니케이션팀"},
            {"pgm_class":"ria.view.pgms.RiaAppMain05","pgm_icon":"grid","pgm_nm":"하위 프로그램5","title":"커뮤니케이션팀"},
            {"pgm_class":"ria.view.pgms.RiaAppMain06","pgm_icon":"grid","pgm_nm":"하위 프로그램6","title":"커뮤니케이션팀"}
            ],
            "errMsg":"","errTitle":"검색결과","message":"","success":true,"totalCount":"2"}



// ria.view.pgms.project.ProjectMgrPanel 구현 하기.
Ext.define('ria.view.pgms.project.ProjectMgrPanel',{
    extend : 'Ext.panel.Panel',
    alias : 'widget.ProjectMgrPanel',
    layout : 'border',
    initComponent: function() {
        var me = this;
        this.callParent(arguments);
    }
});

이제 실행해보면 아래와 같은 모양의 프로그램이 우측 패널에 보여지게 된다.

공통적으로 도움말, MyPage,로그아웃, 검색 등의 기능을 사용할 수 있는 구조이다.

각각의 기능은 나중에 구현하도록 하고 “나의과제”라는 프로그램을 구현해봅니다.

이 프로그램은 좌측에 그리드패널을 그 그리드의 데이터를 select하면 우측의 폼패널이 열리고

해당 과제의 내용을 보여주도록 구현하겠습니다.

/**
 ** Date : 2012.11.30
 ** Desc : 프로젝트 리스트 제공
 ** Posn : /json/projectlist.json
 **/
{"entitys":[{"prj_master_no":"PJ000001","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"},
            {"prj_master_no":"PJ000002","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"},
            {"prj_master_no":"PJ000003","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"},
            {"prj_master_no":"PJ000004","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"},
            {"prj_master_no":"PJ000005","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"},
            {"prj_master_no":"PJ000006","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"},
            {"prj_master_no":"PJ000007","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"},
            {"prj_master_no":"PJ000008","ytree_sgy_dtl_seq":"001","prj_leader":"쥐박이","prj_name":"1원가절감을 위한 공수산정","prj_bgrnd":"추진배경은 .....","prj_target":"프로젝트목표","prj_effect":"프로젝트효과","prj_std_dt":"2012.01.10","prj_end_dt":"2012.12.10","prj_status_cd":"A","prj_status_nm":"결재중","input_user_name":"홍길동","input_date":"2012.01.29"}
            ],
            "errMsg":"","errTitle":"검색결과","message":"","success":true,"totalCount":"340"}
/**
 * Date : 2012.11.30
 * Desc : 프로젝트 정보를 표현할 모델 클래스
 */
Ext.define('ria.model.ProjectMaster', {
    extend: 'Ext.data.Model',   // extend
    fields: [
        'prj_master_no',
        'ytree_sgy_dtl_seq',
        'prj_leader',
        'prj_name',
        'prj_bgrnd',
        'prj_target',
        'prj_effect',
        'prj_status_cd',
        'prj_status_nm',
        'prj_std_dt',
        'prj_end_dt',
        'input_user_name',
        'input_user_id',
        'input_date'
    ]
});

/**
 * Date : 2012.12.14
 * Desc : 프로젝트 리스트
 */
Ext.define('ria.store.project.Projects', {
    extend: 'Ext.data.Store',   // 당연히 store상속
    autoLoad : false,           // 자동 로드는 꺼놓자.
    model: 'ria.model.ProjectMaster',   // 모델은 동일하게 사용 세팅
    proxy: {
        type: 'ajax',
        url: '/json/project/projectlist.json',  
        reader: {
            type: 'json',
            root: 'entitys',
            totalProperty: 'totalCount',
            messageProperty: 'message'
        },
        listeners: {
            exception: function(proxy, response, operation){
                // 나중에 구현할 부분 모든 ajax 통신에 공통으로 쓸 수 있는
                // 에러 캐치 함수를 만들 것이다.
            }
        }
    }
});

Ext.define('ria.view.pgms.project.ProjectListPanel',{
    extend : 'Ext.grid.Panel',
    alias  : 'widget.ProjectListPanel',
    columnLines: true,
    initComponent: function() {
        var me = this;
        var store = Ext.create('ria.store.project.Projects');
        Ext.apply(this, {
            store: store,
            columns: [
            {
                text: '과제번호',
                width : 80,
                dataIndex: 'prj_master_no'
            },
            {
                text: '제목',
                flex : 1,
                dataIndex: 'prj_name'
            },
            { 
                text: '작성자',
                width: 70,
                sortable: false,
                dataIndex: 'input_user_name'
            },
            { 
                text: '시작일',
                width: 70,
                sortable: false,
                dataIndex: 'prj_std_dt'
            },
            { 
                text: '종료일',
                width: 70,
                sortable: false,
                dataIndex: 'prj_end_dt'
            },
            { 
                text: '작성일',
                width: 70,
                sortable: false,
                dataIndex: 'input_date'
            }],
            dockedItems: [
            {
                xtype: 'pagingtoolbar',
                store: store,
                //name : 'fnc003Main01',
                dock: 'bottom',
                displayInfo: true
            }],
            boardLoad : function(){
                me.store.load({             // 아직 로드전이므로 로드한다.이때 json파일 호출
                    params : {
                        km : 'A00000001'
                    }
                });
                //console.log('me.store:::', me.store)
            }
        });
        me.callParent(arguments);
        this.on('render', this.boardLoad, this);
    }
});

Ext.define('ria.view.pgms.project.ProjectMgrPanel',{
        extend : 'Ext.panel.Panel',
        alias : 'widget.ProjectMgrPanel',
        layout : 'border',
        initComponent: function() {
                var me = this;
                Ext.apply(this, {
                        items : [{
                                xtype : 'ProjectListPanel',
                                title : '과제리스트',
                                region: 'center'
                        }]
                });
                this.callParent(arguments);
        }
});

이제 실행해 봅니다. 우측에 프로젝트 리스트 패널이 실행되고 json파일의 내용이 출력되는 것을
확인 할 수 있습니다.

Posted by 베니94
자바스크립트/Ext JS2012. 12. 12. 12:43

이제 어플리케이션의 틀이 완성되었으니 내용을 채울 차례입니다.

아래 그림은 앞으로 만들어볼 화면입니다.

"프로그램"탭에는 자신이 가지고 있는 권한내에서 필요한 프로그램이 보여지고

그 옆 "지식관리"라는 탭은 모두 공통적으로 사용하는 게시판성 프로그램입니다.

각각이 패널과 그리드, 상속관계, 레이아웃, 버튼, PropertyGrid 등의 요소를 포함하고 

있어 좋은 예제로 생각됩니다.

금주내로 아래 프로그램에 대해 강좌를 진행하도록 하겠습니다.


Posted by 베니94
자바스크립트/Ext JS2012. 12. 7. 13:23

이번에는 지금까지 코드를 정리 하는 시간을 좀 갖도록 하겠습니다. 실제 시스템을 ExtJS로 구축하도보면 

특히나 MVC모델로 구현할 경우 Controller가 비대해지는 현상이 발생합니다.


이는 MVC를 오해해서 생기는 현상이 아닌가 생각되는데요. MVC에서 V는 View를 의미하는데 이러한 이유로 

View안에는 기능적인 코드가 다 빠져있고 그 것들이 모두 Controller안에 들어가 있게 됩니다. 


Controller역할을 로직의 구현으로 이해하는 경우인것 입니다. 결과적으로 Controller안에 모든 관련 View의 기능이

 구현되어 있고 View는 껍데기만 존재하는 것이죠.


이렇게 비대해진 Controller는 유지보수 입장에서 굉장히 길고 파악하기 힘들게 되고 View자제의 재활용성이 

기능을 포함하지 않은 단순 껍데기만 재활용하게 되어 반쪽짜리 클래스로 전락하게 되는 것이죠.


제 생각이지만 이게 아니라 View은 일종의 클래스 (화면을 기준으로 화면제어 등의 기능이 포함된.)로 생각하고

UI와 그에 따른 제어기능도 포함되도록 해야합니다. 그래야만 재활용 측면에서도 해당 View Class를 가져다 쓸때

 기능도 함께 쓸 수 있게됩니다.


 기능이 모두 Controller에 구현될 경우 재활용은 Controller까지 신경써야하니 활용도가 떨어지게 되는 것입니다.


그래서 아래 코드는 View안에서는 자신과 관련된 기능에 대해 구현을 하고 Controller는 View에서 구현 된 

기능을 함수 단위로 호출 만 하도록 하게끔 리팩토링 작업을 한 것이니 참고하고 봐주시기 바랍니다.


아래 코드의 요점은 Controller에 구현된 로직을 각 뷰 클래스로 옮기고 Controller에서는 함수단위로 호출만

 하도록 변경한것입니다.


// app/controller/FrameController.js 를 수정한다.
// views, refs, onLaunch를 확인하자.
Ext.define('ria.controller.FrameController', {
    extend: 'Ext.app.Controller',
    //stores: ['system.Systems'],
    views: ['ria.view.frame.WestMenuPanel',
            'ria.view.frame.WestMenuDataViewPanel'], // 꼭 명시되어야함, 명시하지 않을 경우 require를 이용해야합.
    refs: [
            {ref: 'westMenu', selector: 'WestMenuPanel'}
        ],
    init: function() {
    	this.control({
    		// step1. 좌측 매뉴에 추가된 패널 즉 시스템 패널에 대한 이벤트 추가.
    		'WestMenuPanel > WestMenuDataViewPanel': {
    			afterrender: this.firstSelect,
    			expand: this.onItemClicked
            }
         });
    },
    /**
     *  step2. afterrender는 추가된 패널 별로 render된 이후에 호출한다.
     *	추가된 시스템 패널 중에 열려 있는지 확인하면 맨처음 하나만 열리게 된다.
     *	그 열려있는 시스템 패널에 하위 프로그램이 로딩되어 추가될 수 있도록 코딩해야한다.  
    **/
    firstSelect : function(panel){
    	panel.firstSelectDataView();
    },
    /**
     * Step3. 시스템 패널을 클릭 할 때 마다 expand이벤트가 호출 된다.
     * 이 때 클릭되어 expand된 패널 위에 하위 프로그램을 출력하면 된다.
     * @param a
     * @param b
     * @param c
     */
    onItemClicked : function(panel){
    	panel.onItemClicked();
    },
    onLaunch: function() {
        this.getWestMenu().setWestMenuDataViewPanel();
    }
});

// app/view/frame/WestMenuDataViewPanel.js 를 생성한다.
Ext.define('ria.view.frame.WestMenuDataViewPanel',{
    extend: 'Ext.panel.Panel',
    alias: 'widget.WestMenuDataViewPanel',
    animCollapse : true,
    collapsible : true,
    collapsed   : true,
    useArrows: true,
    rootVisible: false,
    multiSelect: false,
    initComponent: function() {
    	var me = this;
        Ext.apply(this, {
            items: [{
                xtype: 'dataview',
                trackOver: true,
                cls: 'feed-list',
                itemSelector: '.feed-list-item',
                overItemCls: 'feed-list-item-hover',
                tpl: '
{pgm_nm}

' }], header: { toolFirst: true }, /*** * Date : 2012.12.06 * Desc : dataview상에 출력된 리스트 중에 맨처음 프로그램이 * 선택되어지도록 한다. * @param store */ firstSelectDataView : function(){ if(me.collapsed) return; var store = this.onItemClicked(); if(store){ var task = new Ext.util.DelayedTask(function(){ me.down('dataview').getSelectionModel().select(store.getAt(0)); }); task.delay(1000); } }, /** * Step3. 시스템 패널을 클릭 할 때 마다 expand이벤트가 호출 된다. * 이 때 클릭되어 expand된 패널 위에 하위 프로그램을 출력하면 된다. * @param a * @param b * @param c */ onItemClicked : function(){ if(me.collapsed) return // 패널이 접히지 않은 놈을 찾는다. if(me.store) return; me.store = Ext.create('ria.store.system.Programs'); // 재활용 되는 것을 막는다. me.store.load({ params: { // 아래 코드는 시스템 패널이 가지고 있는 시스템 아이디를 프로그램 store에 전달하는 // 코드다 이렇게 해야 각기 시스템 패널별로 해당 시스템 이하의 프로그램을 가져올수 있다. pgm_syscd: this.pgm_syscd } }); // 최종 시스템 패널 안으 dataview에 프로그램 store를 바인딩 한다. me.down('dataview').bindStore(me.store); return me.store; } }); this.callParent(arguments); } }); // app/view/frame/WestMenuPanel.js를 추가합니다. Ext.define('ria.view.frame.WestMenuPanel',{ extend : 'Ext.panel.Panel', alias : 'widget.WestMenuPanel', layout:'accordion', // 이놈 하위에 추가될 dataview또한 accordion collapsible: true, split:true, title : '^^', activeItem: 0, initComponent: function() { var me = this; Ext.apply(this, { /*** * Date : 2012.12.07 * Desc : 최초 첫번째 프로그램 선택. */ setWestMenuDataViewPanel: function(){ var store = Ext.create('ria.store.system.Systems'); store.load(function(record, b, c){ // 아직 로드전이므로 로드한다.이때 json파일 호출 store.each(function(rec){ // store를 탐색하여 me.add({ // 메뉴가 추가될 패널에 아래와 같이 패널을 추가. xtype:'WestMenuDataViewPanel', title:rec.get('pgm_sysnm'), // 시스템명 pgm_syscd:rec.get('pgm_syscd'), // 시스템 코드 iconCls:rec.get('pgm_sysicon') // 아이콘이 있다면 표기 }); }); }); } }); this.callParent(arguments); } });

이제 프로그램을 다시 확인해보면 동일하게 작동하는 것을 알수 있습니다. 앞으로는 Controller에 기능을 구현하고 구현된 기능을 어디로 옮겨야할지 생각해 본 후 각 뷰클래스등에 구현하므로해서 코드의 밸런스를 맞춰가도록 하겠습니다.


Posted by 베니94
자바스크립트/Ext JS2012. 11. 30. 14:52

이번에는 이전 강좌에서 좌측 메뉴를 완성해보겠다.

좌측 메뉴는 이전강좌에서 panel을 하나 추가하여 "사용자 관리"라는 메뉴를 하나

강제로 생성 시켰다. 이번에는 ajax를 통해 메뉴정보를 가져올 수 있도록 store를 구성하고

해당 store에서 값을 가져와 메뉴를 구성하기까지 과정을 알아보자.

아래 그림처럼 store와 model폴더에 파일을 만들어 놓자.



/**
 * Date : 2012.11.30
 * Desc : 프로그램 정보를 표현할 모델 클래스
 */
Ext.define('ria.model.system.Program', {
	extend: 'Ext.data.Model',	// extend
    fields: [
		'pgm_id',
		'pgm_nm',
		'pgm_syscd',
		'pgm_sysnm',
		'pgm_class',
		'pgm_icon',
		'pgm_sysicon',
		'group_id',
		'group_nm',
		'group_status_cd',
		'group_status_nm',
		'group_pgm_status_nm'
    ]
});

/**
 * Date : 2012.11.30
 * Desc : 좌측 시스템 리스트
 * 		  여기서 중분류 정도의 시스템 리스틀 가져온다.
 */
Ext.define('ria.store.system.Systems', {
    extend: 'Ext.data.Store',	// 당연히 store상속
	autoLoad : false,			// 자동 로드는 꺼놓자.
    model: 'ria.model.system.Program',	// 모델 세팅
	proxy: {
        type: 'ajax',
        // json폴더를 루트에 만들고 systemlist.json이라는 파일를 통해 
        // 시스템 리스트를 받을 수 있다.
        url: '/json/systemlist.json',	
        reader: {
        	type: 'json',
        	root: 'entitys',
        	totalProperty: 'totalCount',
        	messageProperty: 'message'
        },
        listeners: {
            exception: function(proxy, response, operation){
            	// 나중에 구현할 부분 모든 ajax 통신에 공통으로 쓸 수 있는
            	// 에러 캐치 함수를 만들 것이다.
			}
	    }
    }
});
// 이제 위의 store에서 호출하는 json파일을 만듭니다.
// 현재는 아래와 같이 만들어져 있지만 실제로는 디비의 값을 가져와 이 파일의 내용을 출력하도록
// 만 서버쪽 개발만 하면 클라이언트쪽은 호출주소외에 전혀 손댈때가 없게 됩니다.
/**
 ** Date : 2012.11.30
 ** Desc : 시스템 리스트 제공
 ** Posn : /json/systemlist.json
 **/
{"entitys":[{"pgm_syscd":"F001","pgm_sysicon":"grid","pgm_sysnm":"거래처관리"},
			{"pgm_syscd":"F002","pgm_sysicon":"grid","pgm_sysnm":"영업일지"},
			{"pgm_syscd":"F003","pgm_sysicon":"grid","pgm_sysnm":"서류관리"},
			{"pgm_syscd":"F004","pgm_sysicon":"grid","pgm_sysnm":"통보서관리"},
			{"pgm_syscd":"F005","pgm_sysicon":"grid","pgm_sysnm":"현황관리"},
			{"pgm_syscd":"F007","pgm_sysicon":"grid","pgm_sysnm":"실적관리"},
			{"pgm_syscd":"S002","pgm_sysicon":"grid","pgm_sysnm":"패키지관리"}],
			"errMsg":"","errTitle":"검색결과","message":"","success":true,"totalCount":"8"}




이제 위에서 만들어진 store, model, json파일 등을 이용해서 좌측 메뉴가 나올 수 있도록 Controller를 변경해보겠다.
// app/controller/FrameController.js 를 수정한다.
// views, refs, onLaunch를 확인하자.
Ext.define('ria.controller.FrameController', {
    extend: 'Ext.app.Controller',
    stores: ['system.Systems'],
    views: ['ria.view.frame.WestMenuPanel',
            'ria.view.frame.WestMenuDataViewPanel'], // 꼭 명시되어야함, 명시하지 않을 경우 require를 이용해야합.
    refs: [
            {ref: 'westMenu', selector: 'WestMenuPanel'}
        ],
    init: function() {
        this.control({
             
        });
    },
    onLaunch: function() {
        // refs의 ref명으로 뷰를 찾아온다.
        var menu = this.getWestMenu();
        var store = this.getStore('system.Systems'); 	// store를 호출
        store.load(function(record, b, c){				// 아직 로드전이므로 로드한다.이때 json파일 호출
        	store.each(function(rec){		// store를 탐색하여
			    menu.add({					// 메뉴가 추가될 패널에 아래와 같이 패널을 추가.
			    	xtype:'WestMenuDataViewPanel',
			    	title:rec.get('pgm_sysnm'),	// 시스템명
			    	fnc_pgm_syscd:rec.get('pgm_syscd'),	// 시스템 코드
			    	iconCls:rec.get('pgm_sysicon')	// 아이콘이 있다면 표기
			    });
			});
        });
    }
});

// 위에서 사용한 메뉴 패널
// app/view/frame/WestMenuPanel.js를 추가합니다.
Ext.define('ria.view.frame.WestMenuPanel',{
    extend  : 'Ext.panel.Panel',
    alias   : 'widget.WestMenuPanel',
    layout:'accordion',	// 이놈 하위에 추가될 dataview또한 accordion
    collapsible: true,
    split:true,
    title : '^^',
    activeItem: 0,
    initComponent: function() {
         
        this.callParent(arguments);
    }
});
이제 실행해보자. 아래와 같이 좌측에 시스템리스트가 보일 것이다.


이제 추가된 시스템 패널 중에 열려 있는 패널이 어떤 패널인지 확인 해보자. westMenuPanel에 panel이 accodion레이아웃으로

추가되었으므로 그중 열려 있는 놈을 찾는 것이다.



Ext.define('ria.controller.FrameController', {
.
.
.
    init: function() {
    	this.control({
    		// step1. 좌측 매뉴에 추가된 패널 즉 시스템 패널에 대한 이벤트 추가.
    		'WestMenuPanel > WestMenuDataViewPanel': {
    			afterrender: this.firstSelect,
    			expand: this.onItemClicked
            }
         });
    },
    /**
     *  step2. afterrender는 추가된 패널 별로 render된 이후에 호출한다.
     *	추가된 시스템 패널 중에 열려 있는지 확인하면 맨처음 하나만 열리게 된다.
     *	그 열려있는 시스템 패널에 하위 프로그램이 로딩되어 추가될 수 있도록 코딩해야한다.  
    **/
    firstSelect : function(panel){
    	console.log('추가된 시스템 패널', panel.xtype, panel.title, panel.collapsed);
    	if(!panel.collapsed){
    		// accordion 레이아웃에 패널이 추가되면 디폴트로 맨처음 패널이
    		// 패널이 펼쳐져 있게 되고 이놈을 찾아서 해당 시스템 이하의 프로그램을 불러올 수 있도록 코딩한다.
    		console.log('추가된 시스템 패널 중 접히지 않은 패널 :', panel.title, panel.collapsed);
    	}
    },
    onItemClicked : function(a, b, c){
    	console.log(a,b,c);
    },

콘솔에 출력해 보면 좌측 시스템 패널중에 열려 있는 패널은 "거래처관리"라는 것을 알 수 있다.

다음으로는 추가된 패널을 클릭 했을 때 즉 현재는 click이 아니라 expand이벤트 이므로 이 이벤트가 전달하는 arguments가 뭔지 알아보자. doc.sencha.com에서 아래와 같이 api를 확인해 보자. 역시나 expand이벤트 또한 panel객체 자기자신을 전달하고 있다.


이제 expand이벤트를 구현할 함수 (onItemClick)을 구현해 보자.

프로그램 store를 준비한다.



/**
 ** Date : 2012.11.30
 ** Desc : 시스템이하 프로그램 리스트 제공
 ** Posn : /json/programlist.json
 **/
{"entitys":[{"pgm_class":"ria.view.pgms.RiaAppMain01","pgm_icon":"grid","pgm_nm":"하위 프로그램1"},
			{"pgm_class":"ria.view.pgms.RiaAppMain02","pgm_icon":"grid","pgm_nm":"하위 프로그램2"},
			{"pgm_class":"ria.view.pgms.RiaAppMain03","pgm_icon":"grid","pgm_nm":"하위 프로그램3"},
			{"pgm_class":"ria.view.pgms.RiaAppMain04","pgm_icon":"grid","pgm_nm":"하위 프로그램4"},
			{"pgm_class":"ria.view.pgms.RiaAppMain05","pgm_icon":"grid","pgm_nm":"하위 프로그램5"},
			{"pgm_class":"ria.view.pgms.RiaAppMain06","pgm_icon":"grid","pgm_nm":"하위 프로그램6"}
			],
			"errMsg":"","errTitle":"검색결과","message":"","success":true,"totalCount":"2"}

/**
 * Date : 2012.11.30
 * Desc : 좌측 시스템 이하 프로그램 리스트
 * 		  여기서 최하위 단 프로그램 리스트를 가져온다.
 */
Ext.define('ria.store.system.Programs', {
    extend: 'Ext.data.Store',	// 당연히 store상속
	autoLoad : false,			// 자동 로드는 꺼놓자.
    model: 'ria.model.system.Program',	// 모델은 동일하게 사용 세팅
	proxy: {
        type: 'ajax',
        // json폴더를 루트에 만들고 programlist.json이라는 파일를 통해 
        // 시스템 리스트를 받을 수 있다.
        url: '/json/programlist.json',	
        reader: {
        	type: 'json',
        	root: 'entitys',
        	totalProperty: 'totalCount',
        	messageProperty: 'message'
        },
        listeners: {
            exception: function(proxy, response, operation){
            	// 나중에 구현할 부분 모든 ajax 통신에 공통으로 쓸 수 있는
            	// 에러 캐치 함수를 만들 것이다.
			}
	    }
    }
});

// 이하 컨트롤러의 onItemClicked 함수 구현
    /**
     * Step3. 시스템 패널을 클릭 할 때 마다 expand이벤트가 호출 된다.
     * 이 때 클릭되어 expand된 패널 위에 하위 프로그램을 출력하면 된다.
     * @param a
     * @param b
     * @param c
     */
    onItemClicked : function(panel){
    	var store ;
    	if(!panel.collapsed){	// 패널이 접히지 않은 놈을 찾는다.
    		// 아래 코드를 보면 시스템 패널은 dataview를 가지고 있다. 이 dataview는
    		// 해당 패널이 가지고 있는 시스템 아이디에 해당하는 프로그램을 표현하게 된다.
    		// dataview는 expand될 때 하위 프로그램을 가져오도록 했기 때문에 
    		// 이미 가져왔다면 다시 서버에 로드 되지 않도록 한다. 굳이 매번 쓸데없이
    		// 서버에 다녀오지 않게 하기 위함.
    		if(panel.down('dataview').store.getTotalCount() > 0) return;
    		// 아래 코드에서는 Ext.create를 사용하였다.
    		// 이는 컨트롤러에 아래 스토어를 명시하지 않았기 때문인데 명시하지 않은 것은.
    		// 프로그램 리스트 store가 시스템별로 여러개 존재하므로 컨트롤러에 명시할 경우
    		// 모든 시스템 패널이 동일한 프로그램을 보게 되므로 각기 다르게 생성하여
    		// 공유하지 못하도록 하기 위함이다.
    		store = Ext.create('ria.store.system.Programs');	// 재활용 되는 것을 막는다.
	    	store.load({
	    		params: {
	    			// 아래 코드는 시스템 패널이 가지고 있는 시스템 아이디를 프로그램 store에 전달하는
	    			// 코드다 이렇게 해야 각기 시스템 패널별로 해당 시스템 이하의 프로그램을 가져올수 있다.
	    			pgm_syscd: panel.pgm_syscd
	    		}
	    	});
	    	// 최종 시스템 패널 안으 dataview에 프로그램 store를 바인딩 한다.
	    	panel.down('dataview').bindStore(store);
	   	}
	   	return store;
    },
이제 실행하고 열려 있지 않은 패널을 클릭하여 열어보도록 한다. 그럼 시스템 패널에 프로그램들이 출력 되는 것을 볼 수 있다.
크롬에 개발자 도구를 꼭 사용해야 한다.


그런데 최초에는 거래처 관리에 프로그램이 비어 있다. 클릭 하고 다시 클릭하면 프로그램이 보이지만 처음엔 보이지 않는다.

이 부분은 firstSelect 함수에서 해결해 보자. 이 함수는 afterrender로 가 될 때 한번 호출 되므로 이 때 열려진 패널에 프로그램이

세팅되도록 해보겠다.


   /**
     *  step2. afterrender는 추가된 패널 별로 render된 이후에 호출한다.
     *	추가된 시스템 패널 중에 열려 있는지 확인하면 맨처음 하나만 열리게 된다.
     *	그 열려있는 시스템 패널에 하위 프로그램이 로딩되어 추가될 수 있도록 코딩해야한다.  
    **/
    firstSelect : function(panel){
    	console.log('추가된 시스템 패널', panel.xtype, panel.title, panel.collapsed);
    	if(!panel.collapsed){
    		// accordion 레이아웃에 패널이 추가되면 디폴트로 맨처음 패널이
    		// 패널이 펼쳐져 있게 되고 이놈을 찾아서 해당 시스템 이하의 프로그램을 불러올 수 있도록 코딩한다.
    		console.log('추가된 시스템 패널 중 접히지 않은 패널 :', panel.title, panel.collapsed);
    		
    		// Step4. 
    		// 이제 시스템 패널이 rendering된 후에 열려있는 맨처음 시스템 패널에 프로그램이 자동세팅되도록 해야한다.
    		// expand이벤트를 통해 클릭 시마다 프로그램이 추가되지만 맨처음 시스템 패널은 비어 있으므로 채우도록 코딩한다.
    		// onItemClicked함수는 시스템 패널의 dataview에 store를 바인딩하는 기능을 한다. 
    		var store = this.onItemClicked(panel); 
    	}
    },



이제 main.html파일을 열고 하위프로그램에서 사용할 css를 추가합니다.


// main.html
    < script type="text/javascript" src="app.js" >< /script >
    < style type="text/css" >
	    .feed {
			width: 16px;
			height: 16px;
			background-image: url(/common/images/icon/rss.gif) !important;
		}
		.feed-picker-url {
			 float: left;
			 width: 425px;
		}
		.feed-picker-title {
			/*    float: left;*/
			 color: #777;
			 font-size: 10px;
		}
		.feed-list {
		 	padding: 0 3px 0 3px;
		}
		.feed-list-item {
			 margin-top: 3px;
			 padding-left: 20px;
			 font-size: 11px;
			 line-height: 20px;
			 cursor: pointer;
			 background: url(/common/images/icon/rss.gif) no-repeat 0 2px;
			 border: 1px solid #fff;
		}
		.feed-list .x-item-selected {
			 font-weight: bold;
			 color: #15428B;
			 background-color: #DFE8F6;
			 border: 1px dotted #A3BAE9;
		}
		.feed-list-item-hover {
			
		}
    < /style >
< /head >
< body >


이제 프로그램에 예쁘게 css가 적용되었다. ^^


Posted by 베니94
자바스크립트/Ext JS2012. 7. 22. 23:24

이제 좌측을 구현하겠습니다.
좌측 패널은 프로그램 메뉴가 나오는 영역입니다.
로그인 한 이후 화면을 코딩하는 것이므로 사용자가 가지고 있는 즉 권한에 해당되는 프로그램들이
보여지게 되면 되겠습니다.
panel을 사용하고 layout은 accodion을 이용합니다.

 

// app/view/Viewport.js를 수정합니다.
Ext.define('ria.view.Viewport', {
    extend: 'Ext.container.Viewport',
    layout : 'border',
    items: [
    {
		region: 'center',
		xtype: 'panel',
		title : '중간'
	},{
		region: 'west',
		xtype : 'WestMenuPanel',
		width: 200
	}]
});
// app/view/frame/WestMenuPanel.js를 추가합니다.
Ext.define('ria.view.frame.WestMenuPanel',{
	extend 	: 'Ext.panel.Panel',
	alias	: 'widget.WestMenuPanel',
	layout:'accordion',
    collapsible: true,
    margins:'5 0 5 5',
    split:true,
    title : 'Navigation',
    activeItem: 0,
    margins: '5 5 5 5',
    initComponent: function() {
    	
        this.callParent(arguments);
    }
});
// 이제 이 뷰를 사용하려면 컨트롤러에 위의 뷰가 명시되어 있어야합니다.
Ext.define('ria.controller.FrameController', {
    extend: 'Ext.app.Controller',
    views: ['ria.view.frame.WestMenuPanel'], // 꼭 명시되어야함, 명시하지 않을 경우 require를 이용해야합.
	init: function() {
		this.control({
			
		});
    }
});

여기까지 하고 재실행 해보면 크게 바뀐게 없습니다.

이제 좌측 패널의 accodion레이아웃에 맞게 들어갈 패널을 코딩합니다. 이 패널은 dataview를 가지고 있고 이 dataview에
하나의 서브시스템과 해당 서브시스템 아래의 프로그램이 표현되게 됩니다.
그리고 이 패널은 최초 컨트롤러가 로드될 때 WestMenuPanel의 item으로 자동 추가 되게 구현합니다.

 

// app/view/frame/WestMenuDataViewPanel.js 를 생성한다.
Ext.define('ria.view.frame.WestMenuDataViewPanel',{
    extend: 'Ext.panel.Panel',
    alias: 'widget.WestMenuDataViewPanel',
    animCollapse : true,
    collapsible : true,
    collapsed   : true,
    useArrows: true,
    rootVisible: false,
    store: this.store,
    multiSelect: false,
    initComponent: function() {
        Ext.apply(this, {
            items: [{
            	xtype: 'dataview',
                trackOver: true,
                store: this.store,
                cls: 'feed-list',
                itemSelector: '.feed-list-item',
                overItemCls: 'feed-list-item-hover',
                tpl: '
{pgm_nm}

' }], header: { toolFirst: true } }); this.callParent(arguments); } }); // app/controller/FrameController.js 를 수정한다. // views, refs, onLaunch를 확인하자. Ext.define('ria.controller.FrameController', { extend: 'Ext.app.Controller', views: ['ria.view.frame.WestMenuPanel', 'ria.view.frame.WestMenuDataViewPanel'], // 꼭 명시되어야함, 명시하지 않을 경우 require를 이용해야합. refs: [ {ref: 'westMenu', selector: 'WestMenuPanel'} ], init: function() { this.control({ }); }, onLaunch: function() { // refs의 ref명으로 뷰를 찾아온다. var menu = this.getWestMenu(); menu.add({ xtype : 'WestMenuDataViewPanel', title:'사용자관리' }); console.log(menu); } });

여기까지 하고 실행해보자.

자 이제 동적으로 accondion layout형태로 panel을 추가한것을 볼 수 있다 . 이제 이 것을 응용해서
디비에서 데이터를 불러와 자신이 가지고 있는 권한에 맞게끔 시스템을 불러오도록 하면 되겠습니다.
우선 디비에서 데이터를 줬다고 생각하고 json형태로 파일을 만들고 먼저 작업하도록 하겠습니다.

Posted by 베니94
자바스크립트/Ext JS2012. 7. 22. 01:12

이제 본격적으로 코딩을 해봅니다.

아래 그림은 최종 구현된 모습을 보여주고 있습니다.
실제 프로젝트에서는 항상 권한과 각기 다른 프로그램들을 디비설정에 의해 추가하고
삭제할 수 있어야 합니다.

이제 ExtJS 코딩에 들어가겠습니다. 모든 코딩은 mvc모델을 준수합니다. 아래 처럼 폴더구조를 만들고 시작하겠습니다.
war폴더 밑에 app.js파일 , model, view, controller폴더를 생성합니다.

이제 app.js를 코딩해서 틀을 잡습니다.
Ext.application({
    name: 'ria',	// 어플리케이션 명
    appFolder : '/app',	// 어플리케이션 폴더
    // 실행시 사용할 모든 컨트롤러를 명시한다.
    controllers: [
        'FrameController' // 어플리케이션이 사용할 컨트롤러
    ],    
    autoCreateViewport: true
});
이제 뷰포트와 위에서 명시한 컨트롤러를 코딩합니다.
// app/view/Viewport.js
Ext.define('ria.view.Viewport', {
    extend: 'Ext.container.Viewport',
    layout : 'border',
    items: [
    {
		region: 'center',
		xtype: 'panel',
		title : '중간'
	},{
		region: 'west',
		xtype : 'panel',
		width: 200,
		title : '좌측'
	}]
});
// app/controller/FrameController.js
Ext.define('ria.controller.FrameController', {
    extend: 'Ext.app.Controller',
	
	init: function() {
		this.control({
			
		});
    }
});

// smpl/index.html파일을 수정해서 war밑에 main.html로 추가하자.
< html>
< head>
    < meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    < title id="page-title">샘플
    < link rel="stylesheet" type="text/css" href="/extjs/resources/css/ext-all.css">
    < script type="text/javascript" src="/extjs/ext-debug.js">
    < script type="text/javascript" src="app.js">
< /head>
< body>

< /body>
< /html> 

 

ExtJS 포럼 http://benney.able.or.kr/forum

Posted by 베니94
자바스크립트/Ext JS2012. 7. 21. 23:01

 

오늘은 첫번째 시간으로 ExtJS를 이용한 Ria어플리케이션개발에 필요한 준비를 해보도록 하겠습니다.

1. 이클립스
  - 일반적인 자바웹프로그래밍 환경과 별다른 것 없습니다.
     단 Spring을 사용할 것이므로 관련 설정과 파일들이 들어가겠죠.
     일단 최신버전 juno를 받아 사용하도록 하겠습니다.
아래 그림과 같이 프로젝트르 만들겠습니다.

 

 

 

 

encoding을 맞춰주자 UTF-8

다음은 ExtJS를 다운받고 파일을 카피해 넣도록 하겠습니다.

 

압축이 풀린 폴더구조는 아래와 같습니다.

이제 아래 폴더 중의 파일들을 정리해서 새로 만든 이클립스 프로젝트에 카피하도록 하겠습니다.
일단 이클립스 프로젝트의 war폴더에 extjs라는 폴더를 만듭니다.
그리고 아래 그림의 압축파일 중 루트에 있는 모든 js파일과 src폴더, resources폴더를 이클립스
프로젝트 war/extjs폴더에 카피한다.

이제 ExtJS로 샘플을 만들고 한번 실행해보겠습니다.
프로젝트의 war폴더에 smpl폴더를 추가하고 아래와 같이 코딩한 후 index.html로 저장하고 실행합니다.

< html>
< head>
    < meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    < title id="page-title">샘플

	< link rel="stylesheet" type="text/css" href="/extjs/resources/css/ext-all.css">
    < script type="text/javascript" src="/extjs/ext-debug.js"/>
    < script type="text/javascript" >    
    Ext.onReady(function() {
    	var panel = new Ext.create('Ext.panel.Panel',{
    		title : '시작해 볼까',
    		html : 'ExtJS 쓸만해요 ^^',
    		renderTo : Ext.getBody()
    	});
    	
    });
    < /script>
< /head>
< body>

< /body>
< /html> 

 

Posted by 베니94