728x90

개요

  String 변수가 null이나 undefined인지 확인하는 기능은 쓰임새가 많다. Java라면 그런 목적으로 Apache commons의 StringUtils를 많이 사용하고, String 관련 메서드를 사용할 때 null 체크를 생략시켜줘서 편리하다. JavaScript에서도 클래스 및 메서드 명칭이 동일하고 동작구조도 최대한 동일하면 협업 시 팀원들이 빠르게 기능을 이해하고 사용할 수 있을 거란 생각이 들었다.

샘플코드

  Apache commons의 StringUtils의 메서드를 모두 구현하려면 시간이 많이 들어서 당장 자주 쓸 기능만 작성했다. 나머지는 필요해질 때마다 추가 작성하면 된다.

  [2023-03-28] isEmpty, isNotEmpty, isNotBlank 추가.

<StringUtils.js>

/**
 * Apache commons의 StringUtils 클래스의 편리한 기능을 자바스크립트에서 그대로 사용하기 위한 객체. 
 */
const StringUtils = {};
/**
 * 
 * @param str {String} 확인할 문자열
 * @return 문자열 길이. 문자열이 없으면 0.
 */
StringUtils.length = function(str) {
	if (str) {
		return str.length;
	}
	
	return 0;
};

/**
 * <p>Checks if a String is empty ("") or null or undefined.</p>
 *
 * <pre>
 * StringUtils.isEmpty(undefined) = true
 * StringUtils.isEmpty(null)      = true
 * StringUtils.isEmpty("")        = true
 * StringUtils.isEmpty(" ")       = false
 * StringUtils.isEmpty("bob")     = false
 * StringUtils.isEmpty("  bob  ") = false
 * </pre>
 * 
 * @param cs {String} 확인할 문자열
 * @return {boolean} <code>true</code> if the cs is empty or null or undefined.
 */
StringUtils.isEmpty = function(cs) {
	if (cs == undefined || cs == null) {
		return true;
	}
	
	if (!("string" === typeof(cs) || cs instanceof String)) {
		throw new TypeError("Argument cs must be type of String.");
	}
	
	return cs.length == 0;
};

/**
 * <p>Checks if a String is not empty ("") and not null and not undefined.</p>
 *
 * <pre>
 * StringUtils.isNotEmpty(undefined) = false
 * StringUtils.isNotEmpty(null)      = false
 * StringUtils.isNotEmpty("")        = false
 * StringUtils.isNotEmpty(" ")       = true
 * StringUtils.isNotEmpty("bob")     = true
 * StringUtils.isNotEmpty("  bob  ") = true
 * </pre>
 * 
 * @param cs {String} 확인할 문자열
 * @return {boolean} <code>true</code> if the cs is not empty and not null and not undefined.
 */
StringUtils.isNotEmpty = function(cs) {
	return !StringUtils.isEmpty(cs);
};

/**
 * <p>Checks if a CharSequence is empty (""), null or whitespace only.</p>
 *
 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
 *
 * <pre>
 * StringUtils.isBlank(undefined) = true
 * StringUtils.isBlank(null)      = true
 * StringUtils.isBlank("")        = true
 * StringUtils.isBlank(" ")       = true
 * StringUtils.isBlank("bob")     = false
 * StringUtils.isBlank("  bob  ") = false
 * </pre>
 * 
 * @param cs {String} 확인할 문자열
 * @return {boolean} <code>true</code> if the cs is undefined, null, empty or whitespace only.
 */
StringUtils.isBlank = function(cs) {
	if (cs == undefined || cs == null) {
		return true;
	}

	if (!("string" === typeof(cs) || cs instanceof String)) {
		throw new TypeError("Argument cs must be type of String.");
	}

    let strLen;
    if ((strLen = cs.length) == 0) {
        return true;
    }
//    for (var i = 0; i < strLen; i++) {
//        if (!StringUtils.isWhitespace(cs.charAt(i))) {
//            return false;
//        }
//    }
        if (!StringUtils.isWhitespace(cs)) {
            return false;
        }
    return true;
};

/**
 * <pre>
 * StringUtils.isNotBlank(undefined) = false
 * StringUtils.isNotBlank(null)      = false
 * StringUtils.isNotBlank("")        = false
 * StringUtils.isNotBlank(" ")       = false
 * StringUtils.isNotBlank("bob")     = true
 * StringUtils.isNotBlank("  bob  ") = true
 * </pre>
 *
 * @param cs  the CharSequence to check, may be null
 * @return <code>true</code> if the CharSequence is
 *  not empty and not null and not whitespace only
 */
StringUtils.isNotBlank = function(cs) {
    return !StringUtils.isBlank(cs);
}

/**
 * Apache commons의 StringUtils 클래스에는 없지만, 유용할 듯하여 별도 메서드를 추가함.
 * @param cs {String} 확인할 문자열
 * @return {boolean} <code>true</code> if the str is undefined, null, empty or whitespace only.
 */
StringUtils.isWhitespace = function(cs) {
	if (!("string" === typeof(cs) || cs instanceof String)) {
		throw new TypeError("Argument cs must be type of String.");
	}
	
    if (/^\s+$/g.test(cs)) {
        return true;
    }
    return false;
};

/**
*
* <pre>
* StringUtils.trim(undefined)     = undefined
* StringUtils.trim(null)          = null
* StringUtils.trim("")            = ""
* StringUtils.trim("     ")       = ""
* StringUtils.trim("abc")         = "abc"
* StringUtils.trim("    abc    ") = "abc"
* </pre>
*
* @param str {String} the String to be trimmed, may be null
* @return {String} the trimmed string, <code>null</code> if null String input
*/
StringUtils.trim = function(str) {
	return (str == null || str == undefined) ? str : str.trim();
};
/**
 *
 * <pre>
 * StringUtils.trimToNull(undefined)     = null
 * StringUtils.trimToNull(null)          = null
 * StringUtils.trimToNull("")            = null
 * StringUtils.trimToNull("     ")       = null
 * StringUtils.trimToNull("abc")         = "abc"
 * StringUtils.trimToNull("    abc    ") = "abc"
 * </pre>
 *
 * @param str {String} the String to be trimmed, may be null
 * @return {String} the trimmed String,
 *  <code>null</code> if only chars &lt;= 32, empty or null String input
 */
StringUtils.trimToNull = function(str) {
	const ts = StringUtils.trim(str);
	return StringUtils.isEmpty(ts) ? null : ts;
};
/**
 *
 * <pre>
 * StringUtils.trimToEmpty(undefined)     = ""
 * StringUtils.trimToEmpty(null)          = ""
 * StringUtils.trimToEmpty("")            = ""
 * StringUtils.trimToEmpty("     ")       = ""
 * StringUtils.trimToEmpty("abc")         = "abc"
 * StringUtils.trimToEmpty("    abc    ") = "abc"
 * </pre>
 *
 * @param str {String} the String to be trimmed, may be null
 * @return {String} the trimmed String, or an empty String if <code>null</code> input
 */
StringUtils.trimToEmpty = function(str) {
	return (str == null || str == undefined) ? "" : str.trim();
};

 

단위테스트코드

<StringUtilsTest.html>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>StringUtilsTest</title>
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.19.3.css">
</head>
<body>
	<div id="qunit"></div>
	<div id="qunit-fixture"></div>
	<script src="https://code.jquery.com/qunit/qunit-2.19.3.js"></script>
	<script src="./StringUtils.js"></script>
	<script>
		QUnit.module('isEmpty', function() {
			let undfnd;
			const nll = null;
			const empty = "";
			const blank = new String(" ");
			const longBlank = new String("      ");
			const notBlank = "asdf";
			QUnit.test('isEmpty', function(assert) {
				assert.true(StringUtils.isEmpty(undfnd), 'undefined');
				assert.true(StringUtils.isEmpty(nll), 'null');
				assert.true(StringUtils.isEmpty(empty), 'empty');
				assert.false(StringUtils.isEmpty(blank), 'blank');
				assert.false(StringUtils.isEmpty(longBlank), 'long blank');
				assert.false(StringUtils.isEmpty(notBlank));
			});
		});

		QUnit.module('isNotEmpty', function() {
			let undfnd;
			const nll = null;
			const empty = "";
			const blank = new String(" ");
			const longBlank = new String("      ");
			const notBlank = "asdf";
			QUnit.test('isNotEmpty', function(assert) {
				assert.false(StringUtils.isNotEmpty(undfnd), 'undefined');
				assert.false(StringUtils.isNotEmpty(nll), 'null');
				assert.false(StringUtils.isNotEmpty(empty), 'empty');
				assert.true(StringUtils.isNotEmpty(blank), 'blank');
				assert.true(StringUtils.isNotEmpty(longBlank), 'long blank');
				assert.true(StringUtils.isNotEmpty(notBlank));
			});
		});

		QUnit.module('isBlank', function() {
			let undfnd;
			const nll = null;
			const empty = "";
			const blank = new String(" ");
			const longBlank = new String("      ");
			const notBlank = "asdf";
			QUnit.test('isBlank', function(assert) {
				assert.true(StringUtils.isBlank(undfnd), 'undefined');
				assert.true(StringUtils.isBlank(nll), 'null');
				assert.true(StringUtils.isBlank(empty), 'empty');
				assert.true(StringUtils.isBlank(blank), 'blank');
				assert.true(StringUtils.isBlank(longBlank), 'long blank');
				assert.false(StringUtils.isBlank(notBlank));
			});
		});

		QUnit.module('isNotBlank', function() {
			let undfnd;
			const nll = null;
			const empty = "";
			const blank = new String(" ");
			const longBlank = new String("      ");
			const notBlank = "asdf";
			QUnit.test('isBlank', function(assert) {
				assert.false(StringUtils.isNotBlank(undfnd), 'undefined');
				assert.false(StringUtils.isNotBlank(nll), 'null');
				assert.false(StringUtils.isNotBlank(empty), 'empty');
				assert.false(StringUtils.isNotBlank(blank), 'blank');
				assert.false(StringUtils.isNotBlank(longBlank), 'long blank');
				assert.true(StringUtils.isNotBlank(notBlank));
			});
		});

		QUnit.module('trim', function() {
			QUnit.test('trim', function(assert) {
				assert.equal(StringUtils.trim(undefined), undefined);
				assert.equal(StringUtils.trim(null), null);
				assert.equal(StringUtils.trim(""), "");
				assert.equal(StringUtils.trim(" "), "");
				assert.equal(StringUtils.trim("    "), "");
				assert.equal(StringUtils.trim("asdf"), "asdf");
				assert.equal(StringUtils.trim("  asdf  "), "asdf");
			});
		});

		QUnit.module('trimToEmpty', function() {
			const blank = new String(" ");
			const longBlank = new String("      ");
			const notBlank = "asdf";
			QUnit.test('trimToEmpty', function(assert) {
				assert.equal(StringUtils.trimToEmpty(undefined), "");
				assert.equal(StringUtils.trimToEmpty(null), "");
				assert.equal(StringUtils.trimToEmpty(""), "");
				assert.equal(StringUtils.trimToEmpty("     "), "");
				assert.equal(StringUtils.trimToEmpty("abc"), "abc");
				assert.equal(StringUtils.trimToEmpty("    abc    "), "abc");
			});
		});
	</script>
</body>
</html>

단위테스트결과

 

반응형
728x90

  datagrid 내의 컬럼에 툴팁을 사용하고 싶을 경우 formatter를 이용해 정의하는 것을 생각해볼 수 있다. 그런데, formatter 내에서 tooltip 생성 코드를 바로 사용해도 생성이 되지 않는다. 아마도 datagrid 내의 formatter 내에서 구성한 엘리먼트가 formatter 코드가 모두 실행된 후에야 생성되기 때문일 것이다.( formatter 코드가 실행 완료되기 전에는 아직 존재하지 않는 엘리먼트여서 툴팁을 붙일 엘리먼트가 존재하지 않기 때문일 것으로 생각된다.)

  때문에 formatter 내에서는 툴팁을 띄울 이벤트에 대신 툴팁을 생성하는 함수를 연결해 놓는다. 이벤트가 발생할 때는 이미 요소가 있으므로 툴팁 생성이 가능하다.

  1. 툴팁에 노출할 데이터는 글로벌 변수에 저장한다.(datagrid를 그리면서 툴팁 생성까지 되지 않기 때문에 분리해서 실행이 되도록 데이터를 별도영역에 저장해 놓는다.)
  2. formatter를 이용해 툴팁이 필요한 컬럼의 엘리먼트에 고유한 아이디를 생성하고, 툴팁 생성 이벤트를 설정한다.
  3. 툴팁이 필요한 곳에 마우스이벤트가 발생하면, 툴팁을 생성한다.(현재로서는 엘리먼트에 EasyUI의 tooltip이 생성되었는지 따로 판단할 방법이 없다. 때문에 오류가 발생하면 생성되지 않은 것으로 판단하고 생성하도록 한다.)

<html>

	<table id="dg" title="Example" style="width:600px"  data-options="
            rownumbers:true,
            singleSelect:true,
            autoRowHeight:false,
            pagination:true,
            pageSize:10">
    <thead>
        <tr>
            <th data-options="field:'tupleId',width:100,align:'right'">Tuple ID</th>
            <th data-options="field:'name',width:100">Name</th>
            <th data-options="field:'files',width:200,formatter:uiService.fileFormatter">Files</th>
        </tr>
    </thead>

<JavaScript>

/**
 * 데이터.
 */
var gData = {};
gData.files = [];

/**
 * UI서비스.
 */
var uiService = {};
/**
 * 파일 정보 엘리먼트에 고유한 아이디를 생성하고, 툴팁 생성 이벤트를 설정한다.
 */
uiService.fileFormatter = function(value, row) {
	let result = null;

	gData.files[row.tupleId] = row.files;
	if (row.files) {
		const len = row.files.length;
		if(len > 0){
			const $tempElem = $('<div id="temp'+ row.tupleId + '"></div>');
			const fileListDiv = '<a href="javascript:void(0)" id="tipFile' + row.tupleId 
					+ '"onmouseover="uiService.buildFilesTooltip(\''+ row.tupleId + '\')"><font color="blue"><u>첨부파일 : ' + len + ' 건</u></font></a>';
			$tempElem.html(fileListDiv);

			result = $tempElem.html();
		}
	} else {
		result = '';
	}
	return result;
};

/**
 * 툴팁을 생성한다.
 */
uiService.buildFilesTooltip = function(tupleId) {
	try {
		const opts = $('#tipFile'+tupleId).tooltip('options');
		// EasyUI에 tooltip이 생성되어 있는지 여부를 판단할 방법이 지원되지 않기에, try~catch를 이용한다.
	} catch (e) {
		// tooltip이 없으면 오류가 발생하므로 생성한다.
		const files = gData.files[tupleId];
		var fileIds = [];
		var html = "";
		$.each(files, function(index, value) {
			html += '<div style="margin:5 5 10 5px;">';
			html += '<a href="javascript:void(0)" style="color:#c60;" onClick="fileService.downloadFile(\''
					+ value.fileId
					+ '\')"><u><b>'
					+ value['fileName'] + '</b></u></a>';
			html += '</div>';
			
			fileIds.push(value.fileId);

			if (index > 0 && (files.length - 1) == index) {
				html += '<div style="margin:5px;">'
					  + '<a href="javascript:void(0)" style="color:#c60;" '
					  + 'onClick="fileService.downloadAllFile([' + fileIds + '])"><u><b>일괄 다운로드</b></u></a></div>';
			}
		});
		const $tipFile = $('#tipFile'+ tupleId);
		$tipFile.tooltip({
			position : 'right',
			content : '<span style="color:#fff;">' + html + '</span>',
			onShow : function() {
				var t = $(this);
				t.tooltip('tip').unbind().bind('mouseenter', function() {
					t.tooltip('show');
				}).bind('mouseleave', function() {
					t.tooltip('hide');
				}).css({
					backgroundColor : '#d9d9d9',
					borderColor : '#d9d9d9',
					opacity : 1
				});
			}
		});
		$tipFile.tooltip('show');
	}
}

/**
 * 파일서비스.
 */
var fileService = {};
fileService.downloadFile = function(fileId) {
	// 예시이므로 실제로 파일을 다운로드하지는 않음.
	alert("fileId:" + fileId);
};
fileService.downloadAllFile = function(fileIds) {
	// 예시이므로 실제로 파일을 다운로드하지는 않음.
	alert("fileIds:" + fileIds);
};

/**
 * 초기화.
 */
$(document).ready(function() {
	const list = [
		{"tupleId":"2", "name": "홍길동", "files":[{"fileId":1, "fileName": "증명사진1"}]},
		{"tupleId":"4", "name": "홍길일", "files":[{"fileId":2, "fileName": "증명사진2"},{"fileId":3, "fileName": "여권사진"}]},
		{"tupleId":"8", "name": "홍길삼"},
		{"tupleId":"16", "name": "홍길사"}
	];
	$('#dg').datagrid().datagrid('loadData', list);
});
반응형
728x90

*getSelected : 현재 선택된 탭 가져오기.

var tab = $('#tt').tabs('getSelected');


*getTabIndex : 탭 인덱스 가져오기.

var tab = $('#tt').tabs('getSelected');
var index = $('#tt').tabs('getTabIndex',tab);
alert("index:" + index);

*id 확인하기.

const tab = $('#infoTabs').tabs('getSelected');
const tabOptions = tab.panel('options');
const infoTabId = tabOptions.id;
반응형
728x90

개요

Ajax 통신중에 프로그레스바 마스크를 호출하여 사용자의 조작을 막고 처리중임을 알릴 필요가 있다.

빠른 길잡이

jQuery UI 이용

  html로 프로그레스바를 준비한다.

  스타일

<style>
#progressbar {
	margin-top: 20px;
}

.progress-label {
	font-weight: bold;
	text-shadow: 1px 1px 0 #fff;
}

/* Dialog의 우상단 닫기버튼[x]를 숨기는 설정. */
.ui-dialog-titlebar-close {
	display: none;
}
</style>

  html

<!-- Progressbar Dialog -->
<div id="dialog" title="Progressbar Example" style="display: none;">
  <div class="progress-label">Starting...</div>
  <div id="progressbar"></div>
</div>

beforeSend에서 jQuery UI 프로그레스바를 노출시킨한다.

(*[2022-11-14] modal 속성이 누락되었어서 추가함.)

beforeSend : function() {
	var progressbar = $("#progressbar");
	progressbar.progressbar({
		value: false,
	});
	//progressbar.progressbar("value", 0);
	$('#dialog').dialog({
		modal: true
	});
}

complete에서 jQuery 프로그레스바를 비노출시킨다.

complete/*always*/ : function() {
	$('#dialog').dialog('close');
}

EasyUI 이용

beforeSend에서 EasyUI 프로그레스바를 노출시킨한다.

beforeSend : function(jqXHR, settings) {
	$.messager.progress({
		title:'요청을 처리하는 중입니다.',
		msg:'처리가 끝날 때까지 잠시만 기다려주세요...'
	});
}

complete에서 EasyUI 프로그레스바를 비노출시킨다.

complete/*always*/ : function() {
	$.messager.progress('close');
}

해설

jQuery Ajax에서 beforeSend는 요청 전 항상 실행된다.

또한 complete는 응답을 받고 success와 error 어느 쪽이더라도 그 후에 실행된다.

반응형
728x90

  EasyUI의 [Documentation]을 보면 Properties 항목을 볼 수 있다.
  하지만 Properties 조회 명령어는 properties가 아닌 options이기 때문에 주의가 필요하다.

*예시)

const tab = $('#infoTabs').tabs('getSelected');
const tabOptions = tab.panel('options');
const infoTabId = tabOptions.id;
반응형
728x90

 원래는 이글루스를 사용했었습니다. 아직 미숙한 개발자다 보니 일하면서, 공부하면서 쓸만하다 싶은 코드 같은 것들을 정리해서 올리곤 했었는데, 막상 올려놓아도 코드가 눈에 잘 들어오지가 않더군요.

 그러다가 많은 분들이 사용하는 SystaxHighlighter의 존재를 알게 됐는데, 정작 이글루스에서는 사용할 수가 없더군요. 아예 개인홈페이지를 만들까도 생각했지만, 아직은 거기까지 신경쓸 여유는 안 되는 것 같아, 우선은 SystaxHighlighter가 사용 가능한 티스토리로 옮기게 되었습니다.

 예상이지만, 아마도 이글루는 예전처럼 일상 얘기 등에 사용하고, 티스토리는 일과 관련된 용도로 쓰게 될 것 같습니다. 이글루에 있는 프로그래밍 관련 글들도 천천히 다 옮겨와야겠습니다.

 하여튼 그런 이유로 SystaxHighlighter의 사용법 요약

※상세한 사용법을 보시려면 여기로 → SyntaxHighlighter 2.X를 tistory에 적용하기.(사실 저도 이 글 보고 알았습니다.)

 1. http://alexgorbatchev.com/wiki/SyntaxHighlighter:Download에서 다운로드받는다.

 2. 압축을 풀고, scripts 폴더와 styles 폴더의 파일들을 적당한 곳에 위치시킨다.
※티스토리는 [관리자 화면 > 스킨 > HTML/CSS편집 > 파일 업로드]에 올립니다.

 3. <head>안에 SyntaxHighlighter 스크립트를 페이지에 추가시킨다. 추가시켜야 할 내용은 압축파일의 test.html에 나와있습니다.

	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	

 (또는 http://alexgorbatchev.com/wiki/SyntaxHighlighter:Usage 참고)

 ※티스토리는 [관리자 화면 > 스킨 > HTML/CSS편집 > HTML/CSS편집]에 추가합니다.

 4. SyntaxHighlighter 기능을 사용할 코드는 <PRE> 안에 넣습니다. 이 때 class에 보여주고자 하는 프로그래밍 언어의 Brush aliases를 지정해주면 적절한 형태로 나오게 됩니다.

	<pre class="brush: c-sharp;">
	function test(t)
	{
		alert(t);

		return t;
	}
	</pre>

 어떤 언어에 어떤 Brush aliases를 지정해줘야 하는지는 http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes를 참고합니다.

반응형

+ Recent posts