728x90
반응형

 

 

페이지가 처음 렌더링 될 때, pagereload( 아래 링크 참조 )함수가 실행되고, DB에서 전체 리스트를 가져온다고 했었다.

이 리스트들은 미리 만들어준 Object 객체에 전달하게 된다.

https://typo.tistory.com/39?category=890054

 

Node.js , JQuery | 테이블 만들기 | 데이터 조회

1. html 코드는 간단하다.  thead와 tbody에 id를 준 뒤에 내용이 바뀔 때마다 pagereload를 실행하도록 하였다. 2. script쪽에서 먼저 pagereload함수를 페이지가 켜지자마자 실행되도록 한다. 이전에 선언한

typo.tistory.com

 

 

- pagingObject

var pagingObject = new Object({name : "Car",url : "/ajax/car_list", CNU : "{{company.CNU}}", array : [], sort : "CA", page : 0, postNum : 10, pageNum : 5, startpage : 0, endpage : 0, search: "", searchtext : "", searchdate : ""});

 

name 사용할 스키마의 이름
url Ajax URL
CNU 사업자번호( 페이징과 무관)
array 백엔드에서 가져온 list
sort 정렬 옵션
page 현재 페이지 번호
postNum 게시될 데이터의 갯수
pageNum 게시될 페이지번호의 갯수
startpage 시작 페이지
endpage 끝 페이지
search 검색 옵션
searchtext 검색 옵션(문자)
searchdate 검색 옵션(날짜)

 

 

1. 먼저 위에 써있는거와 같이 Object에 관한 내용들의 초깃값을 정하고 인스턴스를 만들어준다.

그다음 데이터조회 글에서 다루었던 것과 같이 pagereload가 실행된다.

pagereload 함수에서 리스트의 내용이 달라질 때마다 화살표 사이의 숫자들이 바뀌게끔 해주는

구문이 있다.

insertTr +=	"<a href='javascript:;' onclick=pageDoubleBtn('left',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-double-left'></i></a>";
insertTr +=	"<a href='javascript:;' onclick=pageBtn('left',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-left'></i></a>";

for(var i = Object.startpage; i < Object.endpage; i ++) {
	if(Object.page == i) // 그 번호가 맞으면 색상을 넣어준다.
		insertTr +=	"<input type='button' onclick=pagebutton(pagingObject,"+ i +",'basic') value ='"+ (i+1) +"' class='btn btn-white mr-1 px-2 text-primary' style='background-color: #00acac; color: white !important;' >";
	else
		insertTr +=	"<input type='button' onclick=pagebutton(pagingObject,"+ i +",'basic') value ='"+ (i+1) +"' class='btn btn-white mr-1 px-2 text-primary' >";
}
insertTr +=	"<a href='javascript:;' onclick=pageBtn('right',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-right'></i></a>";
insertTr +=	"<a href='javascript:;' onclick=pageDoubleBtn('right',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-double-right'></i></a>";

 

onclick 부분을 자세히 보면 결국은 pageDoubleBtn(), pageBtn(), pagebutton() 이 세가지 함수에 다 엮여있는 것을 볼 수 있다. 우리에게 필요한 조건은 아래와 같다.

   1) 번호를 직접 눌렀을 경우

   2) < 또는 > 버튼을 클릭했을 경우

   3) << 또는 >> 버튼을 클릭했을 경우

 

번호에 맞게 함수를 만들어준다. 페이지네이션을 할 때 pagingObject에서의 array는 바뀌는 값이 없다. 즉 백엔드와 통신을 할 필요가 없이 클라이언트 쪽에서만 이루어지기에 훨씬 빠르게 할 수 있다.

 

 

 // 페이지 기능
	function pagebutton(Object, num, kind) {
		Object.page = num;
		pagereload(Object);
    }
    
    // 다음 페이지 기능
    function pageBtn(dir, Object, kind) {
		if(dir == 'left') {
			if((Object.page - 1) > -1)
			{
				Object.page -= 1;
				pagereload(Object);
			}
		}
		if(dir == 'right') {
			if((Object.page + 1) < Math.ceil(Object.array.length / Object.postNum))
			{
				Object.page += 1;
				pagereload(Object);
			}
			
		}
	}
	
	// <<, >> 버튼 기능
	function pageDoubleBtn(dir, Object, kind) {
		if(dir == 'left') {
			if((Object.page - Object.pageNum) < 0) {
				Object.page = 0;
				pagereload(Object);
			}
			else {
				Object.startpage -= 1;
				Object.page -= 5;
				pagereload(Object);
			}
		}
		if(dir == 'right') {
			
			if ((Object.page + Object.pageNum) >= Math.ceil(Object.array.length / Object.postNum))
			{
				Object.page = Math.ceil(Object.array.length / Object.postNum)-1;
				pagereload(Object);
			}
			else {
				Object.startpage += 1;
				Object.page += 5;
				pagereload(Object);
			}
		}
	}

 

이런 식으로 시작점과 끝점일 때의 조건문을 넣어주면 완성 !

결국은 전체 리스트는 array에 담겨있기 때문에 어떤 시작점에서부터 몇개를 보여줄지만 설정해주면 된다.

 

 

 

 

 

 

728x90
반응형
728x90
반응형

전 글은 데이터를 한개만 삭제를 했을 경우이다.

이번에는 checkbox 선택으로 여러개를 삭제했을 경우를 설명하겠다.

 

1. checkbox 또한 전 글처럼 carlist_condition을 참조하여 HTML 코드를 확인해준다.

    먼저 각 개체 별 checkbox는 이렇다.

insertTr += "<td class='with-btn' nowrap>";
insertTr += "<input id='ck' type='checkbox' name ='ck' class='neHeros' value="+  Object.array[i].CN +" onChange='eachCheckedBox(this);'/>";
insertTr += "</td>";

   

    다음 전체 checkbox 코드는 이렇다.

insertTr += "<th id ='allcheck' width='1%'>";
insertTr += "<input type='checkbox' name='allck' class='neHeros' value='' onChange='allCheckedBox(this);'/>";
insertTr += "</th>";

 

 

2. 위 코드를 보면 onChange 함수에 eachCheckedBox, allCheckedBox 함수가 있는걸 볼 수 있다.

    아래 코드와 같이 각 checkbox별로 checked 옵션을 바꿔줄 수 있다.

// 전체 선택 기능
	function allCheckedBox(obj) {
	   	// @brief ��택한 체크박스의 클래스 명칭을 가져온다.
	   	const termClass = obj.getAttribute("class");
	   	// @brief 선택한 클래스명과 같은 클래스의 갯수
	   	Array.prototype.forEach.call(document.getElementsByClassName(termClass), function(element, index, array) {
	        // @brief 엘리먼트의 값 출력
	        // @brief 선택한 클래스의 첫번째 checkbox의 상태가 체크가 되있는 경우
	        if(document.getElementsByClassName(termClass)[0].checked == true) {
	            // @brief 같은 클래스명을 가진 모든 checkbox의 상태를 선택 완료 처리 한다.
	            element.checked = true;
	        }
	        // @brief 선택한 클래스의 첫번째 checkbox의 상태가 체크가 해제된 경우
	        else {
	            // @brief 같은 클래스명을 가진 모든 checkbox의 상태를 선택 해제 처리 한다.
	            element.checked = false;
	        }
	   	});
    }

	// 항목 선택 기능
	function eachCheckedBox(obj) {
      // @brief 선택한 체크박스의 상태가 선택해제인 경우
      if(obj.checked == false) {
            // @brief 선택한 체크박스의 클래스 명칭을 가져온다.
            const termClass = obj.getAttribute("class");
            // @brief 첫번째 checkbox의 상태가 체크가 되어있는경우
            if(document.getElementsByClassName(termClass)[0].checked == true) {
                // @brief 첫번째 checkbox의 상태를 체크해제한다.
                document.getElementsByClassName(termClass)[0].checked = false;
            }
        }
    }

 

 

3. 그 다음 checked 된 내용들을 불러와 ajax에 담고 백엔드에 데이터를 전송한다.

	// 선택항목 삭제 기능
	function delete_check(url) {
		var check = $("input:checkbox[name='ck']").is(":checked");
		if(!check) {
			alert(i18nconvert('choiceerror'));
		}
		else {
			var select_obj = [];
	        $('input[name="ck"]:checked').each(function (index) {
	                select_obj[index] = $(this).val() ;
	        });
			var answer;
	    		answer = confirm(i18nconvert('deleteconfirm'));
			if(answer == true){
				$.ajax({
		    		url: url,
                    type: "POST",
                    dataType: 'json',
                    data: {
                    	select : select_obj,
                    }
		    	}).done(function (data) {  
					if(data.result == 'success') {
						alert(i18nconvert('deletesuccess'));
						location.reload();
					}
					else {
						alert(i18nconvert('choiceerror'));
					}
		    	});
			}
			else {
				$("input:checkbox[name='allck']").prop("checked", false);
				$("input:checkbox[name='ck']").prop("checked", false);
				return false;
			}
		}
	}

 

 

4. 백엔드에서 받은 데이터를 이용해 해당 데이터들을 삭제한다.

//차량 선택삭제
router.post('/ajax/car_delete', isNotLoggedIn ,async (req, res, next) => {
    var select = req.body["select[]"];
    // const CID = req.decoded.CID;
    const CNU = req.decoded.CNU;
    const CUA = moment().format('YYYY-MM-DD hh:mm:ss');
    try {
        if(!select) {
          res.send({ result : 'fail' });
        }
        else {
          if (typeof(select) == 'string') {
            const carone = await modelQuery(QUERY.Findone,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" : select },{});
            await modelQuery(QUERY.Create,COLLECTION_NAME.Cardelete,{
                "CNU" : carone.CNU,
                "CN" : carone.CN,
                "CPN" : carone.CPN,
            },{});
            await modelQuery(QUERY.Remove,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" : select },{});
          }
          else {
             for(var i = 0; i < select.length; i++){
                var carone = await modelQuery(QUERY.Findone,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" : select[i] },{});
                await modelQuery(QUERY.Create,COLLECTION_NAME.Cardelete,{
                    "CNU" : carone.CNU,
                    "CN" : carone.CN,
                    "CPN" : carone.CPN,
                },{});
                await modelQuery(QUERY.Remove,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" : select[i] },{});
             }
          }
          
          await modelQuery(QUERY.Update,COLLECTION_NAME.Company,{where : { "CNU" : CNU }, update : { "CUA" : CUA }},{});
            
          res.send({ result : 'success' });
        }
    } catch (err) {
      res.send({ result : 'fail' });
      console.error(err);
      next(err);
    }
});
728x90
반응형
728x90
반응형

삭제버튼을 눌렀을 경우 삭제를 확인하는 alert를 띄어준 후, 확인버튼을 눌렀을 경우

백엔드에 데이터를 전송하고 DB쿼리문으로 해당 데이터를 삭제한다.

 

1. 전 글에서 수정했을 때와 마찬가지로 carlist_condition에 추가된 내용을 참조한다.

insertTr += "<input type='button' value='"+i18nconvert("delete")+"' onclick=delete_one(this,'/car/ajax/car_deleteone') class='btn btn-sm btn-white width-60' name =' "+  Object.array[i].CN +"'></td> ";

 

2. 클릭했을 경우 delete_one 함수를 실행해준다. ( obj는 HTML script에서 선언한 pagingObject이다. )

		
	// 일반 삭제 기능
	function delete_one(obj,url) {
		
		var answer;
	    	answer = confirm(i18nconvert('deleteconfirm'));
		if(answer == true){
			$.ajax({
	    		url: url,
	            type: "POST",
	            dataType: 'json',
	            data: {
	            	select : $(obj).attr('name'),
	            }
	    	}).done(function (data) {  
				if(data.result == 'success') {
					alert(i18nconvert('deletesuccess'));
					location.reload();
				}
				else {
					alert(i18nconvert('choiceerror'));
				}
	    	});
		}
		else {
			return false;
		}
	}

 

 

3. 백엔드에서 받은 데이터를 이용해 DB쿼리문으로 해당 데이터를 삭제해준다.

router.post('/ajax/car_deleteone', isNotLoggedIn, async (req, res, next) => {
  var select = req.body["select"];
  // const CID = req.decoded.CID;
  const CNU = req.decoded.CNU;
  const CUA = moment().format('YYYY-MM-DD hh:mm:ss');
  
  try {
    const carone = await modelQuery(QUERY.Findone,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" : select.split(' ') },{});
    await modelQuery(QUERY.Create,COLLECTION_NAME.Cardelete,{
      "CNU" : carone.CNU,
      "CC" : carone.CC,
      "CPN" : carone.CPN,
    },{});
    await modelQuery(QUERY.Remove,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" : select.split(' ') },{});
    await modelQuery(QUERY.Update,COLLECTION_NAME.Company,{where : {"CNU" : CNU}, update : { "CUA" : CUA }},{});
      
    res.send({ result : 'success' });
  } catch (err) {
    res.send({ result : 'fail' });
    console.error(err);
    next(err);
  }
});
728x90
반응형
728x90
반응형

수정하고자 하는 데이터 옆에 수정 버튼을 누르면 아래에 수정하고자 하는 내용이 생기고, 수정 버튼을 누르면

ajax로 백엔드에 필요한 데이터를 송신한다.

 

1. 전 글에서 만들어준 carlist_condition에서 수정버튼이 생성되는데, 이때 onclick으로 이어진 car_editone을 실행해준다.

 

carlist_condition

insertTr += "<a href='javascript:;' id = 'carid' onclick='car_editone(this);' data-type='show' name='"+ Object.array[i]._id +"' data-carnum='"+ Object.array[i].CN +"' class='btn btn-sm btn-primary width-60 m-r-2 edit-btn'>"+i18nconvert("modify")+"</a>";

 

 

car_editone

	// 차량 - 수정 나타내기
	function car_editone(obj) {
		var type = $(obj).data().type;
		var exCN = $(obj).data().carnum;
		console.log(exCN);
		$('.tr-edit').empty();
		$(obj).parents('tr').siblings().find('.edit-btn').text(i18nconvert("modify"));
		$(obj).parents('tr').siblings().find('.edit-btn').removeData().type;
		$(obj).parents('tr').siblings().find('.edit-btn').data('type', 'show');
		if(type == "show") {
			var car_id = $(obj).attr('name');
			if (car_id) {
				$.ajax({
					type: 'POST',
					url: '/car/ajax/car_list_edit1',
					dataType: 'json',
					data: {
						car_id: car_id
					}
				}).done(function(data) {
					if(data.result == 'success') {
						var carone = data.carone[0];
						var insertTr  = "";
						$(obj).text(i18nconvert("modify"));
						$(obj).removeData().type;
						$(obj).data('type', 'hide');
						insertTr += "<tr class='tr-edit'>";
						insertTr += "<td colspan='8'>";
					    insertTr += "<div class='panel-body'>";
						insertTr +=	"<form id='car-edit-form' action='' method='post' data-parsley-validate='true'>";
						insertTr +=	"<div class='form-group row m-b-15'>";
						insertTr +=	"<label class='col-md-4 col-sm-4 col-form-label' for='CN'>"+i18nconvert("CN")+" <span class='text-danger'>*</span> :</label>";
						insertTr +=	"<div class='col-md-8 col-sm-8'>";
						insertTr +=	"<input class='form-control' id='CN' type='text' name = 'CN' value='"+exCN+"'  placeholder='"+i18nconvert("CN")+"' data-parsley-required='true' />";
						insertTr += "<input type='hidden' name='exCN' value='"+exCN+"' />"
						insertTr +=	"</div>";
						insertTr +=	"</div>";
						insertTr +=	"<div class='form-group row m-b-15'>";
						insertTr +=	"<label class='col-md-4 col-sm-4 col-form-label' for='CN'>"+i18nconvert("CPN")+" :</label>";
						insertTr +=	"<div class='col-md-8 col-sm-8'>";
						insertTr +=	"<input class='form-control' id='CPN' type='text' name = 'CPN'  placeholder='"+i18nconvert("CPN")+"' />";
						insertTr +=	"<input class='form-control' id='car_id' type='hidden' name = 'car_id' value='"+carone._id+"'/>";
						insertTr +=	"</div>";
						insertTr +=	"</div>";
						insertTr +=	"<div class='d-flex justify-content-center align-items-center'>";
						insertTr +=	"<a href='javascript:;' onclick='carEdit(pagingObject);' class='btn btn-primary width-80 mx-2'>"+i18nconvert("modify")+"</a>";
						insertTr +=	"<button type='reset' class='btn btn-primary width-80 mx-2'>"+i18nconvert("reset")+"</button>";
						insertTr +=	"</div>";
						insertTr +=	"</form>";
						insertTr +=	"</div>";
						insertTr += "</td></tr>";
						$(obj).parents('tr').after(insertTr);
					}
					else {
						alert("{{__('modify_failed')}}");
					}
				});
			}
			else {
				alert("{{__('modify_failed')}}");
			}
		}
		else {
			$(obj).text(i18nconvert("modify"));
			$(obj).removeData().type;
			$(obj).data('type', 'show');
			$('.tr-edit').remove();
		}
	}
	
	// 차량 - 데이터 수정
	function carEdit(Object) {
		$.ajax({
			type: 'POST',
			url: '/car/ajax/car_list_edit2',
			dataType: 'json',
			data: {
				exCN : $('input[name=exCN]').val(),
				CN : $('#car-edit-form [name="CN"]').val(),
				CPN : $('#car-edit-form [name="CPN"]').val(),
				CNU : Object.CNU,
				car_id : $('#car-edit-form [name="car_id"]').val()
			}
		}).done(function(data) {
			if(data.result == 'success') {
				$('.tr-edit').remove();
				pagereload(Object);
				alert(i18nconvert("car_modify_success"));
			}
			else if(data.result == 'length') {
				alert(i18nconvert("car_length_error"));
			}
			else if(data.result == 'type') {
				alert(i18nconvert("car_type_error"));
			}
			else if(data.result == 'exist') {
				alert(i18nconvert("car_exist_error"));
			}
			else if(data.result == 'numErr') {
				alert(i18nconvert("register_digits_error"));
			}
			else {
				alert(i18nconvert("modify_failed"));
			}
		});
		
	}

 

 

2. 백엔드에서 받아온 데이터로 DB쿼리문을 실행한다.

// 수정 - 브라우저 나타내기
router.post('/ajax/car_list_edit1', isNotLoggedIn, async(req, res, next) => {
  const { car_id } = req.body;
  
  var ObjectId = Mongoose.Types.ObjectId;
  const carone = await modelQuery(QUERY.Find,COLLECTION_NAME.Car,{ _id : ObjectId(car_id) },{});
  
  res.send({ result : "success", carone : carone });
});

// 수정 - 데이터 수정
router.post('/ajax/car_list_edit2', isNotLoggedIn, async(req, res, next) => {
  const { exCN, CN, CPN, CNU, car_id } = req.body;
  
  const exCar = await modelQuery(QUERY.Findone,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" :  CN },{});
  const check = /^[0-9]{2,3}[가-힣]{1}[0-9]{4}/gi;
  const numCheck = /^[0-9]*$/;
  const CUA = moment().format('YYYY-MM-DD hh:mm:ss');
  try{
    if (CN.length >= 7 && CN.length <= 8) {
      check.lastIndex = 0;
      if (check.test(CN) == true) {
        
        if(!exCar) {
          if(numCheck.test(CPN) == true) {
              await modelQuery(QUERY.Update,COLLECTION_NAME.Car, {where : { "_id" : car_id } , update : {
                "CNU" : CNU,
                "CN" : CN,
                "CPN" : CPN,
              }},{});
              await modelQuery(QUERY.Update,COLLECTION_NAME.Company,{where : { "CNU" : CNU }, update : {"CUA" : CUA}},{});
              
            return res.send({ result : "success" });
          }
          else {
            return res.send({ result : "numErr" });
          }
        }
        else {
          if(exCN == CN) {
            await modelQuery(QUERY.Update,COLLECTION_NAME.Car, {where : { "_id" : car_id } , update : {
              "CNU" : CNU,
              "CN" : CN,
              "CPN" : CPN,
            }},{});
            await modelQuery(QUERY.Update,COLLECTION_NAME.Company,{where : { "CNU" : CNU }, update : {"CUA" : CUA}},{});
            
            return res.send({ result : "success" });
          }
          else {
            return res.send({ result : 'exist' });
          }
        }
      }
      else {
        return res.send({ result : 'type' });
      }
    }
    else {
      return res.send({ result : 'length' });
    }
  }catch(err) {
    res.send({ result : "fail" });
    console.error(err);
    next(err);
  }
});
728x90
반응형
728x90
반응형

1. html 코드는 간단하다.
   thead와 tbody에 id를 준 뒤에 내용이 바뀔 때마다 pagereload를 실행하도록 하였다.

<table id="data-table-combine" class="table table-striped table-bordered table-hover table-td-valign-middle text-center">
  <thead id="memDiv1">
  </thead>
  <tbody id="memDiv2">
  </tbody>
</table>

 

 

2. script쪽에서 먼저 pagereload함수를 페이지가 켜지자마자 실행되도록 한다. 이전에 선언한 pagingObject를 인자로 담고 인자에 따라 리스트가 표시되도록 설정했다.( 어느 schema를 조회할지, 어떤 url로 보낼것인지 등)

var pagingObject = new Object({name : "Car",url : "/ajax/car_list", CNU : "{{company.CNU}}", array : [], sort : "CA", page : 0, postNum : 10, pageNum : 5, startpage : 0, endpage : 0, search: "", searchtext : "", searchdate : ""});

$(document).ready(function() {
		pagereload(pagingObject); // 첫 화면 페이지 로드
    });

 

- pagingObject

name 사용할 스키마의 이름
url Ajax URL
CNU 사업자번호( 페이징과 무관)
array 백엔드에서 가져온 list
sort 정렬 옵션
page 현재 페이지 번호
postNum 게시될 데이터의 갯수
pageNum 게시될 페이지번호의 갯수
startpage 시작 페이지
endpage 끝 페이지
search 검색 옵션
searchtext 검색 옵션(문자)
searchdate 검색 옵션(날짜)

 

 

3. pagereload 함수는 변경사항이 있을 때마다 (검색하거나 정렬하거나 페이지가 바뀌거나 등) 실행이 될것이며, 그 때마다 리스트의 내용을 바꿔주는 함수이다.

function pagereload(Object) {
			$("input:checkbox[name='allck']").prop("checked", false);
			$("input:checkbox[name='ck']").prop("checked", false);
    	    $.ajax({
	    		url: Object.url,
                type: "POST",
                async: false,
                dataType: 'json',
                data: {
                	CID : Object.CID,
                	CNU : Object.CNU,
                    sort : Object.sort,
                    search : Object.search,
                    searchtext : Object.searchtext,
                    searchdate : Object.searchdate
                }
	        }).done(function (data) {
	        	if(data.result == true) {
	    		    Object.array = data.pagelist; // 전체 리스트
	    		    Object.startpage = Math.floor((Object.page) / Object.pageNum) * Object.pageNum;
	    		    Object.endpage = Object.startpage + Object.pageNum;
	    		    
	    		    var postNum = Object.postNum; // 게시될 페이지 숫자
	    		    var pageNum = Object.pageNum; // 페이지 번호의 갯수
	    		    var totalPage = Math.ceil(Object.array.length/Object.postNum); // 전체 페이지의 갯수
	    		    
	    		    if (Object.endpage > totalPage) { // 끝 페이지가 총 페이지 수보다 많다면 같게끔 처리
				        Object.endpage = totalPage;
				    }
	    		    
		        	//tr 초기화
		        	$("#data-table-combine > tbody > tr").remove();
		        	//페이지 넘버 박스 초기화
			     	$("#pagebox *").remove();
			    	
		    		if(Object.name == "Company") {
						companylist_condition(Object);
					
					}
					else if (Object.name == "Car") {
						carlist_condition(Object);
					}
					
					var insertTr = " ";
					insertTr +=	"<a href='javascript:;' onclick=pageDoubleBtn('left',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-double-left'></i></a>";
					insertTr +=	"<a href='javascript:;' onclick=pageBtn('left',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-left'></i></a>";
					
					for(var i = Object.startpage; i < Object.endpage; i ++) {
						if(Object.page == i)
							insertTr +=	"<input type='button' onclick=pagebutton(pagingObject,"+ i +",'basic') value ='"+ (i+1) +"' class='btn btn-white mr-1 px-2 text-primary' style='background-color: #00acac; color: white !important;' >";
						else
							insertTr +=	"<input type='button' onclick=pagebutton(pagingObject,"+ i +",'basic') value ='"+ (i+1) +"' class='btn btn-white mr-1 px-2 text-primary' >";
					}
					insertTr +=	"<a href='javascript:;' onclick=pageBtn('right',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-right'></i></a>";
					insertTr +=	"<a href='javascript:;' onclick=pageDoubleBtn('right',pagingObject,'basic') class='btn btn-primary mr-1 px-2'><i class='fas fa-angle-double-right'></i></a>";
					$("#pagebox").append(insertTr);
					
					$("#pagebox").show();
	        	}
	        	
	        	else if (data.result == "nothing") {
	        		$("#searchtext").val('');
			    	$("#searchdatetext1").val('');
			    	$("#searchdatetext2").val('');
			    	$("#memDiv2").empty();
			    	Object.array = [];
			    	
			    	if(Object.name == "Company") {
						companylist_condition(Object);
					}
					else if (Object.name == "Car") {
						carlist_condition(Object);
					}
					
	        	}
	        	else {
			    	$("#searchdatetext1").val('');
			    	$("#searchdatetext2").val('');
	        	}
			});
    }

 

4. 리스트에 따라 xx_condition 이라는 함수를 만들어서 리스트 내용을 채워준다.

ex}carlist_condition

 // 차량 리스트
    
    var carlist_condition = function (Object) {
		var num =  Object.array.length; 	
		var	insertTr = ""; // 채워넣을 HTML 초기화
		var indexcount = 1; // 인덱스번호 초기화
		
		$("#memDiv1").empty();
	    insertTr += "<tr>";
		insertTr += "<th id ='allcheck' width='1%'>";
		insertTr += "<input type='checkbox' name='allck' class='neHeros' value='' onChange='allCheckedBox(this);'/>";
		insertTr += "</th>";
		insertTr += "<th width='2.5%'></th>";
		insertTr += "<th width='10%' name='ANA'>"+i18nconvert("agent_branch")+"<a href='javascript:sortList(pagingObject,ANA);'><i id = 'ANA' class='float-right mx-1 fas fa-lg fa-fw m-t-3 ";
		if(Object.sort == "ANA-2")
			insertTr += "fa-sort-up'></a></i></th>";
		else if(Object.sort == "ANA")
			insertTr += "fa-sort-down'></a></i></th>";
		else 
			insertTr += "fa-sort'></a></i></th>";
		insertTr += "</th>";
		insertTr += "<th width='28%' name='CN'>"+i18nconvert("CN")+"<a href='javascript:sortList(pagingObject,CN);'><i id = 'CN' class='float-right mx-1 fas fa-lg fa-fw m-t-3 ";
		if(Object.sort == "CN-2")
			insertTr += "fa-sort-up'></a></i></th>";
		else if(Object.sort == "CN")
			insertTr += "fa-sort-down'></a></i></th>";
		else 
			insertTr += "fa-sort'></a></i></th>";
		
		insertTr += "<th width='30%'name='CPN'>"+i18nconvert("CPN")+"<a href='javascript:sortList(pagingObject,CPN);'><i id = 'CPN' class='float-right mx-1 fas fa-lg fa-fw m-t-3 ";
		if(Object.sort == "CPN-2")
			insertTr += "fa-sort-up'></a></i></th>";
		else if(Object.sort == "CPN")
			insertTr += "fa-sort-down'></a></i></th>";
		else 
			insertTr += "fa-sort'></a></i></th>";
			
		insertTr += "<th width='27.5%'name='CA'>"+i18nconvert("CA")+"<a href='javascript:sortList(pagingObject,CA);'><i id = 'CA' class='float-right mx-1 fas fa-lg fa-fw m-t-3 ";
		if(Object.sort == "CA-2")
			insertTr += "fa-sort-up'></a></i></th>";
		else if(Object.sort == "CA")
			insertTr += "fa-sort-down'></a></i></th>";
		else 
			insertTr += "fa-sort'></a></i></th>";
			
		insertTr += "<th width='1%'></th>";
		insertTr += "</tr>";
		
		$("#memDiv1").append(insertTr);
		
		insertTr = "";
		for (var i = ( Object.postNum *  Object.page) ; i < ( Object.postNum *  Object.page) +  Object.postNum ; i ++) { // 현재 페이지의 번호에 맞는 리스트 뽑아서 출력 (0 ~ 9, 10 ~ 19)
			if( Object.array.length != 0) {
				if( Object.array[i]) {
					insertTr += "<tr>";
					insertTr += "<td class='with-btn' nowrap>";
					insertTr += "<input id='ck' type='checkbox' name ='ck' class='neHeros' value="+  Object.array[i].CN +" onChange='eachCheckedBox(this);'/>";
					insertTr += "</td>";
					insertTr += "<td class='font-weight-bold'>"+(num - ( Object.page*10)) +" </td>";
					insertTr += "<td><a href='javascript:;' class='text-black' onclick=searchNow(pagingObject,'ANA','"+ Object.array[i].ANA +"')><u>"+ Object.array[i].ANA +"</u></a></td>";
					insertTr += "<td>"+ Object.array[i].CN+"</td>";
					
					if(! Object.array[i].CPN){
						insertTr+="<td>"+"N/A"+"</td><td>";
					}
					else {
						insertTr += "<td>"+  Object.array[i].CPN+"</td><td>";
					}
					
					if ( (moment( Object.array[i].CA).format('DD')) == moment().format('DD') )
					{
						insertTr +=  moment( Object.array[i].CA).format('HH:mm');
					}
					else {
						insertTr += moment( Object.array[i].CA).format('YYYY-MM-DD');
					}
					insertTr += "</td><td class='with-btn' nowrap>";
					insertTr += "<a href='javascript:;' id = 'carid' onclick='car_editone(this);' data-type='show' name='"+ Object.array[i]._id +"' data-carnum='"+ Object.array[i].CN +"' class='btn btn-sm btn-primary width-60 m-r-2 edit-btn'>"+i18nconvert("modify")+"</a>";
					insertTr += "<input type='button' value='"+i18nconvert("delete")+"' onclick=delete_one(this,'/car/ajax/car_deleteone') class='btn btn-sm btn-white width-60' name =' "+  Object.array[i].CN +"'></td> ";
					insertTr += "</tr>";
					num -= indexcount;
				}
			}
			else {
				insertTr += "<tr>";
				insertTr += "<td colspan='10'>No Data</td>";
				insertTr += "</tr>";
			
			break;
			}
		}
		
		$("#memDiv2").append(insertTr);
	};

 

 

5. 백엔드에서 옵션에 관한 내용들을 받고, 데이터를 클라이언트로 보내준다.

 ( searchCNU는 미들웨어에서 이 사이트에서 필요한 내용을 실행한 것이므로 무시하고 원하는 데이터를 얻을 수 있게끔 하시면 됩니다. )

//car list
router.post('/car_list', isNotLoggedIn, DataSet, agentDevide, async function(req, res, next) {
  const CNU = req.body.CNU;
  var sort = req.body.sort;
  var search = req.body.search;
  var searchtext = req.body.searchtext;
  var searchdate = req.body.searchdate;
  var sortText = "";
  var sortNum = 0;
  var cars = new Object;
  
  // company list에서 접속한 것인지 확인
  if(CNU.includes("#") == true) {
    req.searchCNU = CNU.split("#")[0]; // '#' 을 잘라
  }
  else {
    req.searchCNU = req.searchCNU; // 기존 middleware에서 받아온 본사,지점 CNU 그대로 다시 담음
  }
  
  // 정렬 기능
  if(sort.includes('-') == true) {
    sortText = sort.split('-')[0];
    sortNum = 1;
  }
  else {
    sortText = sort;
    sortNum = -1;
  }
  
  try {
    
    var doc = {
      lookup : { from : "Company", localField : "CNU", foreignField : "CNU", as : "ANA" } ,
      unwind : "$ANA",
      match : {},
      project : { CN : '$CN', CPN : '$CPN', CA : '$CA', ANA : '$ANA.ANA' },
      sort : { [sortText]: sortNum }
    }
    
    if(cars.length == 0) {
        return res.send({ result : "nothing" });
      }
    else {
      if (searchdate) {
      var searchtext2 = searchdate.split("~");
        if(search == "ANA") {
          doc.match = { "CNU": { $regex: req.searchCNU }, "ANA.ANA" : {$regex:searchtext}, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } };
        }
        else if(search == "CN") {
          doc.match = { "CNU": { $regex: req.searchCNU }, "CN" : {$regex:searchtext}, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } };
        }
        else if(search == "CPN") {
          doc.match = { "CNU": { $regex: req.searchCNU }, "CPN" : {$regex:searchtext}, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } };
        }
        else {
          doc.match = { "CNU": { $regex: req.searchCNU }, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } } ;
        }
      }
      else {
          if(search == "ANA") {
            doc.match = { "CNU": { $regex: req.searchCNU }, "ANA.ANA" : {$regex:searchtext} };
          }
          else if (search =="CN") {
            doc.match = { "CNU": { $regex: req.searchCNU }, "CN" : {$regex:searchtext} };
          }
          else if (search =="CPN") {
            doc.match = { "CNU": { $regex: req.searchCNU }, "CPN" : {$regex:searchtext} };
          }
          else {
            doc.match ={ "CNU": { $regex: req.searchCNU } };
          }
      }
    }
  
    cars = await modelQuery(QUERY.Aggregate,COLLECTION_NAME.Car,doc,{});
    if(cars.length == 0) {
      return res.send({ result : "nothing" });
    }
    
    var carlist = [];
    if(cars.length) {
      for(var i = 0; i < cars.length; i ++) {
        carlist[i] = cars[i];
      }
    }
    
    res.send({ result: true, pagelist : carlist });
  
  } catch(err) {
    console.error(err);
    next(err);
  }
});

 

try catch  문에서 나는 mongoose aggregate라는 쿼리를 사용했지만, 각자 용도에 맞게 쿼리문을 사용하면 된다.

몽고 쿼리에 관한 내용은 차후에 올리도록 하겠다.

728x90
반응형
728x90
반응형

1. 백엔드 쪽에서 데이터를 받고 행해질 내용들을 구현한다.

   또한 성공할 경우, 실패할 경우에 맞는 데이터를 res.send로 반환한다.

//자동차 등록
  // 수기 입력(하나씩) 차량 등록
router.post('/car_join', isNotLoggedIn, async (req, res, next) => {
  const { data } = req.body;
  const jsonData = JSON.parse(data);
  // const CID = req.decoded.CID;
  const CNU = req.decoded.CNU;
  
  try {
    // 차량번호 정규식
    var check = /^[0-9]{2,3}[가-힣]{1}[0-9]{4}/gi;
    // 업체에 등록된 차량
    const exCar = await modelQuery(QUERY.Findone,COLLECTION_NAME.Car,{ "CNU" : CNU, "CN" :  jsonData.CN },{});
      
    if(jsonData.CN.length >= 7 && jsonData.CN.length <= 8) {
      if(check.test(jsonData.CN) == true) {
        if(!exCar) {
          await modelQuery(QUERY.Create,COLLECTION_NAME.Car,{
            "CNU" : CNU,
            "CN" : jsonData.CN,
            "CPN" : jsonData.CPN
          },{});
          
          const CUA = moment().format('YYYY-MM-DD hh:mm:ss');
          await modelQuery(QUERY.Update,COLLECTION_NAME.Company,{where : { "CNU" : CNU }, update : { "CUA" : CUA }},{});
            
          return res.send({ result : 'success', type : 'car' });
        }
        else {
          return res.send({ result : 'exist', type : 'car' });
        }
      }
      else {
        return res.send({ result : 'type', type : 'car' });
      }
    }
    else {
      return res.send({ result : 'length', type : 'car' });
    }
  } catch(err) {
    res.send({ result : "fail" });
    console.error(err);
    next(err);
  }
});
728x90
반응형
728x90
반응형

먼저 등록하고자 하는 데이터들을 text에 담아 ajax로 보내고, 백엔드에서 쿼리문으로 DB에 내용이 추가되도록 한다.

 

1. 클라이언트는 form으로 데이터들을 submit할 수 있게 만들어준다.

<div id="car-form-div" class="panel-body">
	<form id="car-form" action="" method="post" class="form-horizontal" data-parsley-validate="true" name="car-form" onsubmit="carJoin();">
		<div class="form-group row m-b-15">
			<label class="col-md-4 col-sm-4 col-form-label" for="CN">{{__('car_number')}} <span class="text-danger">*</span> :</label>
			<div class="col-md-8 col-sm-8">
				<input class="form-control" type="String" id="CN" name="CN" placeholder="{{__('car_number_msg')}}" data-parsley-required="true" data-parsley-error-message="{{__('required_detail')}}" data-parsley-trim-value="true" />
			</div>
		</div>
		<div class="form-group row m-b-15">
			<label class="col-md-4 col-sm-4 col-form-label" for="CN">{{__('car_customer_phone')}} :</label>
			<div class="col-md-8 col-sm-8">
				<input class="form-control" type="String" id="CPN" name="CPN" placeholder="{{__('car_customer_phone')}}" data-parsley-type="digits" data-parsley-type-message="{{__('register_digits_error')}}" data-parsley-trim-value="true" />
			</div>
		</div>
		<div class="form-group row m-b-0">
			<label class="col-md-4 col-sm-4 col-form-label">&nbsp;</label>
			<div class="col-md-8 col-sm-8">
				<button type="submit" class="btn btn-primary width-80">{{__('registration')}}</button>
				<button type="reset" class="btn btn-primary width-80">{{__('reset')}}</button>
			</div>
		</div>
		<!--현재 시간 변수-->
		<input type="hidden" class="form-control" name = "Ctime" id = "Ctime" />
	</form>
</div>

 

2. script 쪽에서 데이터들을 검증한 뒤에 ajaxJoin() 함수를 실행해준다.

// 차량 등록 ajax
	function carJoin() {
		var formParsley  = $('#car-form').parsley();
		if(formParsley.isValid() == true) {
			ajaxJoin('/car/car_join', i18nconvert, { CN: document.getElementsByName("CN")[0].value, CPN: document.getElementsByName("CPN")[0].value });
		}
		event.preventDefault();
	}

 

3. ajaxJoin 함수에서 백엔드로 데이터를 전송하고, 받아올 데이터에 따라 실행 될 내용들을 정의해준다.

// 공통 등록(차량, 장비)
function ajaxJoin(url, i18nconvert, data) {
	console.log(data)
    $.ajax({
        type: 'POST',
        url: url,
        dataType: 'json',
        data: {
            data : JSON.stringify(data)
        }
    }).done(function(data) {
        if(data.type == 'device') {
            if(data.result == 'success') {
				alert(i18nconvert('device_register_success'));
				location = '/device_list';
			}
			else if(data.result == 'exist') {
				alert(i18nconvert('device_duplicated'));
			}
			else if(data.result == 'type') {
				alert(i18nconvert('device_mac_wrong'));
			}
			else {
				alert(i18nconvert('device_register_fail'));
			}
        }
		else if(data.type == 'car') {
			if(data.result == 'success') {
				alert(i18nconvert('car_register_success'));
				location = '/car_list';
			}
			else if(data.result == 'exist') {
				alert(i18nconvert('car_exist_error'));
			}
			else if(data.result == 'type') {
				alert(i18nconvert('car_type_error'));
			}
			else if(data.result == 'length') {
				alert(i18nconvert('car_length_error'));
			}
			else if(data.result == 'excelType') {
				alert(i18nconvert('car_exceltype_error'));
			}
			else if(data.result == 'excelLength') {
				alert(i18nconvert('car_excellength_error'));
			}
			else {
				alert(i18nconvert('car_register_fail'));
			}
		}
    });
}

 

728x90
반응형
728x90
반응형

간단하게 큰 그림을 얘기하자면, 먼저 처음 데이터는 백엔드에서 modelquery를 이용해 해당 리스트에 필요한 모든 정보를 불러온다. 등록 버튼을 만들어서 팝업창을 띄어도 되지만, 만들 때 등록 페이지는 따로 있는게 나을거 같다는 의견을 토대로 만들었기에 등록 페이지는 따로 구현을 했다.

 

테이블에서 중요한것은 등록 > 리스트 > 수정 > 삭제 > 페이지네이션 > 검색 > 정렬 > 페이지갯수 > 엑셀 다운

이 순인거 같다.( 주관적인 의견이며 개인적으로 개발할 때 저 순서대로 하는것이 제일 편했고, 엇갈리는것이 없었다. )

 

1. CRUD 작업은 node.js 백엔드를 타서 실행되도록 하였다.

 

2. 페이지네이션은 Object를 사용하였으며, 클라이언트쪽에서 가능하도록 하였다.

 

3. 검색과 정렬은 먼저 어떻게 검색할 지, 정렬할 지 옵션을 변수로 만들어 백엔드에 넘겨주고 원하는 데이터를 받아와

   Ajax로 부분만 바뀌도록 하였다.

 

4. 페이지 갯수는 클라이언트쪽에서 원하는 갯수만큼 보여줄 수 있도록 설정하였다.

 

5. 엑셀 다운은 클라이언트에서 내가 보고있는 테이블들만 엑셀로 담아주도록 하였다.

 

그림으로 나타내면 다음과 같다.

먼저 전체 리스트를 받아오거나, search또는 sort를 할 경우

HTML script에 object를 선언해준 후

var pagingObject = new Object({name : "Device", url : "/ajax/device_list", CNU : "{{company.CNU}}", array : [], sort : "CA", page : 0, postNum : 10, pageNum : 5, startpage : 0, endpage : 0, search: "", searchtext : "", searchdate : ""});

백엔드에 옵션에 대한 정보를 주고, 백엔드에서 쿼리문을 이용해 조회하며, 클라이언트의 object 내용들을 바꿔준다.

 

 

다음은 클라이언트 쪽에서 페이지네이션을 하거나 페이지의 갯수를 조정해줄 경우

ajax로 가져온 데이터들을 조리있게 바꿔서 리스트의 내용들을 교체해준다.

728x90
반응형
728x90
반응형

JQuery를 1년간 개발한 결과 결국 제일 중요한 것은 Ajax와 Object인 것을 깨달았다. 이는 React에선 Axios, State(리액트 훅)으로 이어지며, JQuery를 나름 심도있게 개발하니 React도 이해하기가 쉬웠다.

기억하자 JQuery는 Object와 Ajax가 제일 중요하고 쓸모있다!

 

화면 구성

구성한 테이블의 화면이다. 주요 기능들 위주로 설명하겠다.

 

728x90
반응형
728x90
반응형

i18n은 다국어 지원을 가능하게 해주는 모듈이다. JQuery 와 React에서 쓰이는 방법이 다르며, React에서 쓰이는 방법은 

차후에 올리도록 하겠다. 

 

1. 먼저 i18n을 설치한다.

sudo npm i i18n

 

2. 최상 루트폴더 밑에 i18n.js를 만들어준다.

 

i18n.js

const i18n = require('i18n');

i18n.configure({
    locales: ['ko', 'en'],
    directory: __dirname + '/locales',
    defaultLocale: 'ko',
    cookie: 'lang',
});

module.exports = function(req, res, next) {
    i18n.init(req, res);
    res.locals.__ = res.__;
    var current_locale = i18n.getLocale();
    return next();
};

코드를 보면 /locales 파일 밑에 ko, en 파일들을 쓴다고 되어있다. 

defaultLocale인 ko(한국어)가 최초의 값으로 되어있고, 위에 사진처럼 다른언어를 클릭할 시에 페이지 내에

텍스트들이 바뀌는것을 확인 할 수 있다.

 

3.  최상 루트폴더 밑에 locales 폴더를 만들고, 하위에 ko.json, en.json파일들을 만들어 준다.

 

ko.json

{
	"" : "",
	"lang_kr": "한국어 (기본)",
	"lang_en": "영어",
}

 

en.json

{
	"" : "",
    "lang_kr": "Korean (Basic)",
	"lang_en": "English"
}

 

해당 모듈은 json형식을 완벽하게 따라해야되기 때문에 마지막 value 값 뒤 ,(콤마)를 붙이는 실수를 하면 안된다.

(나중에 큰 고생을 합니다..ㅜㅜ) 두 개의 파일의 순서도 똑같이 해줘야 한다.

 

4. app.js에 i18n을 설정해준다.

const i18n = require('./i18n');

app.use(i18n);

 

5. 클라이언트에서 버튼을 생성해준다

<div class="dropdown-menu dropdown-menu-right">
	<a href="/ko" class="dropdown-item d-flex justify-content-start align-items-center"><img class="flag" src="https://lipis.github.io/flag-icon-css/flags/4x3/kr.svg" alt="South Korea Flag">{{__('lang_kr')}}</a>
	<a href="/en" class="dropdown-item d-flex justify-content-start align-items-center"><img class="flag" src="https://lipis.github.io/flag-icon-css/flags/4x3/us.svg" alt="United States of America Flag">{{__('lang_en')}}</a>
</div>

 

6. 백엔드(node.js)라우터에서 쿠키설정을 해줍니다.

router.get('/en', function(req, res) {
  res.cookie('lang', 'en');
  res.redirect('/main');
});

router.get('/ko', function(req, res) {
  res.cookie('lang', 'ko');
  res.redirect('/main');
});

 

그럼 모든 설정을 끝이 났다. 

사용하는 방법은 아래와같다.

<button type="button">{{__('close')}}</button>

{{__('이곳')}} 에 json파일들로 설정한 이름들을 넣어주면 get메소드가 실행될 때마다 언어를 바꿔줄 수 있다.

 

노가다지만 나중에 다국어를 한다 생각해보면 필요성이 있는 모듈이었다.

728x90
반응형

+ Recent posts