아 디런 페이징.-_-
덕분에 그나마 깔끔했던 xml이 아주 지저분해졌다.~_~;
페이징을 위한 param들도 머리를 아프게 했고.-_-
맨 첫 페이지와 맨 마지막 페이지의 링크를 없애는 방법도 머리 아팠다.~_~
여튼 결론은 table을 다 그리는게 어쩌면 더 편해 보일지 모르는 지저분해진 xml인것이다.;
변경점.
1. row data 가 0일때의 처리
2. xml data는 페이지 로딩 시 1번만 요청 하도록 수정
3. paging기능 추가.
고려사항
- paging으로 추가되는 내용역시 cell이기 때문에 기존에 만들어진
stCell클래스를 확장해서 사용. (소스에 주석 달려 있음)
이전에 좀 불친절 했던 소개라는 느낌이 들어서 좀 친절하게 설명 하겠음.-_-;;
view 역할을 하는 table.xml<?xml version="1.0" encoding="UTF-8" ?>
<gabia>
<tables>
<notice>
<rowCss>noticeRow1</rowCss>
<rowCss>noticeRow2</rowCss>
<rowCss>noticeRow3</rowCss>
<row><![CDATA[noticeTitle^^<a href="#seq#">#title#</a>]]></row>
<row><![CDATA[noticeAuthor^^#author#]]></row>
<row><![CDATA[noticeCount^^#viewcount#]]></row>
<row><![CDATA[noticeDate^^#regdate#]]></row>
<paging>
<pagingCss>pagingCell</pagingCss>
<pageFunction>onclick="goPage('#PAGE#');"</pageFunction>
<firstPage><![CDATA[<p class="firstPage" #FUNCTION#>FIRST</p> ]]></firstPage>
<prevPage><![CDATA[<p class="nextPage" #FUNCTION#>PREV</p> ]]></prevPage>
<currentPage><![CDATA[<p class="currentPage" >#PAGE#</p> ]]></currentPage>
<otherPage><![CDATA[<p class="pageNumber" #FUNCTION#>#PAGE#</p> ]]></otherPage>
<nextPage><![CDATA[<p class="nextPage" #FUNCTION#>NEXT</p> ]]></nextPage>
<lastPage><![CDATA[<p class="firstPage" #FUNCTION#>LAST</p>]]></lastPage>
</paging>
</notice>
</tables>
</gabia>
paging기능 문제로 paging element가 추가 되었다.
pagingCss : paging으로 추가된 td의 css name
pageFunction : page이동시 사용될 스크립트 함수의 형식. 인자는 Page번호만으로 한다.* 맨 마지막 페이지나 맨 처음 페이지일 경우에 링크를 막기 위해 이러한 구조를 취한다.
firstPage : 제일 앞으로 이동 하는 링크 template
prevPage : 현재 페이지 번호보다 1 작은 페이지로 이동하는 링크 template
currentPage : 현재 페이지 번호의 template
nextPage : 현재 페이지 번호보다 1 큰 페이지로 이동하는 링크 template
lastPage : 맨 마지막 페이지로 이동하는 링크 template
기본적으로 style의 cursor모양은 지정하지 않고 page번호 생성 할 때에 onmouseover event를 통해 style을 제어 하도록 한다.
cell을 생성하고 innerHTML을 이용해 문자열을 대입시 <p></p> 태그의 style 속성이 사라지는 버그인지 알수 없는 현상이 발생.
model 역할을 하는 table.php<?
$data = "[";
$data .= "{\"seq\":\"166\",\"title\":\"test166\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"165\",\"title\":\"test165\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"164\",\"title\":\"test164\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"163\",\"title\":\"test163\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"162\",\"title\":\"test162\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"161\",\"title\":\"test161\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"160\",\"title\":\"test160\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"159\",\"title\":\"test159\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"158\",\"title\":\"test158\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
{\"seq\":\"157\",\"title\":\"test157\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"}";
$data .= "]";
$param = "{\"layerid\":\"content\",\"listcount\":\"10\",\"pagingcount\":\"10\"}";
$return_data = "{";
$return_data .= "\"resultCode\" : 'SUCCESS',";
$return_data .= "\"row\" : '10',";
$return_data .= "\"total\" : '10',";
$return_data .= "\"result\" : " . $data . ",";
$return_data .= "\"message\" : '',";
$return_data .= "\"layerID\" : '',";
$return_data .= "\"pagingcount\" : '10',";
$return_data .= "\"listcount\" : '10',";
$return_data .= "\"param\" : " . $param . ",";
$return_data .= "\"page\" : '1'";
$return_data .= "}";
echo($return_data);
?>
실제 개발을 한다라면 ajax call의 response는 위의 형식으로 되어야 함.
$pCode :결과코드
$pValue : json_encode를 이용해 만들어진 jsondata
$pMessage : message
$pLayerID : 작업 대상 layerid
$pParam : json_encode를 이용해 만들어진 parameter
$pRow : 실제 data의 row수
$pTotal : select된 전체 row수 - paging 관련 인자.
$pPage : 요청 받은 page ? paging 관련 인자.
$pPagingCount : page 번호가 보여지는 수. 몇 page부터 몇 page까지 하단에 보여지는지. - paging 관련 인자.
$pListCount : 게시판에 한번에 보여질 글의 수 - paging 관련 인자.
예전에 빠졌던 ajaxBasic.jsfunction getAjaxData(URL,params,func, interID)
{
var myAjax = new Ajax.Request( URL,
{
asynchronous: true,
method: "post",
parameters: params,
onSuccess: function(xmlHttp){
//alert(xmlHttp.responseText);
try
{
var data = eval('(' + xmlHttp.responseText + ')');
}
catch(E)
{
alert("AjaxCall : " + E + " - " + xmlHttp.responseText);
return;
}
//alert(xmlHttp.responseText);
func(data);
},
onFailure : function (request)
{
func("FAIL");
return;
}
}
);
}
control 역할의 table.js (xmlParser는
이전 post참조.)
/********************************************************************************
ajax를 이용한 json data를 parsing해서 table을 그리는 class
작성자 : 이정훈 (ljhoon@gabia.com)
작성일 : 2009.01.13
수정일 : 2009.01.15 - paging 기능 완료
********************************************************************************/
var tableXML = "http://local.inmail.co.kr/xml/table.xml";
// 로컬에서 개발 하던 url이므로 테스트 하는 곳의 url로 수정
/********************************************************************************
stCell : td를 구성하기 위한 기본 정보를 갖게 되는 구조체 성격의 Class
만약 td에 정의 해야 할 속성이 늘어나면 xml에 ^^ 구분자를 이용해서
추가를 하고 stCell class에도 속성을 추가 해 주면 된다.
********************************************************************************/
var stCell = Class.create();
stCell.prototype = {
cssName : null,
cellData : null,
initialize : function(pData)
{
if (pData == null) this.setNullData();
else
{
if (pData.length < 1)
{
this.setNullData();
}
else
{
this.cssName = pData[0];
this.cellData = pData[1];
}
}
},
getCss : function()
{
return this.cssName;
},
getData : function()
{
return this.cellData;
},
setData : function(pValue)
{
this.cellData = pValue;
},
setNullData : function()
{
this.cssName = "nodata";
this.cellData = "nodata";
}
};
/********************************************************************************
stPaging : paging cell의 내용을 위한 구조체 성격의 class
기본적인 stCell보다 디자인적 요소가 많기 때문에 상속받아 구현.
paging node를 인자로 받는다.
다중 상속을 위해 임시 객체를 하나 더 만든다.
paging또한 td(cell)이기 때문에 stCell객체를 확장한다.
********************************************************************************/
var stTemp = Class.create();
stTemp.prototype = Object.extend (new xmlParser, new stCell);
var stPaging = Class.create();
stPaging.prototype = Object.extend (new stTemp, {
m_oFirst : null,
m_oPrev : null,
m_oNumber : null,
m_oCurrent : null,
m_oNext : null,
m_oLast : null,
m_oFunction : null,
initialize : function(pData)
{
this.getPagingTemplate(pData);
},
getPagingTemplate : function(pData)
{
if (pData == null)
return ("paging row Info is null");
else
{
try
{
this.cssName = this.getValue(this.getNode(pData, "pagingCss"));
this.m_oFirst = this.getValue(this.getNode(pData, "firstPage"));
this.m_oPrev = this.getValue(this.getNode(pData, "prevPage"));
this.m_oCurrent = this.getValue(this.getNode(pData, "currentPage"));
this.m_oNumber = this.getValue(this.getNode(pData, "otherPage"));
this.m_oNext = this.getValue(this.getNode(pData, "nextPage"));
this.m_oLast = this.getValue(this.getNode(pData, "lastPage"));
this.m_oFunction = this.getValue(this.getNode(pData, "pageFunction"));
}
catch(E)
{
alert("Paging Node select fail : " + E);
return;
}
}
},
setData : function (pPage, pTotal, pListCount, pPagingCount)
{
//alert(pPage + " - " + pTotal + " - " + pListCount + " - " + pPagingCount);
var firstPage = 1;
var lastPage = Math.ceil(pTotal / pListCount);
var half = parseInt(pPagingCount/2, 10);
var startPage = (pPage > half)?(pPage - half) : 1;
var endPage = startPage + pPagingCount - 1;
if (pListCount < lastPage)
{
endPage = (endPage > lastPage)?lastPage : endPage;
startPage = ( (endPage - pPagingCount) < startPage )?(endPage - pPagingCount + 1) : startPage;
}
else
{
endPage = lastPage;
startPage = firstPage;
}
prevPage = ((pPage -1) <= 0)?1:pPage-1;
nextPage = ((pPage+1) > endPage)?pPage : (pPage +1);
var pagingBuffer = "";
//alert(half + " - " + startPage + " - " + endPage + " - " + lastPage);
/*
맨 끝 페이지나 맨 마지막 페이지의 링크를 막기 위해 이런 구조를 취함.
*/
var regPage = new RegExp("#PAGE#", "ig");
var overCursor = " onmouseover=\"this.style.cursor='pointer';\"";
if (pPage == firstPage)
{
pagingBuffer += this.m_oFirst.replace(/#FUNCTION#/ig, "");
pagingBuffer += this.m_oPrev.replace(/#FUNCTION#/ig, "");
}
else
{
pagingBuffer += (this.m_oFirst.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, firstPage);
pagingBuffer += (this.m_oPrev.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, prevPage);
}
for (var i=startPage ; i<=endPage ; ++i)
pagingBuffer += (i == pPage)? this.m_oCurrent.replace(regPage, i) : (this.m_oNumber.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, i);
if (pPage == lastPage)
{
pagingBuffer += (this.m_oNext.replace("#FUNCTION#", "")).replace(regPage, nextPage);
pagingBuffer += (this.m_oLast.replace("#FUNCTION#", "")).replace(regPage, lastPage);
}
else
{
pagingBuffer += (this.m_oNext.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, nextPage);
pagingBuffer += (this.m_oLast.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, lastPage);
}
this.cellData = pagingBuffer;
}
});
/********************************************************************************
stRow : td 배열을 갖게 되는 row 구조체 class
row별 배경색이 다른 경우를 위해 css이름 속성을 갖는다.
********************************************************************************/
var stRow = Class.create();
stRow.prototype = {
m_stCells : null,
m_oCss : null,
length : 0,
initialize : function()
{
this.m_oCss = new Array();
this.m_stCells = new Array();
},
insert : function(pCell)
{
this.m_stCells[this.m_stCells.length] = pCell;
this.length++;
},
setCss : function(pCss)
{
if (typeof(pCss) != "Object")
this.m_oCss[this.m_oCss.length] = pCss;
else if (pCss.length > 1)
{
var cssCount = pCss.length;
for (var i=0 ; i<cssCount ; ++i)
this.m_oCss[cssCount + i] = pCss[i];
}
},
getCell : function(pIndex)
{
if (this.m_stCells.length >= pIndex)
{
return this.m_stCells[pIndex];
}
},
getColumnCount : function()
{
return this.m_stCells.length;
},
getCssCount : function()
{
return this.m_oCss.length;
},
getCss : function(pIndex)
{
if (this.m_oCss.length > pIndex)
return this.m_oCss[pIndex];
else
return this.m_oCss[0];
}
};
/********************************************************************************
TableTemplate : Table Template XML정보를 가져온다.
(Table class 생성마다 xml을 읽어오게 되어 있어서 따로 분리)
********************************************************************************/
var TableTemplate = Class.create();
TableTemplate.prototype = Object.extend (new xmlParser, {
xmlTemplate : null,
initialize : function()
{
this.loadXML();
},
/********************************************************************************
table 구조 xml을 읽어 오는 부분. 동기로 처리 했음.
********************************************************************************/
loadXML : function()
{
new Ajax.Request(tableXML, {
asynchronous: false,
method: "get",
onSuccess: function(xmlHttp)
{
try
{
xmlTemplate = xmlHttp.responseXML;
return true;
}
catch(E)
{
xmlTemplate = null;
alert("XML Load Fail : " + E);
return false;
}
},
onFailure : function (request)
{
xmlTemplate = null;
alert("Table Template XML Request Fail");
return false;
}
});
},
getTemplate : function()
{
return xmlTemplate;
}
});
var TemplateInfo = new TableTemplate();
/********************************************************************************
Table : stRow, stCell을 이용해 실제 Table row를 그린다.
********************************************************************************/
var Table = Class.create();
Table.prototype = Object.extend (new xmlParser, {
m_nRow : 0,
m_oData : null,
m_oTableID : null,
m_oRowTemplate : null,
m_oDefaultRowCss : null,
xmlBuffer : null,
m_oRows : null,
m_bHeaderExist : true,
m_bPaging : true,
m_oParam : null,
m_nTotal : 0,
m_nPagingCount :0,
m_nListCount : 10,
m_nPage : 1,
m_oMessage : null,
/********************************************************************************
pData : json data
pTableID : target table id
pHeaderExist : 테이블 column별 제목이 있다면 true 없다면 false.
table을 그리기 전에 true이면 1줄을 남기고 없다면 모든 row를 지운다.
********************************************************************************/
initialize : function(pData, pTableID, pHeaderExist, pPaging)
{
if (pData != null)
{
this.m_nRow = parseInt(pData.row, 10);
this.m_oData = pData.result;
this.m_oTableID = pTableID;
this.m_bHeaderExist = pHeaderExist;
this.m_bPaging = pPaging;
this.m_nListCount = pData.listcount;
this.m_nPagingCount = parseInt(pData.pagingcount, 10);
this.m_nTotal = parseInt(pData.total, 10);
this.m_oParam = pData.param;
this.m_nPage = parseInt(pData.page, 10);
xmlBuffer = TemplateInfo.getTemplate();
}
this.m_oMessage = pData.message;
},
/********************************************************************************
table의 row와 total값을 기준으로 table data가 있는지 여부를 확인한다.
********************************************************************************/
hasRow : function()
{
return (this.m_nTotal > 0 && this.m_nRow > 0)?true : false;
},
/********************************************************************************
xml에 정의된 각 td별 설정을 읽어와 rowTemplate 객체를 만든다.
row별 css정보도 여기서 설정한다. css가 명시된 수 만큼 적용된다.
********************************************************************************/
getRowTemplate : function()
{
if (xmlBuffer == null)
return ("table row Info is null");
else
{
try
{
this.m_oRows = null;
this.m_oRows = new stRow();
var objectNode = this.getNode(xmlBuffer, this.m_oTableID);
var cssTemp = this.getNodeAll(objectNode, "rowCss");
for(var i=0 ; i<cssTemp.length ; ++i)
this.m_oRows.setCss(this.getValue(cssTemp[i]));
var rowTemp = this.getNodeAll(objectNode, "row");
for(var i=0 ; i<rowTemp.length ; ++i)
{
var templateValue = this.getValue(rowTemp[i]);
this.m_oRows.insert(new stCell(templateValue.split("^^")));
}
}
catch(E)
{
alert("Node select fail : " + E);
return;
}
}
},
/********************************************************************************
실제로 table row를 생성 하는 부분
row를 추가 하기 전에 기존에 그려진 모든 row를 지운다.
json의 인덱스 명은 xml에서 명시한 delimiter명과 같아야 한다.
********************************************************************************/
insertRow : function()
{
if (!this.hasRow())
{
var row = $(this.m_oTableID).insertRow(-1);
var c = row.insertCell(-1);
c.className = "nodata";
c.colSpan = 10;
c.innerHTML = this.m_oMessage;
return;
}
if ($(this.m_oTableID) == null)
{
alert("Base Table is null.");
return;
}
if ($(this.m_oTableID).rows.length > 1)
{
if (this.m_bHeaderExist)
{
for(var i=($(this.m_oTableID).rows.length-1) ; i>0 ; --i)
$(this.m_oTableID).deleteRow(i);
}
else
{
for(var i=($(this.m_oTableID).rows.length-1) ; i>=0 ; --i)
$(this.m_oTableID).deleteRow(i);
}
}
this.getRowTemplate();
for(var i=0 ; i<this.m_nRow ; ++i)
{
var nLoopCount = 0;
var row = $(this.m_oTableID).insertRow(-1);
row.className = this.m_oRows.getCss( (i+1)%this.m_oRows.getCssCount() );
for (var j=0 ; j<this.m_oRows.length ; ++j)
{
var c = row.insertCell(-1);
var tempCell = this.m_oRows.getCell(nLoopCount).getData();
for(var tempIndex in this.m_oData[i])
{
var delimiter = new RegExp("#" + tempIndex + "#", "ig");
tempCell = tempCell.replace(delimiter, this.m_oData[i][tempIndex]);
}
c.innerHTML = tempCell;
c.className = this.m_oRows.getCell(nLoopCount).getCss();
nLoopCount++;
}
}
// page 표시가 true이면 표시 한다.
if (this.m_bPaging) this.paging();
},
/********************************************************************************
table pagin 처리
********************************************************************************/
paging : function()
{
var pagingCell = new stPaging(this.getPagingTemplate());
var row = $(this.m_oTableID).insertRow(-1);
var c = row.insertCell(-1);
pagingCell.setData(this.m_nPage, this.m_nTotal, this.m_nListCount, this.m_nPagingCount);
c.colSpan = this.m_oRows.getColumnCount();
c.className = pagingCell.getCss();
c.innerHTML = pagingCell.getData();
},
getPagingTemplate : function()
{
if (xmlBuffer == null)
return ("paging row Info is null");
else
{
try
{
var objectNode = this.getNode(xmlBuffer, this.m_oTableID);
return this.getNode(objectNode, "paging");
}
catch(E)
{
alert("Paging Node select fail : " + E);
return;
}
}
},
getParam : function()
{
return this.m_oParam;
}
});
사용 sample table.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>test</title>
<script language="javascript" type="text/javascript" src="/scripts/prototype.js"></script>
<script language="javascript" type="text/javascript" src="/scripts/ajaxBasic.js"></script>
<script language="javascript" type="text/javascript" src="/scripts/simpleXml.js"></script>
<script language="javascript" type="text/javascript" src="/scripts/table.js"></script>
<style type="text/css">
p, table, div { float:left; position:relative; display:inline-block; padding:0; margin:0; }
#list { width:800px; border:solid 1px #dddddd; }
#list #notice {width:600px; font-size:11px; font-family : 맑은고딕, Lucida Grande, Verdana, Sans-serif;}
#list #notice tr { height:20px; }
#list #notice .noticeHeader {font-weight:bold; text-align:center;}
#list #notice .noticeRow1 {border-bottom:solid 1px #dddddd; }
#list #notice .noticeRow2 {border-bottom:solid 1px #dddddd; background-color:#dddddd; }
#list #notice .noticeRow3 {border-bottom:solid 1px #dddddd; background-color:#aaaaaa; }
#list #notice .noticeTitleHead {width:250px;}
#list #notice .noticeDateHead {width:75px;}
#list #notice .noticeCountHead {width:75px; text-align:right;}
#list #notice .noticeAuthorHead {width:100px;}
#list #notice .noticeBoardHead {width:100px;}
#list #notice .currentPage {font-weight:bold; width:15px;}
#list #notice .pageNumber { width:15px;}
#list #notice .firstPage { width:35px;}
#list #notice .nextPage { width:35px;}
#list #notice .pagingCell { width:100%; height:25px; padding-left:8%;}
#list #notice .nodata { width:100%; text-align:center; height:50px; font-weight:bold;}
#list2 { width:800px; border:solid 1px #dddddd; }
#list2 #freeboard {width:600px; font-size:11px; font-family : 맑은고딕, Lucida Grande, Verdana, Sans-serif;}
#list2 #freeboard tr { height:20px; }
#list2 #freeboard .freeHeader {font-weight:bold; text-align:center;}
#list2 #freeboard .freeRow {border-bottom:solid 1px #dddddd; }
#list2 #freeboard .freeTitleHead {width:250px;}
#list2 #freeboard .freeDateHead {width:75px;}
#list2 #freeboard .freeCountHead {width:75px;}
#list2 #freeboard .freeAuthorHead {width:100px;}
#list2 #freeboard .freeBoardHead {width:100px;}
</style>
</head>
<body>
<div>
<div id="list">
<table id="notice">
<tr class="noticeHeader"><td class="noticeTitleHead">제목</td><td class="noticeAuthorHead">등록자</td><td class="noticeCountHead">조회수</td><td class="noticeDateHead">작성일</td></tr>
</table>
</div>
</div>
<input type="button" value="test" onclick="getData();">
<script language="javascript">
var objTable2 = null; // page당 1번의 xml 로딩을 위해 페이지 전역변수로 선언
var param = $H({'page' : 1, 'layerid' : "content", 'listcount' : 10, 'pagingcount' : 10});
// 초기 값을 넘긴다.
function getData()
{
getAjaxData('/table.php', param, rowAppend);
}
function rowAppend(pValue)
{
objTable2 = new Table(pValue, "notice", true, true);
objTable2.insertRow();
}
</script>
</body>
</html>
테스트 방법1. 웹서버의 특정 위치에 table.xml을 저장한다.
2. table.js의 주소를 맞게 변경한다.
3. table.php를 웹 서버에 저장한다.
4. table.html의 getData() 함수에서 table.php를 호출 하는 주소를 맞게 수정한다.
5. table.html의 버튼을 누르면 table이 그려질 것이다.
Posted by SADBLUE