들어가며

이번 강좌에서는 상,하위 노드의 check/uncheck에 따라 상,하위 노드의 체크를 조작하는 방법을 배워보도록 하겠습니다.

이론

업무화면을 개발하다보면 트리구조의 메뉴화면이 필요한 경우가 있을 것입니다.
예를들어 상위노드를 체크하는 경우 하위노드를 일괄로 check/uncheck하거나 반대로 하위노드를 check/uncheck하는 경우 그 하위노드의 형제노드(sibling node)의 체크 상태에 따라 부모 노드의 체크 여부를 조작하는 것.

리얼그리드에서는 아래의 두가지 방법을 이용하여 해당 업무를 처리할 수 있습니다.

  1. 셀 렌더러중 하나인 CheckCellRenderer를 이용하는 방법
  2. 그리드의 왼쪽에 위치한 CheckBar를 이용하는 방법

이전 강좌인 C20 Tree그리드에서 체크셀렌더러 사용에서는 1. 체크 셀 렌더러를 이용하여 트리그리드의 상,하위 노드를 조작해보았습니다. 이번 강좌에서는 2번인 CheckBar를 이용하여 트리그리드의 상,하위 노드를 조작해보도록 하겠습니다.

이번 강좌는 이전 강좌와 크게 다르지 않습니다. 체크를 하는 곳이 CheckBar영역이기에 해당 영역을 체크하는 API와 체크여부를 확인하는 API만 교체하면 됩니다. C20 Tree그리드에서 체크셀렌더러 사용, CheckBar 데모를 먼저 확인하신 후 해당 강좌를 진행하시기 바랍니다.

컬럼과 필드는 아래와 같이 설정합니다.

//필드 배열 객체를 생성합니다.
var fields = [{
    "fieldName": "treeNode"
}, {
    "fieldName": "menuName"
}, {
    "fieldName": "bigo"
}];

dataProvider.setFields(fields);

//필드와 연결된 컬럼 배열 객체를 생성합니다.
var columns = [
    "name": "menuName",
    "fieldName": "menuName",
    "width": 350,
    "header": {
        "text": "메뉴명"
    }       
}, {
    "name": "bigo",
    "fieldName": "bigo",
    "width": 150,
    "header": {
        "text": "비고"
    }
}];

gridView.setColumns(columns);

treeNode필드는 트리 노드의 구성정보가 들어갈 필드입니다. 화면에 표시되는 필드는 menuName, bigo 필드 입니다.

사용할 예제의 데이터는 아래와 같은 형태 입니다.

var data = [
  ["001", "영업관리", ""],
  ["001.001", "영업관리1-중분류", ""],
  ["001.001.001", "영업관리1-소분류1", ""],
  ["001.001.002", "영업관리1-소분류2", ""],
  ["001.001.003", "영업관리1-소분류3", ""],
  ["002", "영업회계", ""],
  ["002.001", "영업회계1-중분류", ""],
  ["002.001.001", "영업회계1-소분류1", ""],
  ["002.001.002", "영업회계1-소분류2", ""],
  ["002.002", "영업회계2-중분류", ""],
  ["002.002.001", "영업회계2-소분류1", ""],
  ["002.002.002", "영업회계2-소분류2", ""],
  ["002.002.003", "영업회계2-소분류3", ""],
  ["002.002.004", "영업회계2-소분류4", ""]
];

dataProvider.setRows(data, "treeNode", true, "", "");

이렇게 표시된 그리드에서 체크바를 클릭하여 체크하는 경우 트리 그리드에서는 onItemChecked() 이벤트가 발생하게 됩니다. 해당 이벤트 안에서 체크된 값에 따라 각 노드를 확인하고 상,하위 노드의 체크를 제어하면 됩니다. 여기서 주의해야 할 것은 onItemChecked() 이벤트는 사용자가 CheckBar를 체크할때 발생하지만 개발자가 API를 사용해서 체크할때에도 발생하므로 API를 이용하여 체크하는 경우 반드시 해당 API의 전달인자 중 하나인 checkEvent를 false로 지정해야 합니다.

실습에서는 아래 API들을 이용하여 노드정보를 획득하고 체크값들을 제어해보도록 하겠습니다. Tree그리드에 접혀있는 노드들이 있는 경우에는 해당 노드들의 itemIndex를 획득할 수 없기 때문에 itemIndex로 조작하는 API는 거의 사용하지 않습니다.

CheckBar의 체크와 관련된 API들

  1. 모두체크: gridBase.checkAll()
  2. 한행체크(itemIndex): gridBase.checkItem()
  3. 여러행체크(itemIndex): gridBase.checkItems()
  4. 한행체크(dataRow): gridBase.checkRow()
  5. 여러행체크(dataRow): gridBase.checkRows()
  6. 체크여부확인(itemIndex): gridBase.isCheckedItem()
  7. 체크여부확인(dataRow): gridBase.isCheckedRow()

CheckBar.head영역과 관련된 API들

  1. 체크표시활성화여부: gridBase.isAllChecked()
  2. 체크: gridBase.setAllCheck()

부모/자식 ID를 가져오는 API들

  1. 조상: dataProvider.getAncestors()
  2. 부모: dataProvider.getParent()
  3. 자식: dataProvider.getChildren()
  4. 자손: dataProvider.getDescendants()

실습

  1. CheckNode() 정의하기
    CheckBar.head영역의 V 표시 제어하는 코드가 추가되었을뿐 이전 강좌와 크게 다른 점은 없습니다.

     function checkNode(grid, dataRow, checked) {
         var provider = grid.getDataSource();
    
         //형제노드체크 후 부모노드 체크
         checkSiblingNode(grid, dataRow, checked);
    
         //자식노드체크
         var desRows = provider.getDescendants(dataRow);
         if (desRows) {
             grid.checkRows(desRows, checked, false);
         }            
     };
    
     function checkSiblingNode(grid, dataRow, checked) {
         var provider = grid.getDataSource();
         //부모노드
         var parent = provider.getParent(dataRow);
         //형제노드
         var sibling = provider.getChildren(parent);
         var index = sibling.indexOf(dataRow);
         //자기자신은 제외
         if (index !== -1) sibling.splice(index, 1);
    
         if (checked) {
             for (var i in sibling) {
                 var value = grid.isCheckedRow(sibling[i]);
    
                 if (checked != value) {
                     checked = false;
                     break;
                 }
             }
         } else {
             checked = false;
         }           
    
         grid.checkRow(parent, checked, false, false);
    
         //checkBar.head 영역의 V표시 제어
         if (parent == -1) grid.setAllCheck(checked);
    
         if (parent > -1) checkSiblingNode(grid, parent, checked);
     }
     
  2. onItemChecked() 안에 CheckNode 적용하기

     $("#btnSetOnItemChecked").click(function () {
         function checkNode(grid, dataRow, checked) {
             var provider = grid.getDataSource();
    
             //형제노드체크 후 부모노드 체크
             checkSiblingNode(grid, dataRow, checked);
    
             //자식노드체크
             var desRows = provider.getDescendants(dataRow);
             if (desRows) {
                 grid.checkRows(desRows, checked, false);
             }            
         };
    
         function checkSiblingNode(grid, dataRow, checked) {
             var provider = grid.getDataSource();
             //부모노드
             var parent = provider.getParent(dataRow);
             //형제노드
             var sibling = provider.getChildren(parent);
             var index = sibling.indexOf(dataRow);
             //자기자신은 제외
             if (index !== -1) sibling.splice(index, 1);
    
             if (checked) {
                 for (var i in sibling) {
                     var value = grid.isCheckedRow(sibling[i]);
    
                     if (checked != value) {
                         checked = false;
                         break;
                     }
                 }
             } else {
                 checked = false;
             }           
    
             grid.checkRow(parent, checked, false, false);
    
             //checkBar.head 영역의 V표시 제어
             if (parent == -1) grid.setAllCheck(checked);
    
             if (parent > -1) checkSiblingNode(grid, parent, checked);
         }
    
         gridView.onItemChecked = function (grid, itemIndex, checked) {
             var dataRow = grid.getDataRow(itemIndex);
             checkNode(grid, dataRow, checked);
         }; 
     });
  3. 체크바의 체크박스를 클릭하여 체크박스 값이 잘 연동되는지 확인하세요.

실행화면

  1. CheckNode() 정의하기

  2. 체크바의 체크박스를 클릭하여 체크박스 값이 잘 연동되는지 확인하세요.

전체 소스코드

SCRIPT
<script type="text/javascript" src="/script/realgridjs-lic.js"></script>
<script type="text/javascript" src="/script/realgridjs_eval.1.1.27.min.js"></script>
<script type="text/javascript" src="/script/realgridjs-api.1.1.27.js"></script>

<script>

var gridView;
var dataProvider;

$(document).ready( function(){
    RealGridJS.setTrace(false);
    RealGridJS.setRootContext("/script");
    
    dataProvider = new RealGridJS.LocalTreeDataProvider();
    gridView = new RealGridJS.TreeView("realgrid");
    gridView.setDataSource(dataProvider);

    //필드 배열 객체를 생성합니다.
    var fields = [{
        "fieldName": "treeNode"
    }, {
        "fieldName": "menuName"
    }, {
        "fieldName": "bigo"
    }];
    //DataProvider의 setFields함수로 필드를 입력합니다.
    dataProvider.setFields(fields);

    //필드와 연결된 컬럼 배열 객체를 생성합니다.
    var columns = [{
        "name": "menuName",
        "fieldName": "menuName",
        "width": 350,
        "header": {
            "text": "메뉴명"
        }       
    }, {
        "name": "bigo",
        "fieldName": "bigo",
        "width": 150,
        "header": {
            "text": "비고"
        }
    }];
    //컬럼을 GridView에 입력 합니다.
    gridView.setColumns(columns);

    var data = [
      ["001", "영업관리", ""],
      ["001.001", "영업관리1-중분류", ""],
      ["001.001.001", "영업관리1-소분류1", ""],
      ["001.001.002", "영업관리1-소분류2", ""],
      ["001.001.003", "영업관리1-소분류3", ""],
      ["002", "영업회계", ""],
      ["002.001", "영업회계1-중분류", ""],
      ["002.001.001", "영업회계1-소분류1", ""],
      ["002.001.002", "영업회계1-소분류2", ""],
      ["002.002", "영업회계2-중분류", ""],
      ["002.002.001", "영업회계2-소분류1", ""],
      ["002.002.002", "영업회계2-소분류2", ""],
      ["002.002.003", "영업회계2-소분류3", ""],
      ["002.002.004", "영업회계2-소분류4", ""]
    ];

    dataProvider.setRows(data, "treeNode", true, "", "");

    gridView.expandAll();   


    $("#btnSetOnItemChecked").click(function () {
        function checkNode(grid, dataRow, checked) {
            var provider = grid.getDataSource();

            //형제노드체크 후 부모노드 체크
            checkSiblingNode(grid, dataRow, checked);

            //자식노드체크
            var desRows = provider.getDescendants(dataRow);
            if (desRows) {
                grid.checkRows(desRows, checked, false);
            }            
        };

        function checkSiblingNode(grid, dataRow, checked) {
            var provider = grid.getDataSource();
            //부모노드
            var parent = provider.getParent(dataRow);
            //형제노드
            var sibling = provider.getChildren(parent);
            var index = sibling.indexOf(dataRow);
            //자기자신은 제외
            if (index !== -1) sibling.splice(index, 1);

            if (checked) {
                for (var i in sibling) {
                    var value = grid.isCheckedRow(sibling[i]);

                    if (checked != value) {
                        checked = false;
                        break;
                    }
                }
            } else {
                checked = false;
            }           

            grid.checkRow(parent, checked, false, false);

            //checkBar.head 영역의 V표시 제어
            if (parent == -1) grid.setAllCheck(checked);

            if (parent > -1) checkSiblingNode(grid, parent, checked);
        }

        gridView.onItemChecked = function (grid, itemIndex, checked) {
            var dataRow = grid.getDataRow(itemIndex);
            checkNode(grid, dataRow, checked);
        }; 
    });    

});   

</script>
HTML
<div id="realgrid" style="width: 90%; height: 380px;"></div>
<p></p>

1. CheckNode() 정의하기    

2. <button type="button" class="btn btn-primary btn-xs" id="btnSetOnItemChecked">onItemChecked() 안에 CheckNode 적용하기</button>    

3. 체크바의 체크박스를 클릭하여 체크박스 값이 잘 연동되는지 확인하세요.  

<p></p>

참조