들어가며

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

이론

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

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

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

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

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

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

dataProvider.setFields(fields);

//필드와 연결된 컬럼 배열 객체를 생성합니다.
var columns = [
    "name": "menuName",
    "fieldName": "menuName",
    "width": 350,
    "header": {
        "text": "메뉴명"
    }       
}, {
    "name": "auth",
    "fieldName": "auth",
    "width": 150,
    "header": {
        "text": "권한여부"
    },
    "editable": false,
    "renderer": {
        "type": "check",
        "shape": "box",
        "editable": true,
        "startEditOnClick": true,
        "trueValues": "Y",
        "falseValues": "N"
    },
}];

gridView.setColumns(columns);

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

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

var data = [
  ["0", "\\", "Y"],
  ["0.001", "영업관리", "Y"],
  ["0.001.001", "영업관리1-중분류", "Y"],
  ["0.001.001.001", "영업관리1-소분류1", "Y"],
  ["0.001.001.002", "영업관리1-소분류2", "Y"],
  ["0.001.001.003", "영업관리1-소분류3", "Y"],
  ["0.002", "영업회계", "Y"],
  ["0.002.001", "영업회계1-중분류", "Y"],
  ["0.002.001.001", "영업회계1-소분류1", "Y"],
  ["0.002.001.002", "영업회계1-소분류2", "Y"],
  ["0.002.002", "영업회계2-중분류", "Y"],
  ["0.002.002.001", "영업회계2-소분류1", "Y"],
  ["0.002.002.002", "영업회계2-소분류2", "Y"],
  ["0.002.002.003", "영업회계2-소분류3", "Y"],
  ["0.002.002.004", "영업회계2-소분류4", "Y"]
];

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

이렇게 표시된 그리드에서 auth(권한여부) 컬럼셀을 클릭하여 값을 변경하는 경우 트리 그리드에서는 onCellEdited() 이벤트가 발생하게 됩니다. 해당 이벤트 안에서 체크된 값에 따라 각 노드를 확인하고 상,하위 노드의 체크를 제어하면 됩니다.

실습에서는 아래 API들을 이용하여 노드정보를 획득하고 체크값들을 제어해보도록 하겠습니다.

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

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

실습

  1. 화면에 표시되고 있는 “권한여부” 컬럼의 체크박스를 클릭하여 체크값 변경 후 다른 행의 expander(+/-)를 클릭해보세요. 체크한 행이 현재 편집중이기에 expander가 동작하지 않습니다.

  2. 체크한 값을 commit()시켜 편집상태를 종료하려면 값이 편집되었을때 발생하는 onCellEdited()안에 아래와 같이 grid.commit()을 추가하여야 합니다.

     //셀 편집 후 commit()
     $("#btnSetOnCellEdited").click(function () {
         gridView.onCellEdited = function (grid, itemIndex, dataRow, field) {
             grid.commit(true);
         }; 
     });
  3. “권한여부” 컬럼의 체크박스를 클릭하여 체크값 변경 후 다른 행의 expander(+/-)를 클릭해보세요. 잘 동작합니다.

  4. “auth” 컬럼을 체크하였을때 하위자손 노드들은 현재의 체크값과 같은 값을 set 해주면 됩니다. 상위 노드들은 체크한 형제노드들의 값이 모두 같으면 체크해주고 하나라도 다르면 체크해제를 set 해주면 됩니다. 해당 코드는 아래와 같습니다.

     //체크셀렌더러 체크 제어
     function checkNode(provider, dataRow, checked) {
         //형제노드체크 후 부모노드 체크
         checkSiblingNode(provider, dataRow, checked);
    
         //자손노드체크
         var desRows = provider.getDescendants(dataRow);
         for (var i in desRows) {
             dataProvider.setValue(desRows[i], "auth", checked);
         }
     };
    
     function checkSiblingNode(provider, dataRow, checked) {
         //부모노드
         var parent = provider.getParent(dataRow);
         //형제노드들
         var sibling = provider.getChildren(parent);
         var index = sibling.indexOf(dataRow);
         //자기자신은 제외
         if (index !== -1) sibling.splice(index, 1);
    
         if (checked == "Y") {
             var result = true;
             for (var i in sibling) {
                 var value = dataProvider.getValue(sibling[i], "auth");
    
                 if (checked != value) {
                     result = false;
                     break;
                 }
             }
             checked = result ? "Y" : "N";
         } else {
             checked = "N";
         }
    
         provider.setValue(parent, "auth", checked);
    
         if (parent > -1) checkSiblingNode(provider, parent, checked);
     }
     
  5. onCellEdited()안에 위에서 작성한 코드를 추가합니다.

     $("#btnSetOnCellEdited1").click(function () {
         //체크셀렌더러 체크 제어
         function checkNode(provider, dataRow, checked) {
             //형제노드체크 후 부모노드 체크
             checkSiblingNode(provider, dataRow, checked);
    
             //자손노드체크
             var desRows = provider.getDescendants(dataRow);
             for (var i in desRows) {
                 dataProvider.setValue(desRows[i], "auth", checked);
             }
         };
    
         function checkSiblingNode(provider, dataRow, checked) {
             //부모노드
             var parent = provider.getParent(dataRow);
             //형제노드들
             var sibling = provider.getChildren(parent);
             var index = sibling.indexOf(dataRow);
             //자기자신은 제외
             if (index !== -1) sibling.splice(index, 1);
    
             if (checked == "Y") {
                 var result = true;
                 for (var i in sibling) {
                     var value = dataProvider.getValue(sibling[i], "auth");
    
                     if (checked != value) {
                         result = false;
                         break;
                     }
                 }
                 checked = result ? "Y" : "N";
             } else {
                 checked = "N";
             }
    
             provider.setValue(parent, "auth", checked);
    
             if (parent > -1) checkSiblingNode(provider, parent, checked);
         };
    
         gridView.onCellEdited = function (grid, itemIndex, dataRow, field) {
             grid.commit(true);
    
             var provider = grid.getDataSource();
    
             var fieldName = dataProvider.getOrgFieldName(field);
             if (fieldName == 'auth') {
                 checkNode(provider, dataRow, grid.getValue(itemIndex, field));
             }
         }; 
     });

실행화면

  1. 화면에 표시되고 있는 “권한여부” 컬럼의 체크박스를 클릭하여 체크값 변경 후 다른 행의 expander(+/-)를 클릭해보세요. 체크한 행이 현재 편집중이기에 expander가 동작하지 않습니다.

  2. 버튼을 클릭하여 onCellEdited() 안에 commit() 적용하기

  3. 화면에 표시되고 있는 “권한여부” 컬럼의 체크박스를 클릭하여 체크값 변경 후 다른 행의 expander(+/-)를 클릭해보세요. 잘 동작합니다.

  4. CheckNode() 정의하기

  5. “권한여부” 컬럼의 체크박스를 클릭하여 체크박스 값이 잘 연동되는지 확인하세요.

전체 소스코드

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": "auth"
    }];
    //DataProvider의 setFields함수로 필드를 입력합니다.
    dataProvider.setFields(fields);

    //필드와 연결된 컬럼 배열 객체를 생성합니다.
    var columns = [{
        "name": "menuName",
        "fieldName": "menuName",
        "width": 350,
        "header": {
            "text": "메뉴명"
        }       
    }, {
        "name": "auth",
        "fieldName": "auth",
        "width": 150,
        "header": {
            "text": "권한여부"
        },
        "editable": false,
        "renderer": {
            "type": "check",
            "shape": "box",
            "editable": true,
            "startEditOnClick": true,
            "trueValues": "Y",
            "falseValues": "N"
        }
    }];
    //컬럼을 GridView에 입력 합니다.
    gridView.setColumns(columns);

    var data = [
      ["0", "\\", "Y"],
      ["0.001", "영업관리", "Y"],
      ["0.001.001", "영업관리1-중분류", "Y"],
      ["0.001.001.001", "영업관리1-소분류1", "Y"],
      ["0.001.001.002", "영업관리1-소분류2", "Y"],
      ["0.001.001.003", "영업관리1-소분류3", "Y"],
      ["0.002", "영업회계", "Y"],
      ["0.002.001", "영업회계1-중분류", "Y"],
      ["0.002.001.001", "영업회계1-소분류1", "Y"],
      ["0.002.001.002", "영업회계1-소분류2", "Y"],
      ["0.002.002", "영업회계2-중분류", "Y"],
      ["0.002.002.001", "영업회계2-소분류1", "Y"],
      ["0.002.002.002", "영업회계2-소분류2", "Y"],
      ["0.002.002.003", "영업회계2-소분류3", "Y"],
      ["0.002.002.004", "영업회계2-소분류4", "Y"]
    ];

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

    gridView.expandAll();   

    //셀 편집 후 commit()
    $("#btnSetOnCellEdited").click(function () {
        gridView.onCellEdited = function (grid, itemIndex, dataRow, field) {
            grid.commit(true);
        }; 
    });

    $("#btnSetOnCellEdited1").click(function () {
        function checkNode(provider, dataRow, checked) {
            //형제노드체크 후 부모노드 체크
            checkSiblingNode(provider, dataRow, checked);

            //자식노드체크
            var desRows = provider.getDescendants(dataRow);
            for (var i in desRows) {
                dataProvider.setValue(desRows[i], "auth", checked);
            }
        };

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

            if (checked == "Y") {
                var result = true;
                for (var i in sibling) {
                    var value = dataProvider.getValue(sibling[i], "auth");

                    if (checked != value) {
                        result = false;
                        break;
                    }
                }
                checked = result ? "Y" : "N";
            } else {
                checked = "N";
            }

            provider.setValue(parent, "auth", checked);

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

        gridView.onCellEdited = function (grid, itemIndex, dataRow, field) {
            grid.commit(true);

            var provider = grid.getDataSource();

            var fieldName = dataProvider.getOrgFieldName(field);
            if (fieldName == 'auth') {
                checkNode(provider, dataRow, grid.getValue(itemIndex, field));
            }
        }; 
    });    

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

1. 화면에 표시되고 있는 "권한여부" 컬럼의 체크박스를 클릭하여 체크값 변경 후 다른 행의 expander(+/-)를 클릭해보세요. 체크한 행이 현재 편집중이기에 expander가 동작하지 않습니다.

2. <button type="button" class="btn btn-primary btn-xs" id="btnSetOnCellEdited">grid.commit()</button> 버튼을 클릭하여 onCellEdited() 안에 commit() 적용하기

3. 화면에 표시되고 있는 "권한여부" 컬럼의 체크박스를 클릭하여 체크값 변경 후 다른 행의 expander(+/-)를 클릭해보세요. 잘 동작합니다. 

4. CheckNode() 정의하기    

5. <button type="button" class="btn btn-primary btn-xs" id="btnSetOnCellEdited1">onCellEdited() 안에 CheckNode 적용하기</button>    

6. "권한여부" 컬럼의 체크박스를 클릭하여 체크박스 값이 잘 연동되는지 확인하세요.  

참조