티스토리 뷰

개발환경 : nodejs / mariaDB / JS / jqgrid 

저번 포스팅에서 화면에 grid 조회를 구현했다.
https://daydreamer-92.tistory.com/106

이제 수정이 가능하게 해보자.
수정하는 방법은 
1. 체크박스 클릭해서 수정버튼 클릭하고 모달 띄워서 수정하는 방법
2. 수정하고싶은 row 클릭해서 그 row 데이터 전부 수정 후 저장하는 방법
    : inline 모드인것 같은데 이거는 cellsubmit 프로퍼티를 clientArray로 주어서 어떻게 해야하는 것 같고
3. 수정하고 싶은 cell 클릭해서 셀 하나하나 단독으로 수정하는 방법
이 정도로 생각을 해봤는데

내가 구현할 것은 3번으로 cell 하나하나를 각각 수정하는 방식으로 클릭시 수정 input으로 바뀌게 되고
수정 후 엔터를 치면 바로 DB에 반영하여 update 되는 방식으로 만들려고 한다.

그러기 위해서 그리드 부분을 먼저 수정해주자 

    $("#dataGrid").jqGrid({
        url:'/data/menumast/getGridData',
        datatype:'JSON',
        mtype:'POST',
        postData : {
            system:$('#txtSystem').val()
        },
        loadtext  : "로딩중...",
        height: 500, 
        width: $('.main-panel').width()-60, 
        colNames: gridColNm, 
        colModel: gridColModel, 
        rowNum : 99999, 
        rownumbers : true, //자동으로 번호 부여 
        cellEdit:true, //그리드 수정 가능 기능
        cellsubmit : 'remote',
        cellurl : '/data/menumast/updateGridData'
        
      });
      
    $(window).on('resize.jqGrid', function () {
        jQuery("#dataGrid").jqGrid( 'setGridWidth', $(".main-panel").width() - 100 );
    });
    
    getGridData();

});

저번에 조회때 생성한 그리드에 옵션들을 추가해준다.
cellEdit : true
cellsubmit : 'remote'
cellurl : ' '
이 옵션들을 설정해서 셀을 편집할 수 있게 해 주고 remote 방식으로 하나의 cell을 바로바로 수정가능하게 해준다. 
cellurl은 수정시에 서버로 요청하는 url이다.

이렇게 수정하고 colModel의 속성에 편집하고 싶은 항목에 editable : true 를 설정해주면
그리드 클릭시 input 박스로 수정이 가능해진다.

그런데 그렇게 하면 클릭시마다 input이 계속 뜨게되서 어지러운 상황(?)이 발생하기 때문에 
셀을 더블클릭시 수정가능하도록 해주려고 한다.

그래서 나는 colModel속성에 editable : true 속성을 처음 선언부터 주지 않았다.

수정하는 이벤트를 추가해보도록 하자. 

	/* 더블클릭시 수정 가능*/
        ondblClickRow : function (rowid, iRow, iCol){
          
          const colModels = $(this).getGridParam('colModel'); 
          const colName = colModels[iCol].name;
         
          /* prog_id(PK), chkbox 수정불가 */
          if(!(colName=='prog_id'||colName=='chkbox')){
            $(this).setColProp(colName, {editable : true}); //gridColModel의 name값을 수정가능하게 해줌 
            $(this).editCell(iRow, iCol, true); 
          }
        },

        afterEditCell : function(rowid, cellname, value, iRow, iCol){
            $("#"+rowid+"_"+cellname).blur(function(){ //input focus out시 변경된 데이터 저장 
              $("#").jqGrid("saveCell", iRow, iCol);
            });
            $(this).setColProp(cellname, {editable : false}); //수정 후 cell 다시 editable flase 로 변경
        },

 

ondbClickRow
: 이 이벤트를 사용하면 셀을 더블클릭 했을 때 편집 input이 생성된다.

$(this).setColProp(colName, {editable : true}); 
$(this).editCell(iRow, iCol, true);

이렇게 해주면 colName이 수정가능한데 나는 prog_id가 PK라 수정되면 안돼고, 체크박스도 수정안되게 하기 위해서
클릭한 셀의 이름이 이 두개가 아닌 경우만 editable : true 옵션을 주었다.

수정을하고 난 뒤에 엔터를 치면 submit 전송 전에 editable : true 했던 것을 다시 false로 주어야하는데
afterEditCell 이벤트를 사용 해 주면 된다.
(이거 안해주면 수정 후에 다른 셀 수정시에 더블클릭이 아니라 클릭으로 수정 input이 열려버림)

수정을 했으니 이제 전송전과 전송 후에 이벤트를 주자.
beforeSubmitCell 이벤트를 사용하여 서버에 전송할 데이터를 추가할 수 있다. 주석을 읽어보면 이해할 수 있겠지만 지금 내가 구현하고 있는것은 메뉴이므로 메뉴보임 yn 을 수정할때 셀이 중메뉴인 경우에는 하위 메뉴들도 함께 변경시켜줘야하고, 메뉴가림 상태에서 하위메뉴를 보이게 할 때 중메뉴도 보이게 변경시켜줘야 하기 때문에 체크해서 데이터를 추가해줬다. json데이터로 리턴 해주면 서버에 전송되는 데이터 param에 함께 추가되어 전송된다. 

 

        beforeSubmitCell : function (rowid, cellname, value, iRow, iCol){ /* url 전송 전 데이터 추가 부분 */
            /* 데이터 수정 : 메뉴 보임에서 레벨에 따라 하위 메뉴 가리는 기능 
                1. if cellname=view_yn 이면 row data 를 가져오기
                2. row data => depth_lv==='1'이면
                3. Row data의 group_code가 같은 (depth_lv==='1'이 아닌) row data들을 찾아서 json 데이터로 저장 
            */
            const progIdObj = new Object();
            const progIdArr =[];
            const rowData = $("#dataGrid").getRowData();
            if(cellname==='view_yn' && rowData[iRow-1].depth_lv==='1'){
                
                for(var key in rowData){
                    if(rowData[key].group_code===rowData[iRow-1].group_code && rowData[key].depth_lv!=='1'){
                        progIdArr.push(rowData[key].prog_id); //prog_id 배열로 저장
                    }
                }
                progIdObj.progidArr = progIdArr;
            }
            /* 데이터 수정 : 중메뉴 view_yn=N인데 하위 메뉴 view_yn=Y로 고칠 때 중메뉴 보이게 설정
               수정하려는 값이 view_yn이고 값이 Y이고 depth_lv이 1이 아닐 때 (하위메뉴일때)
               group_code값이 같고 depth_lv이 1인 row의 view_yn이 N이면 (중메뉴 안보이는 상황) 그 값을 넘겨서 변경시켜준다 
             */ 
            if(cellname==='view_yn' && value==='Y' && rowData[iRow-1].depth_lv!=='1'){
                for(var idx in rowData){
                    if(rowData[idx].group_code===rowData[iRow-1].group_code && rowData[idx].depth_lv==='1' && rowData[idx].view_yn==='N'){
                        progIdArr.push(rowData[idx].prog_id); //prog_id 배열로 저장
                    }
                }
                progIdObj.progidArr = progIdArr;
            }

            return progIdObj;
        },
        
        ====> 에러 수정 전 
        afterSubmitCell : function (serverresponse, rowid, name, val, iRow, iCol){
            if(response.responseText==='success'){
                getGridData(); //grid reload
            }else{
                alert("에러 발생 !\nerrMsg : "+response.responseText);
            } 
        }
        ====> 이걸로 바꿔줌 
        afterSubmitCell : function (serverresponse, rowid, name, val, iRow, iCol){
          if(serverresponse.responseText=='success'){
                return [true, ""];
            }else{
                return [false, alert("에러발생 :"+ serverresponse.responseText)];
            } 
        }

전송 후 afterSubmitCell 이나 afterSaveCell을 사용하면 되는데 afterSaveCell은 성공적으로 저장하고 난 뒤 호출된다고 하길래 afterSubmitCell을 사용해서 성공시에는 그리드를 reload하고 실패시에는 오류 메세지를 띄워줄 수 있게 했다.
=> 수정
성공/실패 결과를 주고싶어서 afterSubmitCell을 사용한건데 이걸 사용하면 TypeError: Cannot read properties of undefined (reading '0') 이 에러가 자꾸 발생한다. afterSubmitCell 이후에 배열[0]번째를 못가져온다고 하는데 오류 해결을 어떻게 해야할지 지금 모르는 상황이라 일단 afterSaveCell로 변경했다. 이거 오류 해결하면 다시 업데이트 해야지
=> 오류 찾음
일단 오류라고 하기가 애매했던게 afterSubmitCell을 사용하면 return값으로 [false, " "] 이런식으로 데이터를 넘겨줘야하는데 내가 리턴값을 안주고 있어서 자꾸 값을 못받아오니까 에러가 났던것.. 근데 오류가 났을때 에러메세지를 [] 데이터로 리턴해주면 화면 한쪽에 그냥 에러메세지가 달랑 떠버리는 현상이 발생함;;; 나는 이거 alert으로 처리하고 싶은데 어떻게 해야하지..
=> 해결
json 데이터를 return 하면 jqGrid에서 제공하는 dialog가 자동으로 띄워지는데 이게 나는 맘에 안듦.. alert으로 띄우고 싶다! 그래서 return [false, alert("에러발생: " + serverresponse.responseText)] 이렇게 주게 되면 에러메세지가 alert으로 띄워진다. 하지만? err message dialog도 또 한쪽에 나와버리는 상황;;; 찾아봤는데 결국 hide 해주는게 젤 빠를것 같..아서.. 
위에 변경 한 소스로 적용 한 후에 err message dialog 에 focus시 hide 해주는 이벤트를 추가로 설정 해 주었다.

//수정 에러시 발생하는 err_message dialog 숨김 
$(document).on("focus", "#info_dialog", function () {
    $("#info_dialog").hide();
});

 

스크립트 부분 전체소스 

더보기
$(function() {
    var gridColNm = ['','그룹코드','프로그램 ID', '메뉴레벨', '프로그램명','프로그램 URL','정렬순서', '메뉴보임 YN']; 
    var gridColModel = [ 
    	{name:'chkbox', index:'chkbox', align:'center', width:'3%', edittype:'checkbox', editoptions:{value:'Y:N'}, formatter:'checkbox', formatoptions: {disabled: false}},
    	{name:'group_code', index:'group_code', align:'center', width:'7%',edittype:'text'},
        {name:'prog_id', index:'prog_id', align:'center', width:'10%', edittype:'text' key:true},
        {name:'depth_lv', index:'depth_lv', align:'center', width:'5%',  edittype:'text'},
        {name:'prog_name', index:'prog_name', align:'left', width:'25%',  edittype:'text'},
        {name:'prog_url', index:'prog_url', align:'left', width:'30%', edittype:'text'},
        {name:'order_no', index:'order_no', align:'center', width:'5%', edittype:'text', },
        {name:'view_yn', index:'view_yn', align:'center', width:'5%',  edittype:'select', editoptions:{value:{"Y":"Y","N":"N"}}, formatter:'select'}
        ]; 
    
    $("#dataGrid").jqGrid({
        url:'/data/menumast/getGridData',
        datatype:'JSON',
        mtype:'POST',
        postData : {
            system:$('#txtSystem').val()
        },
        loadtext  : "로딩중...",
        height: 500, 
        width: $('.main-panel').width()-60, 
        colNames: gridColNm, 
        colModel: gridColModel, 
        rowNum : 99999, 
        rownumbers : true, //자동으로 번호 부여 
        sortable:true, 
        cellEdit:true, //그리드 수정 가능 기능
        cellsubmit : 'remote',
        cellurl : '/data/menumast/updateGridData',
      
        /* 더블클릭시 수정 가능*/
        ondblClickRow : function (rowid, iRow, iCol){
          
          const colModels = $(this).getGridParam('colModel'); 
          const colName = colModels[iCol].name;
          /* 
            prog_id(PK), chkbox 수정불가
            view_yn이 변경되는 경우 depth_lv이 1인 경우에 group_code가 같은 값은 모두 변경 해 주어야 함 
           */
          if(!(colName=='prog_id'||colName=='chkbox')){
            $(this).setColProp(colName, {editable : true}); //gridColModel의 name값을 수정가능하게 해줌 
            $(this).editCell(iRow, iCol, true); 
          }
        },
        /* 수정 후 cell 다시 editable flase 로 변경 */
        afterEditCell : function(rowid, cellname, value, iRow, iCol){
            $("#"+rowid+"_"+cellname).blur(function(){ //input focus out시 변경된 데이터 저장 
              $("#").jqGrid("saveCell", iRow, iCol);
            });
            $(this).setColProp(cellname, {editable : false}); //수정 후 cell 다시 editable flase 로 변경
        },
        beforeSubmitCell : function (rowid, cellname, value, iRow, iCol){ /* url 전송 전 데이터 추가 부분 */
            /* 데이터 수정 : 메뉴 보임에서 레벨에 따라 하위 메뉴 가리는 기능 
                1. if cellname=view_yn 이면 row data 를 가져오기
                2. row data => depth_lv==='1'이면
                3. Row data의 group_code가 같은 (depth_lv==='1'이 아닌) row data들을 찾아서 json 데이터로 저장 
            */
            const progIdObj = new Object();
            const progIdArr =[];
            const rowData = $("#dataGrid").getRowData();
            if(cellname==='view_yn' && rowData[iRow-1].depth_lv==='1'){
                
                for(var key in rowData){
                    if(rowData[key].group_code===rowData[iRow-1].group_code && rowData[key].depth_lv!=='1'){
                        progIdArr.push(rowData[key].prog_id); //prog_id 배열로 저장
                    }
                }
                progIdObj.progidArr = progIdArr;
            }
            /* 데이터 수정 : 중메뉴 view_yn=N인데 하위 메뉴 view_yn=Y로 고칠 때 중메뉴 보이게 설정
               수정하려는 값이 view_yn이고 값이 Y이고 depth_lv이 1이 아닐 때 (하위메뉴일때)
               group_code값이 같고 depth_lv이 1인 row의 view_yn이 N이면 (중메뉴 안보이는 상황) 그 값을 넘겨서 변경시켜준다 
             */ 
            if(cellname==='view_yn' && value==='Y' && rowData[iRow-1].depth_lv!=='1'){
                for(var idx in rowData){
                    if(rowData[idx].group_code===rowData[iRow-1].group_code && rowData[idx].depth_lv==='1' && rowData[idx].view_yn==='N'){
                        progIdArr.push(rowData[idx].prog_id); //prog_id 배열로 저장
                    }
                }
                progIdObj.progidArr = progIdArr;
            }

            return progIdObj;
        },
        afterSubmitCell : function (serverresponse, rowid, name, val, iRow, iCol){
          if(serverresponse.responseText=='success'){
                return [true, ""];
            }else{
                return [false, alert("에러발생 :"+ serverresponse.responseText)];
            } 
        }
      });

    $(window).on('resize.jqGrid', function () {
        jQuery("#dataGrid").jqGrid( 'setGridWidth', $(".main-panel").width() - 100 );
    });
    
    getGridData();

});

function getGridData() {
    $('#dataGrid').setGridParam({
        postData : {system:$('#txtSystem').val()}
    });
    
    //reload 
    $('#dataGrid').trigger('reloadGrid');
}

//수정 에러시 발생하는 err_message dialog 숨김 
$(document).on("focus", "#info_dialog", function () {
    $("#info_dialog").hide();
});

 

이제 라우터 부분을 구현해주자.
수정을 하고 엔터를 치게되면 위에 cellurl 부분에 설정해준 url을 호출하게 된다.

/* MENUMAST GRID UPDATE
    cell 하나하나 바로 수정이 가능
    prog_id(PK) 수정 불가
    view_yn이 변경되는 경우 depth_lv이 1인 경우에 group_code가 같은 값은 모두 변경 해 주어야 함 
*/
router.post('/data/menumast/updateGridData', (req, res) => {
    const body = req.body;
    let sql="";
   
    const key_arr = Object.keys(body);
    const value_arr = Object.values(body);

    // console.log(body);
    if(body["view_yn"]&&body["progidArr"]){
        const val_arr = Object.values(body.progidArr);
        sql = " UPDATE menumast set " + key_arr[1] + "='" + value_arr[1] + "' where prog_id='" + body.id +"';";

        for(const key in val_arr){
            sql += " UPDATE menumast set view_yn='"+ value_arr[1] +"' where prog_id='" + val_arr[key] + "';";
        }
    }else{
        sql = " UPDATE menumast set " + key_arr[0] + "='" + value_arr[0] + "' where prog_id='" + body.id +"'";
    }

    console.log(sql);

    db_pool.getConnection(async function (err, conn) {
        if (err) { //connction 오류 
            if (conn)
                conn.release();
            return;
        }

        await conn.beginTransaction();
        await conn.query(sql, [], function(err, result){
            if(err){ //쿼리 에러 
                conn.rollback();
                console.log(err);
                res.send(err.sqlMessage);
            }else{ //에러 안남 
                res.send('success');
                conn.commit();
            }
        });
        conn.release(); //connection pool을 반납 
    });
});

 

넘어온 데이터인 req.body를 살펴보면 수정할 컬럼, 수정한 값 등등 데이터가 있다 (oper 컬럼도 자동으로 넘어오는데 이건 나한테 쓸모 없음) 무튼 데이터의 0번째 값이 실제 수정하는 데이터이기 때문에 0번째 데이터를 가져와서 업데이트 해주면 되는데,  하위메뉴까지 변경하는 경우에는 하위메뉴들 배열데이터가 0번째, 수정한 컬럼값이 1번째에 있기 때문에 body에 view_yn이 있고 progidArr이 있는 경우에는 obj배열의 1번째 값을 업데이트하고, progidArr을 for문을 통해 값을 업데이트 해 주게 된다. 이런 경우에는 다중쿼리를 한번에 보내야하는 경우인데 처음에 설정해준 db랑 연결하는 정보를 담아둔 db_info안에 multipleStatements : true 속성을 주고, 쿼리 날릴 때 ;(세미콜론)으로 구분해서 보내주면 다중쿼리 잘 작동한다 ! 

multipleStatements : true // 다중 쿼리를 ';'기준으로 한번에 보낼 수 있다

쿼리작업이 성공일때 success를 넘겨주고, 실패일때 에러 메세지를 넘겨주기 때문에 위에서 설정한 afterSubmitCell에서 상태값을 넘겨받아 처리할 수 있게된다. 

 

수정 구현 완! 

 

 

* jqGrid 관련해서 구글을 미친듯이 뒤졌는데 그중에서도 참고한 포스팅들 목록
http://1004lucifer.blogspot.com/2019/05/jqgrid-cell.html (이분게 찐임..다 나와있으나 나같은 초보자는 이해하기 쉽지 않았음)
https://withsoju.tistory.com/703
https://okky.kr/article/307721
https://backback.tistory.com/92
https://isprogramming.tistory.com/13
https://blog.naver.com/PostView.naver?blogId=aufcl4858&logNo=220988211038&parentCategoryNo=&categoryNo=40&viewDate=&isShowPopularPosts=true&from=search
https://monkeybusiness.tistory.com/25
https://pjsprogram.tistory.com/30
https://hianna.tistory.com/488
https://jjanggun.tistory.com/466

댓글