본문으로 건너뛰기

DropDownTextList

Tom Select 2.6.2 기반 검색·조건 태그 컨트롤입니다. 키·표시값을 검색하고 그룹별 후보를 선택합니다. 태그를 클릭하면 AND/OR와 비교 연산자를 편집합니다.

<syn_dropdowntextlist id="animals" syn-datafield="Conditions"
syn-events="['initialize','change']"
syn-options="{
optgroups:[{value:'mammal',label:'Mammal'},{value:'bird',label:'Bird'}],
optgroupField:'class',valueField:'value',labelField:'name'
}"></syn_dropdowntextlist>
const control = syn.uicontrols.$dropdowntextlist;
control.setValue('animals', [
{class:'mammal',value:'dog',name:'Dog'},
{class:'mammal',value:'cat',name:'Cat'},
{class:'bird',value:'duck',name:'Duck'}
]);
control.addItem('animals', 'dog');
const saved = control.getValue('animals');
// [{groupID:'mammal',logic:'AND',operator:'eq',key:'dog',value:'Dog'}]
control.clear('animals');
control.setValue('animals', saved);

반환값과 식별​

선택 태그는 기본적으로(groupDisplay:'tooltip') 항목 이름만 보여주고, 그룹 이름(optgroups.label, 그룹이 등록되지 않았다면 그룹 ID, 그룹 ID도 비어 있으면 기타)은 태그에 마우스를 올렸을 때의 툴팁(title)과 접근성 이름으로 제공합니다. groupDisplay:'inline'을 지정하면 편집 버튼 왼쪽에 그룹 이름을 함께 표시하고, groupDisplay:'badge'를 지정하면(hideLogicBadge:false일 때만) AND/OR 배지 뒤에 그룹 이름을 이어서 보여줍니다(AND · 직원 명, 일반 텍스트의 key 표시와 같은 방식이며 길면 그룹 이름 부분만 줄임표로 자르고 그룹 ID가 비어 있는 후보는 AND/OR만 표시). range 그룹의 범위 태그는 groupDisplay 값과 관계없이 항상 값만 보여주고 그룹 이름은 툴팁으로 제공합니다. 그룹 이름(inline일 때)과 편집 버튼은 클릭 또는 Enter/Space/F2로 같은 태그를 선택하고 편집창에 포커스를 이동합니다. 편집창은 선택한 태그 아래에 표시하며 화면 아래 공간이 부족하면 위로 이동하고, 오른쪽 경계를 벗어나지 않도록 조정합니다. 스크롤·창 크기 변경 시 위치를 다시 계산합니다. 취소·Escape·적용 후에는 해당 태그의 편집 버튼으로 포커스를 돌려줍니다. 적용·취소 버튼은 Tabler btn-group(적용은 btn btn-primary, 취소는 btn)이며 높이는 콤보박스와 같은 34px입니다.

hideLogicBadge:false이면 AND/OR를 왼쪽 상단 배지로 보여주고(기본값 true는 배지를 만들지 않아 AND/OR는 툴팁과 편집창으로만 확인), 비교 조건은 왼쪽 테두리로 구분합니다. 배지를 숨기면 컨테이너에 dttl-hide-badge 클래스가 붙어 배지가 튀어나올 자리로 둔 태그 위쪽 여백(margin-top)이 0이 되고 줄 간격(row-gap)이 5px로 줄어듭니다. 이 설정은 초기화 때 한 번만 반영합니다. 편집창의 연결·비교 선택값과 목록 항목도 같은 Tabler 색상을 사용하며 글자색은 흰색입니다. 전체 조건은 태그에 마우스를 올리거나 편집 버튼의 접근성 이름으로 확인할 수 있습니다.

구분값Tabler 색상
연결AND / ORblue / red
비교같음 (eq)blue
비교작게 (lt)green
비교같거나작게 (lte)cyan
비교크게 (gt)pink
비교같거나크게 (gte)orange
비교다름 (neq)red
비교시작함 (begins_with)gray-600 (회색)
비교끝남 (ends_with)purple
비교포함 (contains)teal
비교미포함 (not_contains)indigo

색상은 컨트롤 CSS의 data-dttl-logic·data-dttl-operator 규칙에서 공통 관리하며 --tblr-* 변수를 사용합니다. 그룹 추가·제거 시 선택 태그의 그룹 표시도 갱신합니다.

getValue()는 선택 순서의 새 객체 배열을 반환하며 빈 선택은 []입니다. maxItems:1이어도 배열입니다.

필드의미
groupIDoptgroupField 속성값. 생략된 후보 그룹은 빈 문자열
logicAND 또는 OR. setValue에서 생략하면 AND
operatoreq, lt, lte, gt, gte, neq, begins_with, ends_with, contains, not_contains. setValue에서 생략하면 contains
keyvalueField 속성값
valuelabelField 속성값. 사용자가 별도로 입력하는 비교값이 아님

연산자는 각각 같음, 작게, 같거나작게, 크게, 같거나크게, 다름, 시작함, 끝남, 포함, 미포함입니다. 컨트롤은 조건을 수집하며 조건 평가나 SQL 생성은 수행하지 않습니다.

그룹·키는 대소문자를 구분하는 문자열로 정규화합니다. 같은 그룹·키는 한 번만 선택할 수 있습니다. 다른 그룹에 같은 키가 있으면 addItem({groupID:'mammal',key:'dog'})처럼 참조 객체를 전달해야 합니다. 모호한 문자열 키는 오류입니다. DOM을 반환하는 메서드는 실제 HTMLElement를 반환합니다.

setValue 구분​

입력동작
후보 객체 배열후보 목록 교체. 남아 있는 그룹·키의 선택 조건 유지 및 표시값 갱신. 사라진 후보 선택 해제
{groupID,logic,operator,key,value} 배열전체 선택 복원. 없는 후보는 등록하고 중복 조건은 첫 항목만 선택
문자열 또는 문자열 배열기존 후보의 키로 선택 재설정. 알 수 없는 키는 오류
[]선택만 해제. 후보는 유지

혼합 배열이나 잘못된 조건은 변경 전에 오류를 발생시킵니다. 조건 객체는 groupID, key가 필요하고 value는 후보가 없을 때 표시값으로 사용하며, logic과 operator를 생략하면 AND, contains를 적용합니다. 후보 스키마의 optgroupField가 groupID이거나 valueField가 key이면 후보와 조건 객체를 구분할 수 없으므로 이때는 logic 또는 operator가 있는 객체만 조건으로 보고, 생략하려면 addOptions()로 후보를 공급한 뒤 addItem()을 사용합니다.

silent:true는 컨트롤 change 및 기반 select의 change 이벤트를 억제합니다. item_add, item_remove, clear 등 개별 이벤트는 발생할 수 있습니다. 변경이 없는 선택 재설정은 change를 발생시키지 않습니다. HandStack 거래 setter가 전달하는 세 번째 인자의 메타데이터 객체는 silent로 처리하지 않습니다.

메서드​

모듈 호출은 syn.uicontrols.$dropdowntextlist.method(elID, ...args)입니다. getControl(elID)로 받은 인스턴스에서는 elID 없이 호출합니다.

메서드동작
addOption(data,user_created=false), addOptions(data[],user_created=false)후보 추가. 동일 그룹·키는 유지하며 드롭다운은 자동 갱신하지 않음
updateOption(ref,data)후보와 선택 태그 갱신. 그룹·키 변경 시 기존 후보와 충돌하면 오류
removeOption(ref)후보와 해당 선택 제거
getOption(ref,create=false)후보 DOM 반환. 필요 시 렌더링
getAdjacent(element,direction)이전(-1)/다음(1) 후보 DOM
refreshOptions(triggerDropdown)검색 목록 갱신
clearOptions(clearFilter?)선택하지 않은 후보 제거. 콜백이 true인 후보와 선택된 후보는 유지
clearFilter(option,ref)기본 유지 판단: 현재 선택된 후보이면 true
clear(silent)선택 전체 해제
getItem(ref)선택 태그 DOM
addItem(ref|text,silent)캐럿 위치에 선택 추가. 후보에 없는 문자열은 create:true일 때 일반 텍스트로 추가
removeItem(ref|HTMLElement,silent)선택 해제
createItem(value,callback?)create 설정으로 선택. 후보의 키·표시값과 같으면 그 후보, 아니면 일반 텍스트 생성
refreshItems()선택 태그 다시 렌더링
addOptionGroup(id,data), removeOptionGroup(id), clearOptionGroups()그룹 추가·제거. 그룹 제거 자체는 후보를 삭제하지 않음
on(event,handler), off(event,handler?), trigger(event,...args)이벤트 구독·해제·실행
open(), close(), positionDropdown()드롭다운 열기·닫기·위치 갱신
focus(), blur()입력 포커스 이동
lock(), unlock()포커스 가능한 입력 잠금·해제. 태그 편집·삭제도 잠금
disable(), enable()포커스를 포함한 사용자 조작 비활성화·활성화
getValue(), setValue(value,silent)후보 조건 조회·데이터 공급·선택 복원. 일반 텍스트는 제외
getGroupConditions(list)[{operator,value,logic}] 목록을 operator와 logic이 같은 것끼리 묶어 [{name,args,logic}]로 반환하는 정적 함수(컨트롤 인스턴스에는 없음). 아래 "조건 묶음 변환" 절 참고
getSummaryText(options?)선택한 조건을 사용자에게 보여줄 텍스트로 반환. 아래 "조건 요약 텍스트" 절 참고
getNormalText(), setNormalText(items,silent)일반 텍스트 [{logic,operator,key,value}] 조회·교체. 아래 "일반 텍스트" 절 참고
setRange(groupID,start,end), getRange(groupID)range 그룹의 시작·끝 범위 설정·조회. 아래 "범위 입력" 절 참고
setCaret(index), isFull()캐럿 이동·선택 한도 도달 여부
clearCache(), setTextboxValue(str)렌더링 캐시 초기화·검색 문자열 설정
load(query)load 콜백 호출. 최신 요청만 반영
sync()source select의 후보·선택을 읽고 기존 조건 메타데이터 유지
destroy()이벤트·팝오버·DOM·컨트롤 레지스트리 정리. 같은 요소 재초기화 가능

ref는 문자열 키 또는 {groupID,key}입니다. clearOptions 콜백 인자는 원래 후보 데이터와 ref입니다. persist:false이면 user_created 후보는 선택 해제 시 제거됩니다(일반 텍스트는 설정과 관계없이 제거). getControl().source는 내부 기반 select이며 option의 value는 JSON.stringify([groupID,key]), text는 표시값입니다. 외부에서 이 select를 수정한 후 sync()를 호출할 수 있습니다. 화면의 syn-datafield 값은 공개 getValue/setValue를 사용합니다.

이벤트​

초기화 전에 onInitialize, onChange 등 Tom Select 이름의 콜백 옵션을 제공하거나 syn-events와 페이지 event.<elID>_<event>를 사용합니다. 초기화 후에는 인스턴스 on을 사용합니다. 핸들러의 this는 컨트롤 인스턴스입니다.

이벤트인자
initialize, focus, blur, clear, option_clear, optgroup_clear, destroy없음
change조건 객체 배열, 일반 텍스트 객체 배열(getValue(), getNormalText()). 논리·연산자 편집도 포함
item_add, item_remove선택한 항목 하나 {groupID,logic,operator,key,value}(일반 텍스트는 {normal:true,logic,operator,key,value}), 태그 HTMLElement. 조건이 지정되지 않은 항목은 logic:'AND', operator:'contains'로 전달
item_select태그 HTMLElement
option_add{groupID,key}, 원래 후보 데이터(일반 텍스트는 {normal:true,logic,operator,key,value} 두 인자 모두)
option_remove{groupID,key}(일반 텍스트는 {normal:true,logic,operator,key,value})
option_change조건 편집창의 적용으로 논리·연산자가 바뀐 항목 하나 {groupID,logic,operator,key,value}(일반 텍스트는 {normal:true,logic,operator,key,value}), 변경 전 {logic,operator}. change 다음에 호출. 범위 편집창의 변경에는 발생하지 않음
optgroup_addid, 그룹 데이터
optgroup_removeid
dropdown_open, dropdown_close드롭다운 HTMLElement
type검색 문자열
load원래 후보 데이터 배열

페이지 핸들러와 DOM 이벤트의 중복 호출을 막기 위해 컨트롤 이벤트는 초기화 시 직접 연결합니다. destroy에서 원래 syn-events를 복원합니다.

옵션과 비동기 제공자​

드롭다운 크기는 입력 영역의 width와 별도로 설정합니다.

옵션기본값설정값
dropdownWidth'100%': 컨트롤 폭'auto': 현재 표시 목록·그룹 제목에 맞춤, 숫자 또는 '320px': 고정 너비
dropdownHeight'auto': 내용에 맞춤, 목록 영역 최대 200px숫자 또는 '180px': 드롭다운 전체의 고정 높이

숫자는 양수 픽셀값입니다. 기본 너비는 컨트롤 폭('100%')이며, 기본 높이는 기존 Tom Select의 크기 스타일을 유지합니다. dropdownWidth:'auto'는 검색·후보 갱신·비동기 조회·창 크기 변경 시 다시 계산하며 화면 오른쪽을 넘지 않도록 제한합니다. 고정 높이에서 내용이 넘치면 목록 내부를 스크롤합니다. null도 해당 옵션의 기본값으로 처리합니다. 후보 목록을 세로로 스크롤하면 현재 그룹의 헤더(.optgroup-header)가 목록 상단에 고정되고 다음 그룹의 헤더가 올라오면 밀려납니다. 목록에 그려진 그룹이 둘 이상이면 목록 위쪽에 그룹 이름 버튼 줄(div.dttl-group-nav, Tabler btn-group)이 나타나고, 버튼을 누르면 해당 그룹의 헤더가 상단에 오도록 부드럽게 스크롤합니다. 버튼을 눌러도 입력 포커스는 유지되며(tabindex="-1") 목록을 스크롤하면 상단에 닿은 그룹의 버튼이 활성(active)으로 표시됩니다. 검색으로 그룹이 바뀌면 버튼도 다시 만들고, 그룹이 하나뿐이거나 후보가 없는 그룹(범위 그룹)은 버튼에 포함하지 않습니다. 버튼이 많으면 가로로 스크롤하며 dropdownHeight를 고정했을 때는 버튼 줄 높이만큼 목록 높이를 줄입니다. groupNav:false이면 버튼 줄을 만들지 않습니다. 운영체제의 "동작 줄이기" 설정이면 즉시 이동합니다.

<!-- 기본값: 컨트롤 폭, 기존 높이 -->
<syn_dropdowntextlist id="defaultSize" syn-options="{}"></syn_dropdowntextlist>
<!-- 목록에 맞춘 너비, 기본 높이 -->
<syn_dropdowntextlist id="autoSize" syn-options="{dropdownWidth:'auto'}"></syn_dropdowntextlist>
<!-- 드롭다운 전체 크기 고정 -->
<syn_dropdowntextlist id="fixedSize" syn-options="{dropdownWidth:320,dropdownHeight:180}"></syn_dropdowntextlist>

기본값은 maxItems:null, hideSelected:true, clearAfterSelect:true, create:false, persist:true, groupDisplay:'tooltip'('tooltip': 그룹 이름을 툴팁으로, 'inline': 태그 안에 표시, 'badge': AND/OR 배지 뒤에 표시, 배지를 표시할 때만 동작), hideLogicBadge:true(false이면 태그 왼쪽 위에 AND/OR 배지를 표시), groupNav:true(그룹 이동 버튼, 아래 설명), loadThrottle:200, preload:false입니다. create:true로 만든 항목은 그룹 없이 일반 텍스트가 되며 defaultGroupID는 더 이상 사용하지 않습니다. 자세한 규칙은 "일반 텍스트" 절을 참고합니다.

함수 옵션은 페이지 hook.controlInit 또는 controlLoad에서 제공합니다.

syn.uicontrols.$dropdowntextlist.controlLoad('animals', {
create(input, callback) {
callback({class:'custom',value:input,name:input});
},
load(query, callback) {
// applicationSearch는 애플리케이션에서 구현하는 데이터 제공자입니다.
applicationSearch(query).then(callback).catch(() => callback([]));
}
});

create는 객체 반환, callback 또는 Promise를 지원하며 결과는 일반 텍스트(valueField는 key, labelField는 value, 그룹은 무시)가 됩니다. load는 후보 배열 반환, callback 또는 Promise를 지원하며 callback의 두 번째 인자로 그룹 배열을 전달할 수 있습니다. callback 방식은 완료 시 한 번 호출하고 데이터를 직접 반환하지 않습니다. 예외·Promise 거절은 잠금/로딩 상태를 해제하고 콘솔에 기록합니다. callback 방식은 실패 시에도 callback()을 호출해야 합니다. 데이터 교체·초기화·파괴 이후 오래된 응답과 이전 검색 응답은 무시합니다.

일반 텍스트 (options에 없는 입력)​

create:true이면 후보에 없는 텍스트를 입력하고 Enter(또는 드롭다운의 일반 텍스트 추가)로 확정할 수 있습니다. 후보에 없는 입력은 모두 일반 텍스트이며 getValue()에는 포함되지 않고 getNormalText()로 따로 받습니다. 일반 텍스트 태그는 점선 테두리로 후보 태그와 구분합니다. hideLogicBadge:false이면 AND/OR 배지 뒤에 key를 이어서 보여주고(AND · 직원 명, key가 없으면 AND만), 배지를 숨기면(기본값) 후보의 그룹 이름처럼 groupDisplay를 따라 groupDisplay:'inline'일 때만 key가 있는 일반 텍스트의 key를 태그 안의 그룹 이름 영역에 보여주고, 그 외에는 툴팁으로만 제공합니다. 배지 색은 후보 태그와 같이 AND/OR에 따르며 긴 key는 key 부분만 줄임표로 자르고 전체는 태그에 마우스를 올리면 툴팁으로 확인합니다. 배지를 표시할 때는 그룹 이름 영역을 표시하지 않습니다.

입력keyvalue
블라블라''블라블라
키 이름:블라블라키 이름블라블라
시각:09:30시각09:30
  • 첫 : 기준으로 앞은 key, 뒤는 value이며 항상 나눕니다. :가 들어간 검색어는 이스케이프할 수 없고 key와 value로 나뉩니다. 앞뒤 공백은 제거하며 value가 비면(abc:) 만들지 않습니다.
  • 입력이 후보의 key 또는 표시값(value)과 정확히 같으면 일반 텍스트를 만들지 않고 그 후보를 선택합니다. 같은 후보가 여러 개면 아직 선택하지 않은 첫 후보를 우선합니다.
  • 일반 텍스트에도 AND/OR와 비교 연산자 편집창을 사용하며 기본값은 AND, contains입니다. key·value 텍스트는 편집창에서 바꾸지 않습니다.
  • addItem('텍스트'), createItem('텍스트')도 같은 규칙으로 일반 텍스트를 만듭니다. 후보 키와 같으면 후보를 선택하며, addItem은 잠금 상태에서도 코드로 추가할 수 있습니다.
  • create를 함수로 지정해도 결과는 일반 텍스트이며 반환한 groupID는 무시합니다. valueField가 key, labelField가 value가 되고, labelField가 없거나 valueField와 같으면 key 없이 value만 사용합니다. 후보와 같은 입력은 함수를 호출하지 않고 그 후보를 선택합니다.
  • 해제한 일반 텍스트는 persist 설정과 관계없이 후보 목록에 남지 않습니다. defaultGroupID는 더 이상 사용하지 않습니다.

getNormalText()는 선택 순서의 [{logic, operator, key, value}]를 반환하며 groupID는 없습니다. setNormalText(items, silent)는 일반 텍스트 선택만 교체하며 항목은 같은 형식의 객체이거나 'value', 'key:value' 문자열입니다. logic·operator를 생략하면 AND·contains이고 잘못된 항목이면 TypeError이며 기존 선택은 유지합니다. maxItems를 넘기면 오류입니다.

control.createItem('작업실'); // 후보 표시값과 같으면 후보 선택, 아니면 일반 텍스트
control.addItem('이름:홍길동');
control.getValue(); // 후보 조건만 (일반 텍스트 제외)
control.getNormalText(); // [{logic:'AND', operator:'contains', key:'이름', value:'홍길동'}]
control.setNormalText([{logic:'OR', operator:'begins_with', key:'', value:'블라블라'}, '비고:확인 필요']);
  • setValue()와 setNormalText()는 서로의 선택을 건드리지 않습니다. 후보 데이터 교체(setValue(options))도 일반 텍스트를 유지하며 clear()는 둘 다 해제합니다. getValue()와 getNormalText()를 저장해 복원하려면 setValue와 setNormalText를 각각 호출합니다.
  • change 이벤트는 (getValue(), getNormalText())를 인자로 받으며 일반 텍스트만 바뀌어도 발생합니다. item_add·item_remove·option_add·option_remove는 일반 텍스트에 {normal:true, logic, operator, key, value}를 전달합니다.
  • syn-datafield에 기록되는 값은 getValue() 결과뿐입니다. 일반 텍스트는 getNormalText()를 화면 코드에서 읽어 서버의 별도 매개변수(예: LIKE 검색어)로 전달합니다.

조건 요약 텍스트​

getSummaryText(options?)는 현재 선택한 조건(후보, 일반 텍스트, 범위)을 사용자에게 보여줄 한 줄 텍스트로 반환합니다. 선택이 없으면 ''입니다. 순수 텍스트이므로 HTML에 넣을 때는 이스케이프하거나 textContent를 사용합니다.

control.getSummaryText();
// (이름 같음 "홍길동" 또는 이름 포함 "김") 그리고 부서 포함 "작업실" 그리고 이름 포함 텍스트 "최" 그리고 작성일 2026-10-01 ~ 2026-10-31
  • 같은 그룹(groupID)의 조건은 선택 순서대로 나열하고, 두 번째 조건부터 그 조건의 logic(그리고·또는)으로 앞 조건과 연결합니다. 서버의 dbclient 필터가 같은 그룹을 각 항목의 logic으로, 그룹 사이를 groupLogic으로 묶는 방식과 같습니다.
  • 각 조건은 그룹 이름 연산자 "값"입니다. 그룹 이름은 optgroups.label(없으면 그룹 ID, 비어 있으면 기타)이고 값에 "가 있으면 \"로 바꿉니다. 일반 텍스트는 key를 그룹 이름으로 쓰며 key가 없으면 텍스트이고 key가 있으면 후보 그룹과 구분되도록 값 앞에 텍스트를 붙여 이름 포함 텍스트 "최"로 표현하며, key가 같은 일반 텍스트끼리 한 그룹입니다.
  • 범위 그룹의 시작·끝 조건은 한 항목으로 합칩니다. 작성일 2026-10-01 ~ 2026-10-31, 한쪽만 있으면 작성일 2026-10-01 부터, 작성일 2026-10-31 까지입니다.
  • 그룹이 둘 이상이고 한 그룹에 조건이 둘 이상이면 그 그룹을 괄호로 묶습니다.
옵션기본값의미
groupLogic'AND'그룹 사이 연결. 'AND' 또는 'OR'. 서버 계약의 groupLogic과 맞춥니다
logicLabels{AND:'그리고', OR:'또는'}연결어 문구. 일부만 지정해도 됩니다
includeNormalTexttruefalse이면 일반 텍스트를 제외
normalTextLabel'텍스트'key가 있는 일반 텍스트의 값 앞에 붙는 문구. ''이면 붙이지 않음
parenthesestruefalse이면 그룹 괄호를 쓰지 않음

컨트롤은 서버 계약을 알지 못하므로 <field logic>처럼 서버에서 그룹 결합을 덮어쓰는 설정은 요약에 반영하지 않습니다. groupLogic이 OR이면 {groupLogic:'OR'}를 전달합니다. groupLogic이 AND·OR가 아니면 TypeError입니다.

조건 묶음 변환​

syn.uicontrols.$dropdowntextlist.getGroupConditions(list)는 [{operator, value, logic}] 목록(예: getNormalText() 결과)을 operator와 logic이 모두 같은 항목끼리 묶어 [{name, args, logic}]로 바꿉니다. 묶음 순서는 처음 나타난 순서이고 args는 value의 순서를 유지합니다. operator 이름은 변환하지 않으므로 대상 컨트롤이 쓰는 이름으로 바꿔야 하면 결과의 name을 직접 바꿉니다.

syn.uicontrols.$dropdowntextlist.getGroupConditions([
{operator:'contains', value:'홍', logic:'AND'},
{operator:'contains', value:'김', logic:'AND'},
{operator:'neq', value:'테스트', logic:'OR'}
]);
// [{name:'contains', args:['홍','김'], logic:'AND'}, {name:'neq', args:['테스트'], logic:'OR'}]

범위 입력 (년월일·년월·시간)​

optgroups의 항목에 range를 지정하면 그 그룹은 후보 목록 대신 시작·끝 범위를 입력하는 그룹이 됩니다. 그룹마다 작성일 · 년월일 같은 범위 버튼이 입력 영역 오른쪽 안에 나란히 배치되며(컨트롤 폭이 좁으면 입력 영역 아래로 내려감), 버튼이나 범위 태그를 누르면 편집창에서 시작·끝을 입력합니다. 시작과 끝은 각각 비울 수 있습니다. 범위 편집창의 적용·해제·취소는 조건 편집창과 같은 Tabler btn-group(적용은 btn btn-primary, 나머지는 btn)이며 높이는 입력 상자와 같은 34px입니다. 선택한 범위가 없으면 편집창의 시작·끝은 현재 날짜·월·시각(브라우저 로컬 시간)이 기본값입니다.

<syn_dropdowntextlist id="periods" syn-datafield="Periods" syn-events="['change']"
syn-options="{
optgroups:[
{value:'createdAt',label:'작성일',range:'date',min:'2026-01-01',max:'2026-12-31'},
{value:'closingMonth',label:'마감월',range:'month',min:'2026-01',max:'2026-12'},
{value:'workTime',label:'근무시간',range:'time',min:'00:00',max:'23:59'}
],
optgroupField:'class',valueField:'value',labelField:'name'
}"></syn_dropdowntextlist>
range입력시작 → 조건끝 → 조건
date (년월일)yyyy-MM-ddgte 2026-10-01lt 2026-11-01 (끝 2026-10-31의 다음 날)
month (년월)yyyy-MMgte 2026-10lt 2027-01 (끝 2026-12의 다음 달)
time (시간, 하루 중 시각)HH:mmgte 09:30lt 10:01 (끝 10:00의 다음 분). 끝이 23:59이면 lte 23:59:59.9999999

min·max는 선택할 수 있는 범위를 제한하는 선택 옵션이며 range와 같은 형식(date는 yyyy-MM-dd, month는 yyyy-MM, time은 HH:mm)입니다. 한쪽만 지정해도 되고 둘 다 생략하면 제한이 없습니다. 편집창의 입력창에 min·max를 적용하고 setRange도 같은 범위를 검사합니다. 범위를 벗어난 시작·끝은 TypeError이며 편집창에 안내 메시지를 표시합니다. 형식이 잘못되었거나 min이 max보다 늦으면 그룹 등록 시 TypeError입니다. setValue()로 복원하는 조건은 min·max로 검사하지 않습니다.

끝을 포함하는 입력(2026-10-31까지)을 서버의 lt 비교로 바꾸기 위해 다음 단위로 변환합니다. 이 방식은 DateTime 컬럼의 정밀도와 무관하게 해당 날짜·월·분 전체를 포함합니다. yyyy-MM 값은 서버가 해당 월 1일 00:00으로 해석합니다.

getValue()에는 일반 조건과 같은 형식으로 포함되며 value는 표시명(2026-10-01 부터, 2026-10-31 까지)입니다. 두 조건의 logic은 AND입니다. 같은 그룹을 시작·끝 한 쌍으로 결합하므로 이 조건의 logic을 OR로 바꾸지 않습니다. 범위 조건 태그는 시작·끝을 함께 편집하는 범위 편집창으로 열리며 AND/OR·비교 연산자 편집창은 사용하지 않습니다.

const control = syn.uicontrols.$dropdowntextlist;
control.setRange('periods', 'createdAt', '2026-10-01', '2026-10-31'); // gte + lt
control.setRange('periods', 'createdAt', '2026-10-15', ''); // 시작만: gte
control.getRange('periods', 'createdAt'); // {start:'2026-10-15', end:''}
control.setRange('periods', 'createdAt', '', ''); // 해제
  • setRange는 min·max를 벗어난 값도 거부합니다. 같은 그룹의 기존 범위 조건을 교체하고 change를 발생시킵니다. 형식이 잘못되었거나(2026-02-30, 24:00) 시작이 끝보다 늦으면(시간은 자정을 넘는 범위 22:00~02:00 포함) TypeError를 던지며 기존 선택은 유지합니다. range가 없는 그룹은 오류입니다.
  • date 끝은 9999-12-30, month 끝은 9999-11까지입니다.
  • maxItems가 범위 조건 수(시작·끝 각 1개)보다 작으면 오류입니다.
  • getValue()의 범위 조건을 setValue()로 복원할 수 있습니다. 컨트롤은 표시명의 부터·까지 접미사와 gte/lt(lte)로 범위 조건을 식별하므로 value를 바꾸지 않습니다.
  • range는 date, month, time만 허용하며 다른 값은 TypeError입니다. 범위 조건은 일반 후보 목록에 남지 않고 선택을 해제하면 사라집니다.
  • type="month"와 type="time" 입력을 지원하지 않는 브라우저(데스크톱 Firefox·Safari 등)에서는 일반 텍스트 입력으로 나타나며 yyyy-MM, HH:mm으로 직접 입력합니다. 형식이 맞지 않으면 편집창에 안내 메시지를 표시합니다.

서버 계약에는 그룹마다 대응하는 <field>를 선언합니다.

<filters parameter="Filters" groupLogic="AND">
<field groupID="createdAt" column="M.CreatedAt" type="DateTime" operators="gte,lt,lte" />
<field groupID="closingMonth" column="M.ClosingMonth" type="Date" operators="gte,lt" />
<field groupID="workTime" column="M.WorkTime" type="Time" operators="gte,lt,lte" />
</filters>

Time은 HH:mm[:ss[.fffffff]](00:00 이상 24:00 미만)를 받습니다. SQL Server·MySQL/MariaDB·PostgreSQL은 time 컬럼과 직접 비교합니다. SQLite는 시각을 텍스트(HH:mm:ss)로 저장하므로 문자열로 비교하고, Oracle에는 시각만 저장하는 타입이 없어 timeFormat="String"(VARCHAR2의 HH:mm:ss 저장)을 명시해야 합니다. 자세한 규칙은 2.Modules/dbclient/readme.md를 참고합니다.

dbclient 필터 연결​

getValue()의 결과를 JSON.stringify()로 직렬화해 dbclient의 Filters 문자열 매개변수에 전달합니다.

const parameter = {
ParameterName: 'Filters', DbType: 'String', Length: -1,
Value: JSON.stringify(syn.uicontrols.$dropdowntextlist.getValue('animals'))
};

서버 SQL 계약에는 <filters parameter="Filters" groupLogic="AND">와 <field groupID="..." column="..." type="String" operators="eq,neq" />를 선언합니다. groupID는 서버에 등록한 검색 필드, key는 비교값, value는 표시명입니다. 컨트롤이 만드는 조건의 기본 operator는 contains이므로, 화면에서 연산자를 지정하지 않고 보낼 필드는 operators에 contains를 포함해야 합니다(포함하지 않으면 서버가 허용하지 않은 연산자로 거부). 동물 데모의 mammal·bird·reptile을 사용할 때도 대상 테이블의 컬럼 매핑을 계약에 등록해야 합니다. GD06의 user·content·createdAt·conversation 그룹을 사용하려면 UI 후보의 그룹과 키도 그 검색 필드에 맞춰 공급합니다.

빈 선택은 []로 전달되어 조건을 추가하지 않습니다. 같은 그룹은 선택 순서의 AND/OR로 묶고 첫 항목의 연결은 무시하며, 그룹 사이는 groupLogic으로 결합합니다. 날짜는 시간대 없는 DateTime과 명시적 오프셋을 갖는 DateTimeOffset을 구분합니다. GUID 저장 형식이 다르면 서버 field의 guidFormat을 지정합니다. 자세한 공급자별 설정과 재현 절차는 2.Modules/dbclient/readme.md의 JSON 배열 필터 SQL을 참고합니다.

유지보수 구조​

기존 UI 컨트롤과 같이 $dropdowntextlist.extend({...})에 메서드를 직접 선언합니다. controlLoad가 설정을 병합하고 addControlSetting에서 생성한 상태를 textListControls에 보관한 다음 initialize 이벤트를 발생시킵니다.

공개 메서드는 (elID, ...) 형태로 컨트롤을 조회하여 처리합니다. getControl(elID)가 반환하는 객체의 동일한 메서드는 기존 인스턴스 호출을 유지하기 위한 연결 함수입니다. 동작을 변경할 때는 모듈의 해당 메서드를 수정합니다.

후보·조건 변환은 getValueType, normalize, condition, resolve에서, 이벤트 연결은 eventCallbacks, relay, on/off/trigger에서 관리합니다. createItem/load의 비동기 완료 콜백은 파괴 여부와 요청 순번을 확인하므로 이 검사를 유지합니다.

빌드와 검증​

2.Modules/wwwroot에서 dotnet libman restore로 고정된 Tom Select 자산을 복원하고 node node_modules/gulp/bin/gulp.js로 번들을 생성합니다. 라이브러리는 기존 저장소 규약대로 libman.json으로 관리합니다.

샘플: /sample/uicontrol/dropdowntextlist.html. 개별 로딩 및 Development/Production 번들에서 확인합니다.

샘플에서 후보 공급, 중복 선택, 조건 편집, 선택 저장·복원과 이벤트 로그를 확인합니다. 비동기 제공자는 콜백과 Promise 양쪽으로 검증하고, 개별 로딩과 Development/Production 번들에서도 같은 결과인지 확인합니다.

실행 샘플​

조건 태그 선택 데모