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

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

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

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

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

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

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


Posted by 1 베니94
TAG extjs, ExtJS4

댓글을 달아 주세요

  1. 최종록

    ^^잘 부탁드립니다.
    혹시 전에 했던 예제는 끝난건가요?

    2012.12.13 13:21 [ ADDR : EDIT/ DEL : REPLY ]
    • 그 예제를 활용해서 쭉 붙여나가는거죠 만들짐 프레임에서 확장한다고 보시면됩니다

      2012.12.13 18:24 신고 [ ADDR : EDIT/ DEL ]
  2. 궁금이

    오.. 궁금해요.. 기다리고있습니다!

    2012.12.13 14:48 [ ADDR : EDIT/ DEL : REPLY ]

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

좌측 메뉴는 이전강좌에서 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 1 베니94

댓글을 달아 주세요

  1. jongoks

    작성해주신 글 너무나도 유용하게 잘 보았습니다.
    그런데 전 그대로 따라했는데 CSS가 제대로 안먹히는 이유는 어떤이유일까요? ;;
    .feed-list .x-item-selected 이부분도 안되는 것 같고, feed-list-item도 안되어서 feed-list-itemgrid로 고치니 반영되네요..
    css 적용하기 위해 .x-item-selected 이런 클래스들이 생기는 건 어떤 문서를 봐야 알 수 있는 부분인가요?
    혹시 시간 되실때 답변도 주시면 감사합니다. ^^

    2013.01.17 16:07 [ ADDR : EDIT/ DEL : REPLY ]
  2. benneykwag@gmail.com

    소스를 아예 올려드려야겠어요
    좀만 기다려주세요

    2013.01.18 09:04 [ ADDR : EDIT/ DEL : REPLY ]
  3. choiys6031@naver.com

    Extjs4 학습중에 많은 도움얻고있습니다.
    궁금한점이 있는데
    store.load({
    params: {
    // 아래 코드는 시스템 패널이 가지고 있는 시스템 아이디를 프로그램 store에 전달하는
    // 코드다 이렇게 해야 각기 시스템 패널별로 해당 시스템 이하의 프로그램을 가져올수 있다.
    pgm_syscd: panel.pgm_syscd
    }
    })
    위소스에서 store(Programs.js) 에 파라미터로 pgm_syscd 를 넘기고 있는데 실제 store에서는 pgm_syscd 값을 어떤방법으로 얻을수 있나요?

    2013.08.14 13:04 [ ADDR : EDIT/ DEL : REPLY ]
  4. lee

    이것좀 봐주세요
    Ext.onReady(function(){
    var selNodeObj=null;


    var store = Ext.create('Ext.data.TreeStore', {
    id: 'store',
    autoHeight:true,


    autoLoad: false,
    // autoSync: true,
    root :{
    text : '프로젝트명',
    name : '1단',
    expanded : false,
    },
    proxy : {
    type : 'ajax',
    api: {
    read : '/auto/tree/json/jsonobject.do',
    create: '/auto/tree/EgovTreeCreateInsert.do',
    update: '/auto/tree/EgovTreeCreateUpdate.do',
    destroy: '/auto/tree/EgovTreeCreateDelete.do'

    },
    reader: {
    type: 'json',
    successProperty:'success',
    rootProperty: 'children',
    // rootProperty: 'items',
    messageProperty:'message'
    // ,root:'tree'
    }
    ,writer:{
    type:'json',
    writeAllFields:true,
    // root:'auto'
    encode:true,
    rootProperty:'children'
    }

    },
    listeners: {



    }







    });

    var tree = Ext.create('Ext.tree.Panel', {
    title: '에스제이테크시스템',
    id: 'tree',
    rootVisible: true,
    multiSelect: true,
    store: store,
    viewConfig: {
    plugins: {
    ptype: 'treeviewdragdrop',
    enableDrag: true,
    enableDrop: true
    }
    },
    multiSelect: true,
    plugins: 'cellediting',
    columns: [{
    header:'프로젝트명',
    xtype: 'treecolumn',
    dataIndex: 'text',
    flex: 1,
    editor: {
    xtype: 'textfield',
    allowBlank: false,
    allowOnlyWhitespace: false
    }
    },{header:'합계',dataIndex:'disable',flex:1, editor: {
    xtype: 'textfield',
    allowBlank: false,
    allowOnlyWhitespace: false
    },width:500}],
    renderTo: Ext.get('treeList'),
    listeners:{
    itemcontextmenu : function( object, record, item, index, e){
    e.stopEvent();
    var menu = Ext.create('Ext.menu.Menu',{
    items : [
    {
    text: '추가',
    handler: onItemClick,
    iconCls: 'icon-edit'
    },
    {
    text: '삭제',

    handler: onItemClick
    }
    ]
    });
    menu.showAt(e.getXY());
    }
    }

    });
    function onItemClick(item){
    node = store.getNodeById("Node1");
    var treePanel = Ext.getCmp('tree');
    if(item.text=='삭제'){

    var record = treePanel.getSelectionModel().getSelection()[0];
    record.remove(true);
    store.sync();

    }else if(item.text=='추가'){


    var selNode = treePanel.getSelectionModel().getSelection()[0];
    selNodeObj=selNode;
    var appendedChild = selNode.appendChild({
    // task: 'Task1',
    // user: 'Name',
    // duration: '10',
    text : '추가',
    name : '1단',
    leaf: true
    });
    var newRecords = store.getNewRecords();
    Ext.each(newRecords, function(record,index){
    console.log('New (',index,')',record.get('name'));
    });
    store.sync();

    }

    }

    });

    여기서 proxy를 사용했는데요 아래에 api를 지정하고 하면 삭제시에 destroy 수정시 update, 트리 추가시에 create롤 타겠끔 할려고 합니다 어떻게 해줘야 타는지요 ?

    이것에 대한 방법을 아시면 메일로 좀 보내주세요
    ldongkyu@hanmail.net 입니다
    감사합니다

    2015.07.15 12:32 [ ADDR : EDIT/ DEL : REPLY ]

분류없음2012.11.18 16:42

코드성 콤보 박스를 그리드의 에디터와 일반 폼화면에서 사용할 수 있도록 구현한다.



위의 모습은 최종 구현 모습니다.

그리드에 경우 더블클릭하여 콤보박스가 표시되도록 하였고 일반 폼에 경우는 바로 바인딩된 값이 보여지도록 하였다.

일반적인 코드콤보박스는 코드명이 화면에 보여지고 코드가 내부적으로 선택되고 저장되게 된다.

아래 그림은 서버를 통해 가져온 json값이다.  그리드에서는 아래와 같이 코드와 코드명 둘다 가져온다.

코드는 그리드에서 표시하고 코드명은 바인딩된 폼에서 보여주게 된다.

사실 기존에 사용하던 방법이 약간에 문제가 있어 이번에는 모두 해결된 코드를 소개하도록 합니다.

/***
 * Date : 2012.11.17
 * Desc : 본 클래스는 그리드가 렌더링 될 시 렌더러 함수내에서 외부 콤보박스의 
 * 		  store가 로드되지 않은 채로 호출되는 문제를 해결하였다.
 */
Ext.define('pns.common.ComboColumn', {
    extend:'Ext.grid.Column',
    alias: 'widget.pns.combocolumn',
    initComponent: function() {
        var me = this;
        me.callParent();
        // Refreshes grid on store load in order to apply the renderer function
        var editor = this.editor || this.field
            store = editor.store;
        store.on('load', function() { me.up('gridpanel').getView().refresh() });
        // Manages store autoloading
//        if (!store.autoLoad && !store.loaded) store.load();
    },
    renderer: function(value, metaData, record, rowIndex, colIndex, store) {
    	
    	/**
    	 * Date : 2012.11.17
    	 * Desc : 이쪽을 좀 수정했다.
    	 * 		  우리가 원하는 것은 렌더링 시 코드가 코드명으로 보여지고 콤보박스에서 
    	 *   	  수정하면 수정된 코드명이 record에 update 되도록 해야한다.
    	 *   	  이래야 코드와 코드명을 일관되게 서버에 보내고 클라이언트에서 확인 할 수 있다.
    	 */
        var column = this.columns[colIndex],
            editor = column.editor || column.field,
            comboStore = editor.store,
            displayField = editor.displayField;
            index = comboStore.findExact(editor.valueField, value);
            if (index != -1){
            	rs = comboStore.getAt(index).data;
            	record.set(editor.updateNameField, eval('rs.'+displayField)); // 이부분을 통해서 변경된 콤보박스의 코드명이 수정 세팅된다.
            	console.log('변경된 레코드 ', editor.updateNameField,  displayField,  eval('rs.'+displayField), record)
            	return eval('rs.'+displayField);
            }
            return value;
    }
});

/***
 * Date : 2012.11.17
 * Oprt : winkos
 * Desc : 이 클래스는 그리드에서 더블클릭하여 에디터(콤보박스)로 사용되고 폼에서
 * 		  콤보박스로 사용된다.
 * UseCase 
 * 1. 그리드에서 에디터로 사용시
 *    var mst_use_yn = Ext.widget('commonCbx',{
 *    	updateNameField : 'mst_use_yn_nm',
 *    	autoload : true,	// true로 한다. 한번만 서버에 다녀오고 이후 에는 재활용된다.
 *    	group_code : 'A001'
 *    });
 *    // 이하 그리드 컬럼
 *   	columns: [
 *   		        {
 *   				    header: "컬럼",
 *   				    xtype: 'pns.combocolumn',	// 필수
 *   				    dataIndex: 'mst_use_yn',
 *   				    editor : mst_use_yn,	// 그리드에서는 xtype사용하지 않는다 위에서 처럼 하나 만들어서 같이 사용한다. autoload문제도 있다.
 *   				} ,
 *   2. 폼에서 사용 시 
 *   { 
 *    	xtype : 'commonCbx',
 *     	group_code : 'A001',
 *     	allowBlank:false,
 *      fieldLabel: '심사부문 ',
 *     	name: 'mst_use_yn_nm', // 이 클래스는 최초 store를 로드 하지 않게 하였다. 성능문제를 신경쓰다보니. 무조건 코드명이 보여지도록 필드명도 신경쓴다.
 *      codefield: 'mst_use_yn', // 이 부분에 명시된 텍스트로 hiddenfield를 만들고 선택된 값을 자동으로 세팅하게 한다.
 *      emptyText: 'password'
 * }      
		
 */
Ext.define('pns.common.CodeComboBox', {
	extend:'Ext.form.field.ComboBox',
    alias:'widget.commonCbx',
    labelAlign: 'right',
    labelWidth: 75,
	initComponent: function(){
		var me = this;
		// 폼에서 사용 시 코드용 텍스트 필드를 추가해준다.
		var task = new Ext.util.DelayedTask(function(){
			me.codeFieldCreate(me);
		});
		task.delay(1000);
		this.on('select', this.onSelectChanged, this);
		this.store =  Ext.create('Ext.data.Store', {
			autoLoad: this.autoload,
			fields: ['code','code_name','cd_amt'],
			proxy: {
				type: 'ajax',
				url: '/json/code.jsp?codes',
				reader: {
					type:'json',
					root: 'entitys'
				}
			},
			extraParams: {
    			notInCode: this.notInCode,
    			likeCode: this.likeCode,
    			likeCodeDesc:this.likeCodeDesc,
    			group_code: this.groupCode
            }
		});
		 this.callParent(arguments);
		 var store = this.store;
		 this.store.on('load', function() { me.setValue(me.getValue()) });
		 // Manages store autoloading
		 //if (!this.store.autoLoad && !this.store.loaded) this.store.load();
	},
	codeFieldCreate : function(){
		if(!this.codefield)
			return;
		this.up('form').add({
			xtype : 'hiddenfield',
			name : this.codefield
		});
	},
	onSelectChanged: function(combo, t) {
		if(!this.codefield)	// codefield가 없다면 그리드에서 사용한 것이다.
			return;
		// 아래 내용은 코드와 코드명이 뒤바뀌 세팅되는 것을 막기위해 재세팅한다.
		var form = this.up('form').getForm();
		form.findField(this.codefield).setValue(t[0].get('code'));
		form.findField(this.name).setValue(t[0].get('code_name'));
	},
	displayField : 'code_name',
	valueField : 'code',
    typeAhead : true,
    editable : false,
    lazyRender: true
});

// 이하 사용 예
// 에디터 생성
var mst_use_yn = Ext.widget('commonCbx',{
			updateNameField : 'mst_use_yn_nm',
			autoload : true,  // 미리 로딩해야 한번 만 로딩하고 모든 로우가 재활용 한다.
			group_code : 'A001' // 서버쪽에 넘겨줄 상위코드, 이값으로 하위에 코드를 가져온다.
		});
// 그리드 예
items : [{
		        	xtype: 'grid',
		        	columnLines: true,
..
..
{
					    header: "상태",  // 컬럼헤드
					    width: 70, 
					    xtype: 'pns.combocolumn', 
					    dataIndex: 'mst_use_yn',
					    editor : mst_use_yn
					}
// 폼에서 활용.
 { 
                	xtype : 'commonCbx',
                	group_code : 'A001',
                	allowBlank:false, 
                	fieldLabel: '심사부문 ', 
                	name: 'mst_use_yn_nm', 
                	codefield: 'mst_use_yn',
                	emptyText: '심사부문을 선택해주세요'
                }
Posted by 1 베니94
TAG ExtJS4

댓글을 달아 주세요

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

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

이제 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 1 베니94
TAG ExtJS4

댓글을 달아 주세요

  1. 이용약관위배로 관리자 삭제된 댓글입니다.

    2014.12.22 05:52 [ ADDR : EDIT/ DEL : REPLY ]
  2. 포럼 운영 안하시나요~~?

    2015.06.23 14:06 신고 [ ADDR : EDIT/ DEL : REPLY ]
  3. 비밀댓글입니다

    2015.07.15 12:26 [ ADDR : EDIT/ DEL : REPLY ]

ExtJS 4가 정식버전이 출시되었습니다.

기존에 문제 있던 던 부분들에 대해 거의 해결된 듯 하고 성능부분도 

충분히 개선되었다고 생각됩니다

해서 실무에서 적용가능한 코드를 공개하여 강좌를 진행 해보려고 합니다.

현재 mvc모델을 적용하여 프로젝트를 진행하고 있고 이 프로젝트에서 나오는

주요 코드를 오픈하는 것으로 강좌를 진행할까 합니다.

주요 내용

1. ExtJS 4를 활용한 UI 구성

2. MVC모델을 통한 콤포넌트 제어

3 실프로젝트에서 활용가능한 공통컴포넌트 만들기(데이터베이스 연계)

4 스프링프레임웍을 활용한 서버쪽 코드 만들기

5 Sencha SDK를 활용한 배포파일 만들기

Posted by 1 베니94
TAG ExtJS4

댓글을 달아 주세요

오늘은 히스토리 Api에 대해 알아보겠습니다.
Doc App을 분석하다보니 History가 중요한 역할을 하고 있습니다.

예제를 통해 알아 보겠습니다.

    

    
// app.js
Ext.onReady(function() {
    Ext.History.init();
    var tokenDelimiter = ':';
    
    function onAfterRender() {
    	for(var i=0; i<10; i++){
//    	 Ext.util.History.add(i)
    	 Ext.History.add(i);	
    	}
    }
    
    Ext.create('Ext.TabPanel', {
        renderTo: Ext.getBody(),
        id: 'main-tabs',
        height: 300,
        width: 600,
        activeTab: 0,
        defaults: {
            padding: 10
        },
        
        items: [
        {
            title: 'Tab 1',
            id: 'tab1',
            html: 'Tab 1 content'
        },{
            title: 'Tab 2',
            id: 'tab2',
            html: 'Tab 2 content'
        },{
            title: 'Tab 3',
            id: 'tab3',
            html: 'Tab 3 content'
        },{
            title: 'Tab 4',
            id: 'tab4',
            html: 'Tab 4 content'
        },{
            title: 'Tab 5',
            id: 'tab5',
            html: 'Tab 5 content'
        }],
        listeners: {
            afterrender: onAfterRender 
        }
    });
});

 
브라우저 "뒤로가기"버튼을 클릭 해보면 주소창의 #9가 0까지 변하는 것을 볼 수 있다. Ext.History.add(i) 이 부분을 실행 할 때 마다 브라우저에 #으로 add의 인자가 표시되는 것을 알 수 있다.


이제 코드를 좀 수정해서 ExtJs가 History가 변경되었을 경우를 모니터링 해봅니다.

    function onAfterRender() {
    	 Ext.History.on('change', function(token) {	// history 가 변경되었을 경우 감시할 리스너
    		 console.log(token);
    	 });
    	for(var i=0; i<10; i++){
//    	 Ext.util.History.add(i)
    	 Ext.History.add(i);	
    	}
    }

브라우저를 통해 실행 해 봅니다. 뒤로가기 버튼을 클릭해서 console에 숫자가 찍히는지 확인 합니다. 이를 통해 extjs가 히스토리 객체에 특정 텍스트를 넣고 브라우저의 뒤로가기 버튼을 클릭하면 변경된 히스토리를 감시 할 수 있습니다.


다시 소스를 수정합니다. 이번에는 히스토리에 위의 그림에서 보이는 5개의 탭아이디를 저장하고 뒤로가기 버튼을 클릭해서 각 탭이 선택되도록 해보겠습니다. 사용자가 클릭하지 않고 히스토리를 통해 UI를 조정하는 것입니다.
    function onAfterRender() {
    	 Ext.History.on('change', function(token) {	// history 가 변경되었을 경우 감시할 리스너
    		 console.log('History id ', token);
    		 Ext.getCmp('main-tabs').setActiveTab(Ext.getCmp(token));	// history값으로 탭이 선택되도록
    	 });
    	for(var i=0; i<5; i++){
//    	 Ext.util.History.add(i)
    	 Ext.History.add('tab'+(i+1));	// 아래쪽 탭들의 아이디와 매칭되도록 
    	}
    }
1

아래 그림처럼 뒤로가기 앞으로가기 버튼을 클릭할 때 마다 해당 히스토리에 맞게 탭에 활성화 되는 것을 확인 할 수 있습니다.


이제 좀 다르게 html에 링크를 만들고 해당 링크를 통해 탭을 활성화 해보겠습니다. href="#tab1"과 같이 코딩하고 클릭하면 해당 탭이 활성화 됩니다.
   
    

tab1


tab2


tab3


tab4


tab5




이런 식으로 히스토리에 UI의 아이디 등을 저장하고 사용자가 뒤로가기 또는 앞으로 가기 버튼을 클릭하면 ExtJS는 히스토리
변화를 감지하고 변경되었을 경우 해당 로직대로 화면을 보여줄 수 있도록 코딩하면 될 듯 싶습니다.
Posted by 1 베니94
TAG ExtJS4

댓글을 달아 주세요

이제 app.js파일의 원상태를 확인 할 수 있으니 파일내용을 찬찬히 들여다 보기로 하겠습니다.
우선 메인 함수부터 찾아야 겠죠. 모든 프로그램이 그렇겠지만 맨 아래쪽에서 메인함수와 주요 클래스들을 발견할 수 있었습니다.
실마리는 여기서 부터 잡아나가면 되겠죠


그런데 좀 이상한 것은 파일 라인수 입니다. 4만5천라인이라니~ 용량도 1.3메가나 나갑니다.~ ext-all-debug.js파일이 13만라인에 4메가 정도인데 이건 뭔가 이상합니다.
해서 파일 내용을 좀 자세하게 살펴 보았더니 클래스 중에 Ext로 시작하는 클래스가 상당히 존재합니다. 아래 그림처럼 말이죠!

 


일단 예상하기로는 빌드 과정에서 용량을 최소화 하기 위해 해당 어플리케이션에서 사용하는 클래스들만 따로 골라서 app.js파일에 통체로 같이 빌드하지 않았나 싶습니다. 그래야만 전체 클래스를 내려받는것 보다 낫고 필요한 클래스만 내려 받을 수 있도록 해서 성능을 개선하기 위함이지 않나 싶습니다.  이놈들의 정체가 뭔지 확실히 알려면 Ext로 시작하는 클래스를 ext-all-debug.js와 비교해 보면 알 수 있겠죠 클래스 중 하나를 골라 ext-all-debug.js파일의 동일한 클래스와 내용이 같은지 확인 해보니 100%동일 한 것으로 확인 되었습니다.

분석하는 입장에서는 모두 불필요하니 Ext로 시작하는 클래스들은 모두 app.js파일에서 제거하였습니다. 그리고 브라우저를 통해 다시 실행해봅니다. 이상없는걸 보니 제 생각이 맞는것 같습니다. 일단 이상태의 소스를 카피하여 남겨 놓습니다.

그런데 Ext클래스들을 제거 했는데도 소스량이 아직도 상당히 많아 보여서 확인 해보니 CodeMirror라는 놈이 있습니다. 소스의 맨처음에 시작하여 4천라인정도를 사용하고 있습니다. 일단 제거 해보고 나중에 필요하다면 다시 넣는것으로 하겠습니다.

이제 소스코드 량은 4천 라인정도로 실제 필요한 내용들만 남기고 제거가 되었습니다. 이 상태의 소스도 남겨 놓습니다.

이제 하나씩 프로그램을 다시 조립해 보겠습니다.

우선 app.js의 내용을 모두 삭제합니다. index.html도 내용을 모두 정리합니다. 아래 처럼요 그래야 처음 부터 하나씩 조립해서 완성품을 만들 수 있겠죠!


이제 웹브라우저를 통해 실행해보면 아무것도 보이지 않습니다. 브라우저는 항상 파이어폭스를 쓰는게 좋습니다. 구글크롬도 좋지만 디버그 모드의 활용은 파이어폭스쪽이 좀더 나아 보입니다.
이제 본격적으로 app.js파일에 코딩을 하고 프로그램을 만들어봅니다. 제일 중요한 것은 이 어플리케이션의 layout이 어떻게 구성되어 있느냐를 파악해야 합니다. 해서 뼈대 부터 만들고 하나씩 뼈대위에 살을 붙여 보도록 하겠습니다.
아래 그림은 Doc 어플리케이션의 뼈만 앙상한 모습입니다.

Posted by 1 베니94
TAG ExtJS4

댓글을 달아 주세요

Ext JS 4.1 beta가 출시 되었습니다. 새로운 것들이 좀 있겠지만 개인적으로 Doc Application을 좀 분석 해볼려고 합니다.
Ext JS 4.1 beta 를 다운로드 받아사 압축을 풀고 톰캣에 세팅을 합니다. 압축 파일이 48메가 정도입니다.

압축을 풀고 이클립스 프로젝트를 생성해서 소스를 포팅한 모습입니다.

톰캣에 올려 실행하면 아래처럼 sencha.com과 동일한 화면을 볼 수 있습니다.


제가 분석해볼려고 하는 부분은 위의 화면에서 하단 중간의 "API Docs"를 클릭하면 실행되는 어플리케이션입니다.
이 어플리케이션에는 일반적으로 개발에 필요한 요소들이 모두 존재하고 구조적으로 잘 디자인되어 있어 이것을 차용하여 향후 다른 프로젝트에서 기본 베이스로 사용해볼까 하고 생각하고 있습니다. 아무리 생각해보아도 참 잘 디자인 되어 있습니다.
그리고 제가 풀지 못했던 몇가지 문제를 이 어플리케이션을 분석하여 해결될 수 있지 않을까 기대를 하고 있습니다.


이 강좌를 따라해보시려고 한다면 위의 화면 캡쳐에서 보시는 Toturial을 공부하시고 보시는게 좋을 듯 합니다.
Posted by 1 베니94

댓글을 달아 주세요