I. Introduction... 1 II. jquery jqgrid jqgrid 를위한 Spring MVC Controller 구현 JqGridVO 상속 BoardController.list(

Similar documents
PowerPoint Template

제이쿼리 (JQuery) 정의 자바스크립트함수를쉽게사용하기위해만든자바스크립트라이브러리. 웹페이지를즉석에서변경하는기능에특화된자바스크립트라이브러리. 사용법 $( 제이쿼리객체 ) 혹은 $( 엘리먼트 ) 참고 ) $() 이기호를제이쿼리래퍼라고한다. 즉, 제이쿼리를호출하는기호

2파트-07

var answer = confirm(" 확인이나취소를누르세요."); // 확인창은사용자의의사를묻는데사용합니다. if(answer == true){ document.write(" 확인을눌렀습니다."); else { document.write(" 취소를눌렀습니다.");

UNIST_교원 홈페이지 관리자_Manual_V1.0

Eclipse 와 Firefox 를이용한 Javascript 개발 발표자 : 문경대 11 년 10 월 26 일수요일

Ext JS À¥¾ÖÇø®ÄÉÀ̼ǰ³¹ß-³¹Àå.PDF

Javascript

다른 JSP 페이지호출 forward() 메서드 - 하나의 JSP 페이지실행이끝나고다른 JSP 페이지를호출할때사용한다. 예 ) <% RequestDispatcher dispatcher = request.getrequestdispatcher(" 실행할페이지.jsp");

쉽게 풀어쓴 C 프로그래밍

제8장 자바 GUI 프로그래밍 II

iii. Design Tab 을 Click 하여 WindowBuilder 가자동으로생성한 GUI 프로그래밍환경을확인한다.

INDEX 들어가기 고민하기 HTML(TABLE/FORM) CSS JS

Microsoft PowerPoint - web-part03-ch19-node.js기본.pptx

Spring Boot

신림프로그래머_클린코드.key

어댑터뷰

MAX+plus II Getting Started - 무작정따라하기

chap 5: Trees

- 이벤트의처리 <input type= button id= button1 value= 확인 /> <input type= button id= button2 value= 확인 /> 자바스크립트인경우 : document.getelementbyid( button1 ).oncl

Javascript.pages

I T C o t e n s P r o v i d e r h t t p : / / w w w. h a n b i t b o o k. c o. k r

JAVA PROGRAMMING 실습 08.다형성

Modern Javascript

Orcad Capture 9.x

API STORE 키발급및 API 사용가이드 Document Information 문서명 : API STORE 언어별 Client 사용가이드작성자 : 작성일 : 업무영역 : 버전 : 1 st Draft. 서브시스템 : 문서번호 : 단계 : Docum

슬라이드 1

구축환경 OS : Windows 7 그외 OS 의경우교재 p26-40 참조 Windows 의다른버전은조금다르게나타날수있음 Browser : Google Chrome 다른브라우저를사용해도별차이없으나추후수업의모든과정은크롬사용 한

Dialog Box 실행파일을 Web에 포함시키는 방법

PowerPoint 프레젠테이션

Spring Boot/JDBC JdbcTemplate/CRUD 예제

Microsoft PowerPoint - Java7.pptx

3장

Microsoft PowerPoint - web-part03-ch20-XMLHttpRequest기본.pptx

Remote UI Guide

혼자서일을다하는 JSP. 이젠일을 Servlet 과나눠서한다. JSP와서블릿의표현적인차이 - JSP는 <html> 내에서자바를사용할수있는수단을제공한다. - 서블릿은자바내에서 <html> 을작성할수있는수단을제공한다. - JSP나서블릿으로만웹페이지를작성하면자바와다양한코드가

게시판 스팸 실시간 차단 시스템

Microsoft PowerPoint 세션.ppt

슬라이드 1

- 목차 - - ios 개발환경및유의사항. - 플랫폼 ios Project. - Native Controller와플랫폼화면연동. - 플랫폼 Web(js)-Native 간데이터공유. - 플랫폼확장 WN Interface 함수개발. - Network Manager clas

서현수

PowerPoint Presentation

untitled

PowerPoint 프레젠테이션

Microsoft PowerPoint - ch10 - 이진트리, AVL 트리, 트리 응용 pm0600

KYO_SCCD.PDF

untitled

TP_jsp7.PDF

Prototype에서 jQuery로 옮겨타기

untitled

untitled

PHP & ASP

Modal Window

Microsoft PowerPoint UI-Event.Notification(1.5h).pptx

DocsPin_Korean.pages

Lab10

1. SNS Topic 생성여기를클릭하여펼치기... Create Topic 실행 Topic Name, Display name 입력후 Create topic * Topic name : 특수문자는 hyphens( - ), underscores( _ ) 만허용한다. Topi

API - Notification 메크로를통하여어느특정상황이되었을때 SolidWorks 및보낸경로를통하여알림메시지를보낼수있습니다. 이번기술자료에서는메크로에서이벤트처리기를통하여진행할예정이며, 메크로에서작업을수행하는데유용할것입니다. 알림이벤트핸들러는응용프로그램구현하는데있어

쉽게 풀어쓴 C 프로그래밍

[ 그림 8-1] XML 을이용한옵션메뉴설정방법 <menu> <item 항목ID" android:title=" 항목제목 "/> </menu> public boolean oncreateoptionsmenu(menu menu) { getme

JUNIT 실습및발표

목차 BUG offline replicator 에서유효하지않은로그를읽을경우비정상종료할수있다... 3 BUG 각 partition 이서로다른 tablespace 를가지고, column type 이 CLOB 이며, 해당 table 을 truncate

Analytics > Log & Crash Search > Unity ios SDK [Deprecated] Log & Crash Unity ios SDK. TOAST SDK. Log & Crash Unity SDK Log & Crash Search. Log & Cras

Macaron Cooker Manual 1.0.key

ibmdw_rest_v1.0.ppt

PowerPoint Presentation

untitled

EMBARCADERO TECHNOLOGIES (Humphery Kim) RAD Studio : h=p://tech.devgear.co.kr/ : h=p://blog.hjf.pe.kr/ Facebook : h=p://d.com/hjfactory :

PowerPoint 프레젠테이션

쉽게 풀어쓴 C 프로그래밍

Microsoft PowerPoint - 04-UDP Programming.ppt

로거 자료실

슬라이드 1

Interstage5 SOAP서비스 설정 가이드

Microsoft PowerPoint - CSharp-10-예외처리

C H A P T E R 2

C# Programming Guide - Types

SproutCore에 홀딱 반했습니다.

gnu-lee-oop-kor-lec10-1-chap10

오버라이딩 (Overriding)

Microsoft PowerPoint - web-part01-ch10-문서객체모델.pptx

C++ Programming

Microsoft Word - src.doc

Week13

adfasdfasfdasfasfadf

<property name="configlocation" value="classpath:/egovframework/sqlmap/example/sql-map-config.xml"/> <property name="datasource" ref="datasource2"/> *

MasoJava4_Dongbin.PDF

SOFTBASE XFRAME DEVELOPMENT GUIDE SERIES HTML 연동가이드 서울특별시구로구구로 3 동한신 IT 타워 1215 호 Phone Fax Co

rmi_박준용_final.PDF

1

MySQL-.. 1

Polly_with_Serverless_HOL_hyouk


표준프레임워크로 구성된 컨텐츠를 솔루션에 적용하는 것에 문제가 없는지 확인

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

Microsoft PowerPoint - web-part02-ch15-문서객체조작.pptx

Microsoft Word - ntasFrameBuilderInstallGuide2.5.doc

XSS Attack - Real-World XSS Attacks, Chaining XSS and Other Attacks, Payloads for XSS Attacks

Microsoft PowerPoint - 11주차_Android_GoogleMap.ppt [호환 모드]

쉽게 풀어쓴 C 프로그래밍

Transcription:

Anyframe jquery Plugin Version 1.0.1 저작권 2007-2011 삼성 SDS 본문서의저작권은삼성 SDS 에있으며 Anyframe 오픈소스커뮤니티활동의목적하에서자유로운이용이가능합니다. 본문서를복제, 배포할경우에는저작권자를명시하여주시기바라며본문서를변경하실경우에는원문과변경된내용을표시하여주시기바랍니다. 원문과변경된문서에대한상업적용도의활용은허용되지않습니다. 본문서에오류가있다고판단될경우이슈로등록해주시면적절한조치를취하도록하겠습니다.

I. Introduction... 1 II. jquery... 2 1. jqgrid... 3 1.1. jqgrid 를위한 Spring MVC Controller 구현... 3 1.1.1. JqGridVO 상속... 3 1.1.2. BoardController.list()... 3 1.2. jqgrid 를이용한 HTML 개발... 4 1.2.1. jqgrid 를위한 Javascript 라이브러리 dependency... 4 1.2.2. jqgrid Type 1... 4 1.2.3. jqgrid Type 2... 8 1.2.4. jqgrid 와 jstree 연동... 12 2. Quickpager... 14 2.1. jqgrid 와 quickpager 연동... 14 3. jstree... 15 3.1. jstree 의활용... 15 4. Upload... 21 4.1. uploadify 소개... 21 4.2. jqueryupload.js... 21 5. jquery-ui... 24 5.1. Autocomplete... 24 5.2. Tab widget... 25 5.3. Dialog widget... 28 5.4. Button widget... 30 5.5. Theme... 31 6. Validation... 35 ii

I.Introduction jquery Plugin 은 AJAX 를활용한 Spring MVC 기반의웹어플리케이션개발사례를제공할목적으로만들어졌다. jquery Plugin 은 JSON 형태의데이터를이용한공통 Controller 클래스및 jquery Javascript 프레임워크및이를바탕으로개발된오픈소스 UI Component 를활용하여작성된샘플코드와이를활용하는데필요한참조라이브러리들로구성되어있으며, Plugin 샘플코드에활용된 jquery UI 컴포넌트들로는 jqgrid, quickpager, jstree, jquery-ui(button, dialog, tab, autocomplete), uploadify 등이있다. Installation Command 창에서다음과같이명령어를입력하여 jquery plugin 을설치한다. mvn anyframe:install -Dname=jquery installed(mvn anyframe:installed) 혹은 jetty:run(mvn clean jetty:run) command 를이용하여설치결과를확인해볼수있다. Dependent Plugins Plugin Name Query [http://dev.anyframejava.org/docs/ anyframe/plugin/optional/query/1.1.2/ reference/htmlsingle/query.html] Version Range 2.0.0 > * Message Source 추가하기 jquery 플러그인은별도의 Message Source 파일을가지고있다. 플러그인을설치할때파일은다운로드되었으니, context-message.properties 파일에 <value>classpath:message/message-jquery</ value> 를추가하여사용하도록한다.

II.jQuery

1.jqGrid jqgrid 는웹상에서 tabular data 를표현하고조작하기위한솔루션을제공하는 AJAX 기반자바스크립트컨트롤이다.(http://www.trirand.com/jqgridwiki/doku.php) 1.1.jqGrid 를위한 Spring MVC Controller 구현 1.1.1.JqGridVO 상속 JqGridVO 는 jqgrid 적용을위한 Abstract Domain Class 로서 jqgrid 에서 parameter 로제공하는 page, sord, sidx 를 member 로가지고있으므로이클래스를상속받아 Domain 객체를구현하면별도의추가적인코딩없이 jqgrid 연동 Controller 를구현하는것이가능하다. 다음은 jqgrid 적용을위해서 JqGridVO 를상속받은 Board Domain Class 의코드일부이다. package org.anyframe.plugin.jquery.domain; public class Board extends JqGridVO implements Serializable{... 중략... 1.1.2.BoardController.list() 현재 jquery plugin 에서는 jquery 의 Grid 컴포넌트 (jqgrid plugin) 에서 Grid 를그릴때비즈니스서비스호출후반환되는 Page 객체를바로받을수있는것이아닌 Grid 에서인식할수있는 Key 와 Value 쌍의 Map 형태로 Model 객체에셋팅해줘야한다. 다음은 BoardController 클래스의일부이다. @RequestMapping(params = "method=list") public String list( @RequestParam(value = "searchkeyword", defaultvalue = "") String searchkeyword, @RequestParam(value = "searchcondition", defaultvalue = "") String searchcondition, @RequestParam(value = "communityid", required = false) String communityid, Board board, Model model, HttpServletRequest request) throws Exception {... 중략... Page resultpage = boardservice.getpaginglist(board); Map<String, Object> jsonmodel = new HashMap<String, Object>(); jsonmodel.put("page", resultpage.getcurrentpage() + ""); jsonmodel.put("total", resultpage.getmaxpage() + ""); jsonmodel.put("records", resultpage.gettotalcount() + ""); jsonmodel.put("rows", resultpage.getlist()); model.addallattributes(jsonmodel); return "jsonview"; 3

jqgrid 위의코드에서볼수있듯이비즈니스서비스수행후 Return 값이 org.anyframe.pagination.page 타입일경우 jquery 의 Grid 에서인식할수있는 key 값으로 jsonmodel 객체를셋팅해주고있다. 1.2.jqGrid 를이용한 HTML 개발 1.2.1.jqGrid 를위한 Javascript 라이브러리 dependency jqgrid 를사용하기위해서는 jquery, jquery-ui, jqgrid 라이브러리가필요하다. <!-- jquery --> <script type="text/javascript" src="<c:url value='/jquery/jquery/jquery-1.6.2.min.js'/>"></ script> <script type="text/javascript" src="<c:url value='/jquery/jquery/validation/ jquery.validate.js'/>"></script> <!-- jquery-ui --> <script type="text/javascript" src="<c:url value='/jquery/jquery/jquery-ui/jqueryui-1.8.16.custom.min.js'/>"></script> <!-- jqgrid --> <script type="text/javascript" src="<c:url value='/jquery/jquery/jqgrid/i18n/grid.localeen.js'/>"></script> <script type="text/javascript" src="<c:url value='/jquery/jquery/jqgrid/ jquery.jqgrid.min.js'/>"></script> <script type="text/javascript" src="<c:url value='/jquery/jquery/jqgrid/plugins/ grid.setcolumns.js'/>"></script> 참고 라이브러리참조선언의순서에주의하여야한다. jqgrid 의경우필요에따라추가적인플러그인라이브러리를호출하여사용할수있다.(jqGrid 위키사이트참조 : http://www.trirand.com/jqgridwiki/doku.php) jquery plugin 에서는크게두가지형태의 jqgrid 적용 type 을제공하고있다. 1.2.2.jqGrid Type 1 i. : 커뮤티니의게시물 list 를제공 ii. : row 단위로 select 할수있으며, quickpager 오픈소스컴포넌트와결합하여 page navigation 을제공 iii. : jquery-ui 에서제공하는 dialog widget 과연동하여등록 / 수정기능을제공 iv. : jquery-ui 에서제공하는 autocomplete 와연동하여목록에대한검색기능을제공 다음은 type 1 게시물리스트를출력하는 tabmain.jsp 파일의일부이다. function _creategridtype1(id) { var gridid = '#grid_' + id; var paginationid = gridid + '_pagination'; $(gridid).jqgrid({ url: "<c:url value='/jqueryboard.do?method=list' />", mtype:'post', datatype : "json", 4

jqgrid postdata : {'communityid' : id, 'searchkeyword' : '', 'searchcondition' : '', colnames : [ '<spring:message code="board.id" />', '<spring:message code="category.name" / >', '<spring:message code="board.title" />', '<spring:message code="board.contents" />', '<spring:message code="board.regid" />', '<spring:message code="board.regdate" />', '<spring:message code="community.id" />'], jsonreader: {repeatitems: false, colmodel : [ {key : true, name : 'postid', editable:false, hidden:true, {name : 'communityname', name : 'communityname', editable:false, hidden:true, {name : 'title', index : 'title', align : 'center', editable:true, {name : 'contents', index :'contents', align : 'left', editable:true, hidden : false, {name : 'regid', index : 'regid', align : 'center', editable:false, width:50, {name : 'regdate', index : 'regdate', align : 'center', sorttype:"date", editable:true,width:70, {name : 'communityid', name : 'communityid', editable:false, hidden:true], autowidth : true, height : "auto", viewrecords : true, rownum : 10, sortable : true, loadcomplete : function(xhr) { $(paginationid).quickpager( { pagesize: "10", pageunit: "10", pageindexid: 'grid_' + id + "_pageindex", searchbuttonid: 'grid_' + id + "_btnsearch", currentpage: $(gridid).getgridparam("page"), totalcount: $(gridid).getgridparam("records"), searchurl: "#" );, gridcomplete: function() { $("#_empty",gridid).addclass("nodrag nodrop"); //$("#grid").tabledndupdate(); $(gridid).setgridwidth($('#right').width() - 40); $(window).bind('resize', function() { $(gridid).setgridwidth($('#right').width() - 40); ).trigger('resize');, loaderror: function(xhr,st,err) { alert(err); ); $("button", ".buttons").button(); $(gridid + "_btnadd").click(function() { dialogmode = "add"; AnyframeUpload.options.refId = ''; $("#dialog-form").dialog( "open" ); ); $(gridid + "_btnedit").click(function() { var rownum = $(gridid).jqgrid('getgridparam','selrow'); if(rownum == null rownum == ""){ alert('<spring:message code="board.msg.delete.alert" />'); else{ dialogmode = "edit"; $.post( "<c:url value='/jqueryboard.do?method=get'/>", {postid : rownum, function(data) { 5

jqgrid $("#boardpostid").val(data.board.postid); $("#boardtitle").val(data.board.title); $("#boardcontents").val(data.board.contents); $("#boardregid").val(data.board.regid); $("#boardregdate").val(data.board.regdate); $("#communities").val(data.board.communityname).selected; AnyframeUpload.options.refId = data.board.postid; AnyframeUpload.loadAttachedFileList('uploadPane'); ); ); $( "#dialog-form" ).dialog( "open" ); $(gridid + "_btnremove").click(function() { var rownum = $(gridid).jqgrid('getgridparam','selrow'); var postid = $(gridid).getcell(rownum, 'postid'); $(gridid).delgridrow( rownum, { reloadaftersubmit:true, msg:'<spring:message code="board.msg.delete.confirm" />', deldata:{postid: postid, url:"<c:url value='/jqueryboard.do?method=remove'/>" ); ); $(gridid + "_btnrefresh").click(function() { $(gridid).trigger("reloadgrid"); ); $(gridid + "_btnsearch").click(function() { $(gridid).setgridparam({ page: $(gridid + "_pageindex").val(), postdata: { searchkeyword:$(gridid + "_searchkeyword").val(), searchcondition:$(gridid + "_searchcondition").val() ); $(gridid).setgridparam({url:"<c:url value='/jqueryboard.do?method=list'/ >").trigger("reloadgrid"); ); /* auto click by enter key */ $(gridid + "_searchkeyword").keypress(function (e) { if (e.which == 13){ $(gridid + "_pageindex").val('1'); $(gridid + "_btnsearch").trigger("click"); return false; ); $(gridid + "_searchkeyword").autocomplete({ source : function(request, response){ logger.log('call'); $.ajax({ 'url' : '<c:url value="/jqueryboard.do?method=searchkeyword"/>', 'type' : 'POST', 'async' : false, 'data' : 'searchkeyword=' + $(gridid + "_searchkeyword").val() + '&searchcondition=' + $(gridid + "_searchcondition").val() + '&communityid=' + _currentnodeid, 'datatype' : 'json', 'success' : function(data){ 6

jqgrid logger.log('return:data.r.length=' + data.r.length); response(data.r); );, minlength : 1, select : function(event, ui) { logger.log('autocomplete selected:' + ui.item.value); $(gridid + '_searchkeyword').val(ui.item.value); $(gridid + '_pageindex').val('1'); $(gridid + '_btnsearch').trigger("click"); return false; ); 위와같이 jqgrid 로구현된리스트의모습은아래와같다. 7

jqgrid 1.2.3.jqGrid Type 2 i. : 카테고리에속한커뮤니티 list를제공 ii. : cell 단위로 select/edit 할수있으며, scroll down 방식의 page navigation을제공 iii. : jquery-ui의 date-picker 컴포넌트와연동하여 date 형식의 data 수정 8

jqgrid iv. : jquery-ui 에서제공하는 autocomplete 와연동하여목록에대한검색기능을제공 v. : 그리드내버튼을통한삭제기능제공 다음은 type 2 방식의커뮤니티리스트를출력하는 tabmain.jsp 파일의일부이다. var iscellsaved; var lastsel_row; var lastsel_col; function _creategridtype2(id) { var gridid = '#grid_' + id; $(gridid).jqgrid({ url: "<c:url value='/jquerycommunity.do?method=list' />", mtype:'post', datatype : "json", postdata : {'categoryid' : id, colnames : ['<spring:message code="community.id" />','<spring:message code="community" /> <spring:message code="community.name" />', '<spring:message code="community.desc" />', '<spring:message code="community.redid" />', '<spring:message code="community.regdate" />', '<spring:message code="category.id" />', '<spring:message code="category.name" />',''], jsonreader: {repeatitems: false, colmodel : [ {key : true, name : 'communityid', editable:false, hidden:true, {name : 'communityname', index : 'communityname', align : 'center', editable:true, {name : 'communitydesc', index :'communitydesc', align : 'left', editable:true, {name : 'regid', index : 'regid', align : 'center', editable:false, width:75, {name : 'regdate', index : 'regdate', align : 'center', sorttype:"date", width:100, editable:true, editoptions: { datainit: function(element) { $(element).datepicker({ dateformat: 'yy/mm/dd', onselect: function(datatext, inst){ $(gridid).jqgrid('savecell',lastsel_row, lastsel_col); );, {name : 'categoryid', name : 'categoryid', editable:false, hidden:true, {name : 'categoryname', name : 'categoryname', editable:false, hidden:true, {name: 'myac', width:40, fixed:true, sortable:false, resize:false, formatter:'actions', formatoptions:{editbutton:false, keys:true, deloptions:{msg:'<spring:message code="board.msg.delete.confirm" />', onclicksubmit:function(rp_ge, rowid){ $(gridid).delgridrow( rowid, { reloadaftersubmit:true, deldata:{communityid: rowid, url:"<c:url value='/jquerycommunity.do?method=remove'/>", aftercomplete : function (response, postdata, formid) { $('#${treeid').jstree("remove","#" + rowid); return false; ); ], scroll : true, 9

jqgrid height : 220, multiselect : false, viewrecords : true, rownum : 10, sortable : true, celledit: true, cellsubmit:"remote", cellurl:"<c:url value='/jquerycommunity.do?method=updatecell'/>", beforeeditcell: function(id,name,val,irow,icol){ lastsel_row = irow; lastsel_col = icol; iscellsaved = false;, beforesavecell: function(id,name,val,irow,icol){ iscellsaved = true;, aftersavecell:function(rowid, cellname, value, irow, icol){ if(cellname=="communityname"){ $('#${treeid').jstree('get_selected').find("#"+rowid+" a").html('<ins class="jstreeicon"> </ins>' + value);, gridcomplete: function() { $("#_empty",gridid).addclass("nodrag nodrop"); $(gridid).setgridwidth($('#right').width() - 40); $(window).bind('resize', function() { $(gridid).setgridwidth($('#right').width() - 40); ).trigger('resize');, loaderror: function(xhr,st,err) { alert(err); ); $(gridid + "_btnsearch").click( function() { $(gridid).setgridparam({ postdata: { searchkeyword:$(gridid + "_searchkeyword").val(), searchcondition:$(gridid + "_searchcondition").val() ); $(gridid).setgridparam({url:"<c:url value='/jquerycommunity.do?method=list'/ >").trigger("reloadgrid"); return false; ); /* auto click by enter key */ $(gridid + "_searchkeyword").keypress(function (e) { if (e.which == 13){ $(gridid + "_btnsearch").trigger("click"); return false; ); $(gridid + "_searchkeyword").autocomplete({ source : function(request, response){ logger.log('call'); $.ajax({ 'url' : '<c:url value="/jquerycommunity.do?method=searchkeyword"/>', 'type' : 'POST', 'async' : false, 'data' : 'searchkeyword=' + $(gridid + "_searchkeyword").val() + '&searchcondition=' + $(gridid + "_searchcondition").val() + '&categoryid=' + id, 'datatype' : 'json', 'success' : function(data){ 10

jqgrid logger.log('return:data.r.length=' + data.r.length); response(data.r); );, minlength : 1, select : function(event, ui) { logger.log('autocomplete selected:' + ui.item.value); $(gridid + '_searchkeyword').val(ui.item.value); $(gridid + '_pageindex').val('1'); $(gridid + '_btnsearch').trigger("click"); return false; ); //$(gridid).jqgrid('gridresize'); 위와같이 jqgrid 로구현된리스트의모습은아래와같다. 11

jqgrid 1.2.4.jqGrid 와 jstree 연동 i. : 커뮤니티리스트가삭제되거나수정되는경우, jstree 에도이를반영 $(gridid).jqgrid({... 중략... {name: 'myac', width:40, fixed:true, sortable:false, resize:false, formatter:'actions', 12

jqgrid formatoptions:{editbutton:false, keys:true, deloptions:{msg:'<spring:message code="board.msg.delete.confirm" />', onclicksubmit:function(rp_ge, rowid){ $(gridid).delgridrow( rowid, { reloadaftersubmit:true, deldata:{communityid: rowid, url:"<c:url value='/jquerycommunity.do?method=remove'/>", aftercomplete : function (response, postdata, formid) { $('#${treeid').jstree("remove","#" + rowid); // 커뮤니티삭제시 tree 반영 return false; ); ],... 중략... aftersavecell:function(rowid, cellname, value, irow, icol){ if(cellname=="communityname"){ // 커뮤니티수정시 tree 반영 $('#${treeid').jstree('get_selected').find("#"+rowid+" a").html('<ins class="jstreeicon"> </ins>' + value);,... 중략... ); 참고 jqgrid 를사용하여리스트를작성할때너무많은양의데이터를한꺼번에출력하려고하면리스트를출력하는데있어서많은시간이걸리거나브라우저가멈추는현상이발생할수있다. 이에한번에출력하는데이터의건수는 100 개이내로하며데이터가많을경우 pager 를이용해 paging 처리할것을권고한다. 13

2.Quickpager jqgrid 는기본적으로 Paging 처리를위한 Pager 를제공하고있다. Anyframe 에서는 pagenavigator 와유사한 Pager 출력을위해 quickpager 를확장하여사용하고있다. quickpager 를사용하기위해서는리스트 Script 내의 loadcomplete 함수안에 paging 정보를셋팅해주고 search 버튼을클릭하는이벤트를발생시키도록한다. 2.1.jqgrid 와 quickpager 연동 관련 jquery 코드는다음과같다. jquery("#grid").jqgrid({... 중략... loadcomplete : function(xhr) { $(paginationid).quickpager( { pagesize: "10", pageunit: "10", pageindexid: 'grid_' + id + "_pageindex", searchbuttonid: 'grid_' + id + "_btnsearch", currentpage: $(gridid).getgridparam("page"), totalcount: $(gridid).getgridparam("records"), searchurl: "#" );,... 중략... ); $(gridid + "_btnsearch").click(function() { $(gridid).setgridparam({ page: $(gridid + "_pageindex").val(), postdata: { searchkeyword:$(gridid + "_searchkeyword").val(), searchcondition:$(gridid + "_searchcondition").val() ); $(gridid).setgridparam({url:"<c:url value='/jqueryboard.do?method=list'/ >").trigger("reloadgrid"); ); 위와같이 Script 코드가작성되면 pagenavigator 출력부분에아래와같이 div 영역을표시해준다. <div id="${gridid_boardnav"> <div id="${gridid_pagination" class="pagination"></div> </div> 위와같이정의한 quickpager 는아래와같은 pagenavigator 를출력하게된다 jqgrid 에서제공하는 pager jqgrid 에서도 paging 처리를위한간편한 pager 를제공한다. 구현방법은아래와같다. //jqgrid 속성설정내에정의 pager : jquery('#pager') <!-- JSP 내의 pager 출력부분에정의 --> <div id="pager" class="scroll" style="text-align: center;"></div> 14

3.jstree jstree 는계층적으로조직된데이타를 tree 형태로보여주기위해제공되는 jquery 기반의오픈소스 UI 컴포넌트이다. jstree is a javascript based, cross browser tree component. It is packaged as a jquery plugin(http:// www.jstree.com) 3.1.jsTree 의활용 jstree 는 0.9.9a 이후 1.0-rc3 로업그레이드되면서일부 api 및사용법이변경되었다. 여기서는 1.0-rc3 버전을기준으로설명한다. jstree 는크게 html, json, xml 방식의 data loading 방식을제공하며, 여기서는 html 방식을제공한다. html 은 JSTL 을사용하여다음과같이표시할수있다. <!-- start of tree --> <div id="tree"> <span>listnode</span> <ul> <li id="root" rel="root"> <a href='#'>root</a> <c:set var="prevdepth" value="-1"/> <c:foreach var="node" items="${treelist"> <c:if test="${node.depth > prevdepth"> <ul> </c:if> <c:if test="${prevdepth > node.depth"> <c:foreach begin="${node.depth" end="${prevdepth - 1" step="1"> </ul></li> </c:foreach> </c:if> <li id="${node.nodeid" parentid="${node.parentid" depth="${node.depth" rel="${node.type"> <a href='#'>${node.nodename</a> <c:if test="${node.haschild == 0"> </li> </c:if> <c:set var="prevdepth" value="${node.depth"/> </c:foreach> </li> </ul> </div> <!-- end of tree --> 위와같은 html 트리관련 jquery 구현코드는아래와같다. // tree definition $(document).ready(function() { $('#${treeid').jstree({ 'plugins' : ["themes","html_data","ui","crrm","search","types","hotkeys","contextmenu"], //,"dnd","html_data", 'checkbox', "cookies", 'themes' : { 'theme' : 'default', 'dots' : false, 'icons' : true 15

jstree, 'contextmenu' : { 'items' : createcontextmenu, 'search' : { 'case_insensitive' : true, 'types' : { 'valid_children' : ["root"], 'types' : { 'CA' : {, 'CO' : {// change icon for community 'icon' : {'image' : '<c:url value="/sample/images/tree_types/leaficons.png"/>', 'core' : { 'initially_open' : ['ROOT'], 'animation' : 0 ).bind("select_node.jstree", function (e, data) { // event handling for node select logger.log('select_node:' + data.rslt.obj.attr("id")); if(data.rslt.obj.attr('id') == 'ROOT') { // Root is selected logger.log('root Selected'); _currentnodetype = 'ROOT'; _currentnodeid = 'ROOT'; $tabs.tabs('select', '#tabs-0'); else if(data.rslt.obj.attr('rel') == 'CA') { // Category is selected logger.log('category Selected'); _currentnodetype = data.rslt.obj.attr('rel'); _currentnodeid = data.rslt.obj.attr('id'); // commuity list load $.get("<c:url value='/jquerycategory.do?method=get'/>", {'categoryid' : data.rslt.obj.attr('id'), function(r) { addtab(r.category.categoryname, data.rslt.obj.attr('id')); ); else if(data.rslt.obj.attr('rel') == 'CO'){ // Community is selected logger.log('community Selected'); _currentnodetype = data.rslt.obj.attr('rel'); _currentnodeid = data.rslt.obj.attr('id'); // community's board list load $.get("<c:url value='/jquerycommunity.do?method=get'/>", {'communityid' : data.rslt.obj.attr('id'), function(r) { addtab(r.community.communityname, data.rslt.obj.attr('id')); ); $('#community').val(data.rslt.obj.attr('id')); ).bind("remove.jstree", function(e, data) { // event handling for node delete data.rslt.obj.each(function() { if($(data.rslt.obj).attr("rel") == 'CO') { // for community logger.log('community removed:' + $(data.rslt.obj).attr("id")); $.ajax({ async : false, type : 'POST', url : '<c:url value="/jquerycommunity.do?method=remove"/>', data : { "communityid" : $(data.rslt.obj).attr("id"), success : function(r) { logger.log('111'); data.inst.refresh(); logger.log('222'); 16

jstree $tabs.tabs('remove', '#tabs-' + $(data.rslt.obj).attr("id")); logger.log('333'); $("#community option[value='" + $(data.rslt.obj).attr("id") + "']").remove(); logger.log('444');, error : function() { $.jstree.rollback(data.rlbk); ); else if($(data.rslt.obj).attr("rel") == 'CA'){ // for category $.ajax({ async : false, type : 'POST', url : '<c:url value="/jquerycategory.do?method=remove"/>', data : { "categoryid" : $(data.rslt.obj).attr("id"), success : function(r) { data.inst.refresh(); $tabs.tabs('remove', '#tabs-' + $(data.rslt.obj).attr("id"));, error : function() { $.jstree.rollback(data.rlbk); ); ); ); // tree search event handler for button click case $('#treesearch').click(function(e) { $('#${treeid').jstree('search', $('#searchkeyword').val()); ); // tree search event handler for enter key down case $('#searchkeyword').keydown(function(e) { if(e.keycode == '13') { $('#${treeid').jstree('search', $('#searchkeyword').val()); return false; ); ); 트리의노드타입에따른컨텍스트메뉴구성은다음과같이구현한다. /** * context menu generate for tree */ function createcontextmenu(node) { var default_object = { 'create' : {, 'edit' : {, 'remove' : { ; if(node.attr('id') == 'ROOT') { default_object = { create : { label : '<spring:message code="category.context.add" />', action : function(obj) { logger.log('create category : ' + obj.attr('id')); 17

jstree opencategoryform(obj, 'create');, edit : false, remove : false ; else if(node.attr('rel') == 'CA') { default_object = { create : { label : '<spring:message code="community.context.add" />', action : function(obj) { logger.log('create community : ' + obj.attr('id')); opencommunityform(obj, 'create');, edit : { label : '<spring:message code="category.context.edit" />', action : function(obj) { logger.log('edit category : ' + obj.attr('id')); opencategoryform(obj, 'edit');, remove : { label : '<spring:message code="category.context.delete" />', _disabled : node.children('ul').length > 0? true : false, action : function(obj) { logger.log('remove category : ' + obj.attr('id')); if(this.is_selected(obj)) { this.remove(); else { this.remove(obj); ; else if(node.attr('rel') == 'CO') { default_object = { create : false, edit : { label : '<spring:message code="community.context.edit" />', action : function(obj) { logger.log('edit community : ' + obj.attr('id')); opencommunityform(obj, 'edit');, remove : { label : '<spring:message code="community.context.delete" />', action : function(obj) { logger.log('remove community : ' + obj.attr('id')); if(this.is_selected(obj)) { logger.log('try remove 1: ' + obj.attr('id')); this.remove(); else { logger.log('try remove 2: ' + obj.attr('id')); this.remove(obj); ; return default_object; 18

jstree 트리검색기능구현은다음과같이한다. // tree definition $(document).ready(function() { // tree search event handler for button click case $('#treesearch').click(function(e) { $('#${treeid').jstree('search', $('#searchkeyword').val()); ); // tree search event handler for enter key down case $('#searchkeyword').keydown(function(e) { if(e.keycode == '13') { $('#${treeid').jstree('search', $('#searchkeyword').val()); return false; ); ); Tree 유틸을사용한데이타의정렬 트리형식으로표시할데이타를표시순서대로정렬하기위해서, Tree 와 TreeNode 유틸클래스를활용하여정렬하는것이가능하다.(DB 에서가져온 1 차데이타는 depth 와 display order 순으로정렬되어있어야함.) 다음은카테고리와커뮤니티의데이타를각각가져와서 tree 형태로정렬시킨 CommunityServiceImpl 클래스의 gettreelist() 메소드의구현코드이다. public List<Map<String, String>> gettreelist() throws Exception { List<Map<String, String>> trees = new ArrayList<Map<String, String>>(); List<Community> communities = (List<Community>) communitydao.getlist(new Community()); List<Category> categories = (List<Category>) categorydao.getlist(new Category()); Tree tree = null; if (categories!= null) { tree = new Tree(); int cl = categories.size(); for (int i = 0; i < cl; i++) { Category ca = (Category) categories.get(i); Map<String, String> item = new HashMap<String, String>(); item.put("nodeid", ca.getcategoryid()); item.put("parentid", "ROOT"); item.put("depth", "0"); item.put("displayorder", Integer.toString(i)); item.put("nodename", ca.getcategoryname()); item.put("type", "CA"); tree.add(ca.getcategoryid(), "ROOT", item); if (communities!= null) { int ccl = communities.size(); for (int i = 0; i < ccl; i++) { Community co = (Community) communities.get(i); Map<String, String> item = new HashMap<String, String>(); item.put("nodeid", co.getcommunityid()); item.put("parentid", co.getcategoryid()); item.put("depth", "1"); item.put("displayorder", "0"); 19

jstree item.put("nodename", co.getcommunityname()); item.put("type", "CO"); tree.add(co.getcommunityid(), co.getcategoryid(), item); trees = (List<Map<String, String>>) tree.getlist(); return trees; 다음은 jstree 를이용하여 Tree 를출력한화면이다. 20

4.Upload jquery 와 AJAX 를활용한 Multi file 첨부기능을구현하여제공하고있다. 4.1.uploadify 소개 uploadify 는 jquery 와 flash Object 를통하여간편하게 multi file 첨부를구현할수있게해주는오픈소스컴포넌트이다. 자세한내용은 http://www.uploadify.com/ 사이트를참조하기바란다. 4.2.jqueryUpload.js jquery plugin 에서는 uploadify 를사용하여파일첨부를구현한별도의서브셋을 jqueryupload.js 에별도로구현하였다. 이를통해서파일첨부관련코드가비즈니스로직에추가되는것을최소화하도록의도된것이다. 파일업로드컴포넌트는다음과같이인스턴스화시킨다. $(document).ready(function() { $('#uploadpane').attachment({ 'contextroot' : '${ctx', 'callback' : function() { savepost(); // after file upload ); ); 위의코드에서 'uploadpane', 즉, 첨부파일 UI 가표시될영역은게시물등록 form 인 "dialog-form" 영역에선언되어있다. <!-- board form start --> <form:form id="dialog-form" name="dialog-form" title="board Form"> <fieldset> <input type="hidden" name="postid" id="boardpostid"> <input type="hidden" name="regid" id="boardregid"> <input type="hidden" name="regdate" id="boardregdate"> <table summary="jquery" width="100%">... 중략... <tr> <td><label><spring:message code="board.attach" /></label></td> <td> <div id="uploadpane"></div> <input type="hidden" name="refid" id="refid" value=""/> </td> </tr> </table> </fieldset> </form:form> <!-- board form end --> 파일을업로드한후실제게시물을등록시켜야하므로실제로게시물을저장하는스크립트함수인 savepost() 를 callback 으로선언한다. 등록의경우첨부된파일첨부대표 ID(Reference ID) 는 AnyframeUpload.options.refId 에저장되어있으므로이를파라메터로전달하여게시물정보에저장될수있도록한다. 여기서는 Reference ID 를게시물 ID 와일치시키도록하였으므로 BoardServiceImpl.create() 에서다음과같이처리하여야한다. 21

Upload @Service("jqueryBoardService") @Transactional(rollbackFor = { Exception.class ) public class BoardServiceImpl implements BoardService{ @Inject @Named("jqueryUploadInfoService") private UploadInfoService uploadservice; public int create(board board, String filerefid) throws Exception { SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS", new Locale("ko", "KR")); String postid = "POST-" + formatter.format(new Date()); board.setpostid(postid); board.setregdate((new SimpleDateFormat("yyyy/MM/dd")).format(new Date())); int r = boarddao.create(board); if(r > 0) uploadservice.updatefilerefid(filerefid, postid); // reference id 를 post id 와일치시킨다. return r;... 중략... 참고 jqueryupload.js 내에구현된내용은하나의구현사례이므로구현요건에따라자유롭게재구성될수있다. 다음은 uploadify 와 jqueryupload.js 를활용하여파일첨부기능을구현한것이다. 22

Upload 23

5.jquery-ui jquery 는 UI 플러그인을통해 UI 컴포넌트를추가적으로제공하며, 테마기능과연동된 UI 컴포넌트를통해강력한 User Inteface 를 Pure-HTML 환경에서도구현할수있도록도와주고있다. jquery 에서제공하는 UI 컴포넌트에대한자세한기능은 http://jqueryui.com 에서확인하기바란다. Anyframe jquery plugin 에서는 jquery ui 1.8.16 버전을바탕으로 autocomplete, tab, dialog, button, theme 기능을활용하여제공하고있다. 5.1.Autocomplete autocomplete 는사용자가입력한 prefix 를가지고자동완성기능을제공하는 UI 컴포넌트이다. 아래자바스크립코드는게시물리스트의검색에 autocomplete 기능을적용한내용이다. success 부분과 select 부분의코드활용을주목하기바란다. $(gridid + "_searchkeyword").autocomplete({ source : function(request, response){ logger.log('call'); $.ajax({ 'url' : '<c:url value="/jqueryboard.do?method=searchkeyword"/>', 'type' : 'POST', 'async' : false, 'data' : 'searchkeyword=' + $(gridid + "_searchkeyword").val() + '&searchcondition=' + $(gridid + "_searchcondition").val() + '&communityid=' + _currentnodeid, 'datatype' : 'json', 'success' : function(data){ logger.log('return:data.r.length=' + data.r.length); response(data.r); );, minlength : 1, select : function(event, ui) { logger.log('autocomplete selected:' + ui.item.value); $(gridid + '_searchkeyword').val(ui.item.value); $(gridid + '_pageindex').val('1'); $(gridid + '_btnsearch').trigger("click"); return false; ); 다음은 Autocomplete 을적용한결과이다. 24

jquery-ui 5.2.Tab widget Tab 위젯은동일한목적을가지고있으나성격이상이한 UI 를분할하여화면복잡도를낮추고효율적인 User Inteface 구현을도와주는자바스크립트기반의 UI 컴포넌트이다. var _currenttabid = ''; var $tabs = null; 25

jquery-ui // 탭을추가 function addtab(title, id) { if($('#tabs-' + id).length > 0) { $tabs.tabs("select", '#tabs-' + id); else{ $tabs.tabs("add", "#tabs-" + id, title ); $(document).ready(function(){ $tabs = $('#tabs').tabs({ tabtemplate: '<li><a href="<%="#"%>{href"><%="#"%>{label</a><span class="ui-icon uiicon-close">remove Tab</span></li>', add: function( event, ui ) { // 탭이추가되었을때의이벤트핸들링 var tab_content = ''; tab_content = _currentnodetype + ':' + _currentnodeid; $(ui.panel).append(tab_content); $(ui.tab).attr('nodeid', _currentnodeid); $(ui.tab).click(); if(_currentnodetype == 'CO') { $.get('<c:url value="/jqueryboard.do?method=getgridtype1"/>', {'gridid' : 'grid_' + _currentnodeid, function(data) { $(ui.panel).html(data); _creategridtype1(_currentnodeid); ); else if(_currentnodetype == 'CA'){ $.get('<c:url value="/jqueryboard.do?method=getgridtype2"/>', {'gridid' : 'grid_' + _currentnodeid, function(data) { $(ui.panel).html(data); _creategridtype2(_currentnodeid); );, select : function(event, ui) { logger.log('tab selected!!:' + $(ui.tab).attr('nodeid')); _currenttabid = $(ui.tab).attr('nodeid'); ); // 탭의 'x' 버튼클릭시해당탭을제거한다. $( "#tabs span.ui-icon-close" ).live( "click", function() { var index = $( "li", $tabs ).index( $( this ).parent() ); $tabs.tabs( "remove", index ); ); ); 다음은 jstree 의구현코드중일부이며, 트리의노드가선택되었을때 tab 을추가하도록하는부분을주목하기바란다.... 생략.bind("select_node.jstree", function (e, data) { // event handling for node select logger.log('select_node:' + data.rslt.obj.attr("id")); if(data.rslt.obj.attr('id') == 'ROOT') { // Root is selected logger.log('root Selected'); _currentnodetype = 'ROOT'; _currentnodeid = 'ROOT'; $tabs.tabs('select', '#tabs-0'); // ROOT 선택시에는탭을추가하지않고포커스만전환한다. else if(data.rslt.obj.attr('rel') == 'CA') { // Category is selected logger.log('category Selected'); 26

jquery-ui _currentnodetype = data.rslt.obj.attr('rel'); _currentnodeid = data.rslt.obj.attr('id'); // commuity list load $.get("<c:url value='/jquerycategory.do?method=get'/>", {'categoryid' : data.rslt.obj.attr('id'), function(r) { addtab(r.category.categoryname, data.rslt.obj.attr('id')); // 커뮤니티리스트를위한탭을추가한다. ); else if(data.rslt.obj.attr('rel') == 'CO'){ // Community is selected logger.log('community Selected'); _currentnodetype = data.rslt.obj.attr('rel'); _currentnodeid = data.rslt.obj.attr('id'); // community's board list load $.get("<c:url value='/jquerycommunity.do?method=get'/>", {'communityid' : data.rslt.obj.attr('id'), function(r) { addtab(r.community.communityname, data.rslt.obj.attr('id')); // 게시물리스트를위한탭을추가한다. ); $('#community').val(data.rslt.obj.attr('id')); 생략... 다음은 Tab widget 이적용된화면이다. 왼쪽트리선택시해당화면이우측에탭을활용하여추가되고또한삭제가가능하다. 또한트리에서커뮤니티나카테고리가삭제되는경우우측화면의 tab section 도같이삭제되도록구현되어있다. 27

jquery-ui 5.3.Dialog widget Dialog 위젯은 Window Popup 이나브라우져의 Message Alert Box 를 Layed 된 html 요소를활용하여대체할수있도록한것이다. jquery UI 에서제공하는 Dialog 위젯은아래코드와같이다양한옵션및이벤트핸들링을구성할수있다. $(document).ready(function() { 28

jquery-ui... 생략 // Dialog form definition for Category $( "#category-form" ).dialog({ autoopen: false, width: 400, height:"auto", modal: true, resizable:true, close: function() { clearcategorydialog(); );... 생략 ); 다음은 Dialog 를적용한결과이다. Modal 형태의 Window 로서 'Cancel' 버튼과타이틀바의 'x' 버튼및 ESC 키를통해창을닫을수있으며, window 의사이즈조절도가능하도록옵션처리되었다. 29

jquery-ui 5.4.Button widget jquery UI 에서제공하는버튼위젯의특징은 <button> 태그를그대로활용한다는점으로웹표준을그대로준수하고있다는점이다. <div class="buttons"> <button id="${gridid_btnadd">add</button> 30

jquery-ui <button id="${gridid_btnedit">edit</button> <button id="${gridid_btnremove">remove</button> <button id="${gridid_btnrefresh">refresh</button> </div> 위의버튼 tag 들에다음과같이간단한코딩으로위젯을적용하는것이가능하다. 이벤트핸들링또한위젯적용여부에관계없이핸들링하고있다. $("button", ".buttons").button(); // 위젯적용 // 'Add' 버튼의 onclick 이벤트핸들링 $(gridid + "_btnadd").click(function() { dialogmode = "add"; AnyframeUpload.options.refId = ''; $("#dialog-form").dialog( "open" ); ); 다음은게시물리스트에서 Button 위젯을적용한결과이다. 5.5.Theme jquery-ui 에서제공하는 UI 컴포넌트와이를기반으로작성된 jqgrid 는 jquery-ui 에서제공하는 Theme 관련 feature 를통해다양한테마를손쉽게변경할수있다. 다음은테마변경에대한구현코드이다. // Theme Switcher $(document).ready(function(){ $('#themeswitcher').change(function() { var cssurl = '<c:url value="/jquery/jquery/jquery-ui/themes/"/>'; var theme = $('#themeswitcher').val(); $('#uitheme').attr('href',cssurl + theme + '/jquery-ui-1.8.16.custom.css'); ); ); 다음은여러가지테마를변경하여적용한화면이다. Redmond( 기본테마 ) 31

jquery-ui Blitzer 32

jquery-ui South Street 33

jquery-ui 34

6.Validation jquery 에서는 validation plugin 을통해 jquery 를적용한 AJAX 기반의웹어플리케이션의 form validation 수단을제공한다. validation plugin 은다양한 rule 과메시지표시를제공하지만, 예제에서는가장태그의 attribute 를통해가장간단히구현하는방법을소개한다. 좀더자세한내용은 http://docs.jquery.com/plugins/validation 을참조하기바란다. * validation 을위한 css 설정 validation 메시지표시를위해본예제코드에서는 admin_new.css 에다음의내용을추가하였다. /* validation */ label.error { width: 10em;float: none; color: red; padding-left:.5em; vertical-align: top; * { font-family: verdana", " 돋움 ";font-size: 9pt p { clear: both;.submit { margin-left: 12em; em { font-weight: bold; padding-right: 1em; vertical-align: top; vallidation 적용을위해서는해당되는 form 을다음과같이지정한다. $('#dialog-form').validate(); 해당 form 의 html 은아래와같으며, class 에 'required' 값을부여함으로써필수필드로지정된다. 또한 maxlength 값지정을통해최대길이를제한할수있다. <!-- board form start --> <form:form id="dialog-form" name="dialog-form" title="board Form"> <fieldset>... 생략... <table summary="jquery" width="100%"> <tr> <td><spring:message code="board.title" /></td> <td><input type="text" name="title" id="boardtitle" class="dialog_text required" maxlength="25"></td> </tr> <tr> <td><spring:message code="board.contents" /></td> <td><textarea name="contents" id="boardcontents" class="dialog_text required" maxlength="128"></textarea></td> </tr>... 생략... </table> </fieldset> </form:form> <!-- board form end --> 실제 form 값들의 validation 을수행하기위해서는 valid() 함수를사용한다. /** * Add/Modify for Post */ function savepost() {... 생략 35

Validation if(!$('#dialog-form').valid()) { // validation logger.log('dialog-form is invalid.'); return false;... 생략 다음은 validation 을게시물등록 / 수정 form 에적용한결과이다. 36