# 열 정의

## Content

SpreadJS의 테이블 시트에서는 런타임 중 의미 있는 열 유형을 사용하여 열을 추가, 업데이트 또는 제거함으로써 테이블을 구성할 수 있습니다.
마지막 열 뒤에 **열 추가** 버튼을 표시하려면 `enableDefineColumn` 테이블 시트 옵션을 `true`로 설정하면 됩니다.
오른쪽 클릭 시에는 열을 **수정**, **제거**, **기본 키로 설정**하는 컨텍스트 메뉴가 아래 이미지와 같이 표시됩니다.
![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/ts-definecolumn.73a1e0.png)
다음 예제는 테이블 시트에서 **열 추가** 버튼을 활성화하는 방법을 보여줍니다.

```javascript
tableSheet.options.enableDefineColumn = true;
```

### 기본 제공 열 유형

테이블 시트는 열 유형을 미리 정의된 속성 세트로 설정할 수 있도록 지원하며, 다음과 같은 유형으로 분류됩니다:
*Number, Text, Formula, Checkbox, Date, Currency, Percent, Phone, Email, URL, Lookup, CreatedTime, ModifiedTime, Attachment, Select, Barcode* 등
각 열 유형에 대해 정의할 수 있는 주요 속성은 아래와 같습니다:

* **Value (필수)**: 열에 포함될 실제 값입니다. 단, 열 유형이 `Formula` 또는 `Lookup`이 아닌 경우 수식이나 관계 데이터를 포함할 수 없습니다.
* **Caption (선택)**: 열 헤더에 표시될 이름입니다.
* **Type (필수)**: 사용할 내장 열 유형을 지정합니다. 일부 내장 열 유형은 아래 이미지와 같이 표시됩니다.
<br>
    ![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/ts-definecolumn-button.35a9d7.png)

SpreadJS가 지원하는 모든 열 유형에 대한 데이터 타입과 샘플 열 옵션은 다음 표에 정리되어 있습니다.

| **열 유형** | **데이터 유형** | **샘플 열 옵션** |
| ---- | ------ | ------- |
| Number | number | {<br>    value: "Number",<br>    type: "Number",<br>    caption: "Number",<br>    dataType: "number",<br>    defaultValue: 0,<br>    style: {formatter: "0.00", mask: { pattern: "0{0,}" } }<br>} |
| Text | string | {<br>    value: "Text",<br>    type: "Text",<br>    caption: "Text",<br>    dataType: "string"<br>} |
| Formula | depends on the result | {<br>    name: "NamedFormula", // it's a name of the formula for the Referencing Calculated Fields which will be a named formula<br>    value: "=[@Amount]\*[@UnitPrice]",<br>    type: "Formula",<br>    caption: "Total Sales",<br>    dataType: "formula",<br>    style: {formatter: "0.00"}<br>    readonly: true,<br>} |
| Lookup | depends on the related | {<br>    value: "customer.ContactName",<br>    type: "Lookup",<br>    caption: "Customer",<br>    readonly: true,<br>} |
| Date | date | type DateTimePickerOptions = {<br>    showTime: boolean;<br>    calendarPage: "year" \| "month" \| "day";<br>    startDay: "monday" \| "tuesday" \| "wednesday" \| "thursday" \| "friday" \| "saturday" \| "sunday";<br>};<br>{<br>    value: "Date",<br>    type: "Date",<br>    caption: "Date",<br>    dataType: "date",<br>    style: {<br>        formatter: "m/d/yyyy",<br>        cellButtons:[{<br>            buttonBackColor: "transparent",<br>            command: "openDateTimePicker",<br>            enabled: true,<br>            visibility: 'always',<br>            imageType: "dropdown",<br>            useButtonStyle: false,<br>        }],<br>        dropDowns:[{<br>            type: "dateTimePicker",<br>            // The cell type is DateTimePickerOptions<br>            option: {<br>                calendarPage: "day",<br>                showTime: false,<br>                startDay: "sunday",<br>            }<br>        }]<br>    }<br>} |
| Checkbox | boolean | type CheckboxOptions = {<br>    type: "checkbox";<br>    caption: string;<br>    textTrue: string;<br>    textIndeterminate: string;<br>    textFalse: string;<br>    textAlign: "top" \| "bottom" \| "left" \| "right";<br>    isThreeState: boolean;<br>    boxSize: number;<br>};<br>{<br>    value: "Checkbox",<br>    type: "Checkbox",<br>    caption: "Checkbox",<br>    dataType: "boolean",<br>    style: {<br>        // The cell type is CheckboxOptions<br>        cellType: {<br>            type: "checkbox"<br>        }<br>    }<br>} |
| Select | depends on the option | type ItemOptions = {<br>    text: string;<br>    value: any;<br>};<br>type ComboBoxOptions = {<br>    type: "combobox";<br>    editorValueType: "text" \| "index" \| "value";<br>    items: string\[\] \| ItemOptions\[\];<br>    itemHeight: number;<br>    maxDropDownItems: number;<br>    editable: boolean;<br>};<br>{<br>    value: "Select",<br>    type: "Select",<br>    caption: "Select",<br>    dataType: "number",<br>    style: {<br>        // The cell type is ComboBoxOptions<br>        cellType: {<br>            type: "combobox",<br>        }<br>    }<br>} |
| Currency | number | {<br>    value: "Currency",<br>    type: "Currency",<br>    caption: "Currency",<br>    dataType: "number",<br>    style: {formatter: "[$$-409]#,##0.00"}<br>} |
| Percent | number | {<br>    value: "Percent",<br>    type: "Percent",<br>    caption: "Percent",<br>    dataType: "number",<br>    style: {formatter: "0%"}<br>} |
| Phone | string | {<br>    value: "Phone",<br>    type: "Phone",<br>    caption: "Phone",<br>    dataType: "string",<br>    style: {<br>        mask: {<br>            pattern: "1 \\(0{3}\\) 0{3}-0{4}"<br>        }<br>    }<br>} |
| Email | string | {<br>    value: "Email",<br>    type: "Email",<br>    caption: "Email",<br>    dataType: "string",<br>    style: {<br>        mask: {<br>            pattern: "[a0.*\\-]{1,}@[a0.*\\\\-\]\{1\,\}\.\(com\|cn\|gov\|edu\)"<br>        }<br>    }<br>} |
| URL | string | type HyperlinkOptions = {<br>    type: "hyperlink";<br>    linkColor: ColorString;<br>    visitedLinkColor: ColorString;<br>    text: string;<br>    linkToolTip: string;<br>    target: "blank" \| "self" \| "parent" \| "top";<br>    activeOnClick: boolean;<br>};<br>{<br>    value: "URL",<br>    type: "URL",<br>    caption: "URL",<br>    dataType: "string",<br>    style: {<br>        // The cell type is HyperlinkOptions<br>        cellType: {<br>            type: "hyperlink"<br>        }<br>    }<br>} |
| CreatedTime | date | {<br>    value: "CreatedTime",<br>    type: "CreatedTime",<br>    caption: "CreatedTime",<br>    dataType: "date",<br>    defaultValue: "=NOW()",<br>    readonly: true,<br>    trigger: {<br>        when: "onNew",<br>        formula: "=NOW()",<br>    },<br>    style: {<br>        formatter: "m/d/yyyy",<br>        // not necessary options(cell buttons and dropDowns)<br>        cellButtons:[{<br>            buttonBackColor: "transparent",<br>            command: "openDateTimePicker",<br>            enabled: true,<br>            visibility: 'always',<br>            imageType: "dropdown",<br>            useButtonStyle: false,<br>        }],<br>        dropDowns:[{<br>            type: "dateTimePicker",<br>            option: {<br>                calendarPage: "day",<br>                showTime: false,<br>                startDay: "sunday",<br>            }<br>        }]<br>    }<br>} |
| ModifiedTime | date | {<br>    value: "ModifiedTime",<br>    type: "ModifiedTime",<br>    caption: "ModifiedTime",<br>    dataType: "date",<br>    readonly: true,<br>    trigger: {<br>        when: "onNewAndUpdate",<br>        formula: "=NOW()",<br>        fields: "\*",<br>    },<br>    style: {<br>     formatter: "m/d/yyyy",<br>     // not necessary options(cell buttons and dropDowns)<br>      cellButtons:[{<br>        buttonBackColor: "transparent",<br>        command: "openDateTimePicker",<br>        enabled: true,<br>        visibility: 'always',<br>        imageType: "dropdown",<br>        useButtonStyle: false,<br>        }],<br>        dropDowns:[{<br>            type: "dateTimePicker",<br>            option: {<br>                calendarPage: "day",<br>                showTime: false,<br>                startDay: "sunday",<br>            }<br>        }]<br>    }<br>} |
| Attachement | object | type FileUploadOptions = {<br>    type: "fileUpload";<br>    maxSize?: number;<br>    accept?: string;<br>    isPreviewEnabled?: boolean;<br>    isDownloadEnabled?: boolean;<br>    isClearEnabled?: boolean;<br>    marginTop?: number;<br>    marginRight?: number;<br>    marginBottom?: number;<br>    marginLeft?: number;<br>    valuePath?: string;<br>};<br>{<br>    value: "Attachment",<br>    type: "Attachment",<br>    caption: "Attachment",<br>    style: {<br>    // The cell type is FileUploadOptions<br>        cellType: {<br>            type: "fileUpload"<br>        }<br>    }<br>} |
| Barcode | depends on the input | {<br>    value: "Barcode",<br>    type: "Barcode",<br>    caption: "Barcode",<br>    defaultValue: 0,<br>    style: {<br>        formatter: "=BC\_QRCODE([@Barcode],\\"#000000\\",\\"#FFFFFF\\",\\"L\\",2,\\"auto\\",\\"auto\\",false,0,,\\"UTF-8\\",,,,)"<br>    }<br>} |

## 원격 구성

SpreadJS의 테이블 시트는 다음과 같이 사용자 정의 함수를 통해 원격 테이블과 통신할 수 있도록 지원합니다.

| 함수 이름 | 설명 | 샘플 정의 |
| ----- | --- | ----- |
| `getColumns` | 테이블에서 열 목록을 가져옵니다. | `getColumns: { url: apiColumnUrl }` |
| `addColumn` | 테이블에 열을 추가합니다. | `addColumn: { url: apiColumnUrl, method: 'POST' }` |
| `updateColumn` | 테이블의 열 속성을 업데이트합니다. | `updateColumn: { url: apiColumnUrl, method: 'PUT' }` |
| `removeColumn` | 테이블에서 열을 제거합니다. | `removeColumn: { url: apiColumnUrl, method: 'DELETE' }` |
| `batch` | 변경 사항을 배치 모드로 한 번에 전송합니다. | `batch: { url: apiUrl + 'Collection' }` |

각 사용자 정의 함수가 데이터 구조와 상호작용할 때의 요청 및 응답 예시:

| 사용자 정의 함수 | 요청 데이터 | 응답 데이터 |
| --------- | ------ | ------ |
| `getColumns` | 없음 | `Column[]` |
| `addColumn` | `ColumnWithDefaultData` | `ColumnData` |
| `updateColumn` | `ModifiedColumnData` | `ModifiedColumnData` |
| `removeColumn` | `ColumnData` | `ColumnData` |
| `batch` | `BatchItemData[]` | `BatchResultData[]` |

컬럼 관련 원격 API는 `autoSync` 및 `batch` 모드에서의 Handle Requests와 유사하게 **사용자 정의 함수**로 처리할 수 있습니다.

```javascript
var orderTable = dataManager.addTable("orderTable", {
        remote: {
            read: function(): Promise<any[]>{
                return Promise.resolve([...])
            },
            getColumns: function(): Promise<Column[]>{
                return Promise.resolve([...])
            },
            addColumn: function(change): Promise<ny>{
                return Promise.resolve()
            },
            updateColumn: function(change): Promise<any>{
                return Promise.resolve();
            },
            removeColumn: function(change): Promise<any>{
                return Promise.resolve();
            },
            // batch: function(changes): Promise<any>{
            //   return Promise.resolve([...]);
            // },
        },
        autoSync: true,
        // batch: true
 });
```

autoSync 모드가 활성화된 경우, 테이블 시트에서 열의 변경 사항은 즉시 원격 테이블과 동기화됩니다. 그러나 batch 모드가 활성화된 경우, 열 변경 사항은 로컬에 저장됩니다. 이후 `submitChange` 명령을 호출하면 변경된 열 정보와 변경된 데이터 정보가 함께 원격 테이블로 전송됩니다.

```javascript
[
    { "type": "updateColumn", "column": { field: 'columnName', dataType: 'number', ...otherproperties }, "originalColumn": { field: 'columnName0', ...otherproperties } },
    { "type": "addColumn", "column": { field: 'columnName1', dataType: 'number', ...otherproperties }, },
    { "type": "removeColumn", "column": { field: 'columnName2', ...otherproperties }, },
   
    { "type": "delete", "dataItem": { ...} },
    { "type": "insert", "dataItem": { ...} },
    { "type": "update", "dataItem": { ...}, "oldDataItem": { ...}, "sourceIndex": 1 }
]
```

반면 `cancelChange` 명령을 호출하면 모든 변경 사항이 취소됩니다. batch 모드에서는 `getChanges` 명령을 통해 테이블 시트의 열 변경 및 데이터 변경 사항을 함께 가져올 수 있습니다.

### 열 정의를 위한 명령

SpreadJS는 `DefineColumn`, `ModifyColumn`, `RemoveColumn` 등의 명령을 통해 열을 추가, 수정 또는 삭제할 수 있는 기능을 제공합니다. 이러한 명령은 IColumn 인터페이스에 정의된 `value`, `dataType`, `defaultValue`, `isPrimaryKey`, `type`, `name` 등의 속성을 사용하여 테이블 열을 구현할 수 있게 합니다.

### 열 정의

다음은 `DefineColumn` 명령을 구현하는 샘플 코드입니다:

```javascript
spread.commandManager().execute({
  cmd: "DefineColumn",
  sheetName: spread.getActiveSheetTab().name(),
  // col: 3, // optional
  column: {
    type: 'Number', // 열 유형 지정
    value: 'Number1', //  열 유형에서 파생된 열 인스턴스의 식별자
    caption: '# Number1', // 해당 문화권(culture)에 맞는 열의 표시 형식 
    style: { formatter: '#,##0.00_);[Red](#,##0.00)' } // 서식 지정자(formatter)는 직접 지정할 수도 있고 UI 작업을 통해 변환될 수도 있음 
  } as GC.Data.IColumn,
});
```

### **열 수정**

아래 샘플 코드는 `ModifyColumn` 명령을 구현하는 방법을 보여줍니다.

```javascript
spread.commandManager().execute({
  cmd: "ModifyColumn",
  sheetName: spread.getActiveSheetTab().name(),
  col: 3,
  column: {
    type: 'Text', // 열 유형 지정
    value: 'Text1',//  열 유형에서 파생된 열 인스턴스의 식별자
    caption: 'A Text1',// 해당 문화권(culture)에 맞는 열의 표시 형식 
  } as GC.Data.IColumn,
});
```

### 열 삭제

다음 샘플 코드는`RemoveColumn` 명령을 구현하는 방법을 보여줍니다.

```javascript
spread.commandManager().execute({
  cmd: "RemoveColumn",
  sheetName: spread.getActiveSheetTab().name(),
  col: 3,
});
```

## **사용자 정의 열 정의**

테이블 시트는 `defineColumnCommand`, `submitDefineColumnCommand`와 같은 사용자 정의 명령을 통해 열 유형을 사용자 정의하거나 일부 특별한 열 유형을 추가할 수 있는 기능을 제공합니다. 또한, `columnTypeItems` 명령을 사용하여 기본 제공 열 유형을 업데이트할 수 있습니다.
다음 코드 샘플은 특별한 열 유형을 추가하거나 기본 제공 열 유형을 업데이트하는 방법을 보여줍니다.

```javascript
// 표시할 영 유형을 선택
const items = tableSheet.options.columnTypeItems.filter((item)=>item.name === 'Select' || item.name === 'Attachment' || item.name === 'Barcode' || item.name === 'CreatedTime' || item.name === 'ModifiedTime' || item.name === 'Currency')

tableSheet.options.columnTypeItems = tableSheet.options.columnTypeItems.slice(0, 4).concat(items);

// 사용자 정의 열 유형 추가
tableSheet.options.columnTypeItems.unshift({ name: 'CustomizingType', text: 'Progress', iconClass: 'gc-defined-column-type-icon-number'
});
```

`defineColumnCommand`는 열 추가 버튼 또는 열 수정 메뉴 항목에서 열 정의 팝업을 여는 기본 명령을 대체하는 명령을 지정합니다.
다음 샘플 코드는 `defineColumnCommand`를 구현하는 방법을 보여줍니다.

```javascript
// 열 정의 팝업 열기 처리
const defineCommand = tableSheet.options.defineColumnCommand;
tableSheet.options.defineColumnCommand = 'defineColumnCustom';
  spread.commandManager().register('defineColumnCustom', {
    canUndo: false,
    execute: function (context, options) {
    // 사용자 정의 UI를 구성할 수 있음 (열 수정/추가 시)
    // 샘플에서는 기본 UI 열기
       options.cmd = defineCommand;
       spread.commandManager().execute(options);
    }
});
```

`submitDefineColumnCommand`는 열 정의 팝업에서 "Submit" 버튼을 선택할 때 정의된 열 옵션을 제출하는 명령을 지정합니다.
다음 샘플 코드는 `submitDefineColumnCustom`을 구현하는 방법을 보여줍니다.

```javascript
// 정의된 열 옵션 보내기 처리
tableSheet.options.submitDefineColumnCommand = 'submitDefineColumnCustom';
spread.commandManager().register('submitDefineColumnCustom', {
  canUndo: false,
  execute: function (context, options) {
    if (options.command === 'DefineColumn' && options.column.type === 'CustomizingType') {
        options.column.defaultValue = 0.1;
        options.column.style = {
          formatter: "=HBARSPARKLINE([@"+options.column.value+"], \"#66B032\")"
        };
}
  options.cmd = options.command;
  spread.commandManager().execute(options);
}
});
```

## SpreadJS 디자이너 사용하기

SpreadJS 디자이너에 **테이블 시트 디자인(TABLE SHEET DESIGN) > 옵션(Options)** 그룹에서 **열 정의 활성화(Enable Define Column)** 체크박스를 선택하여 열을 정의할 수 있는 기능을 제공합니다.
![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/Enable%20Define%20Column%20checkbox.4a70f0.png)
또한, 원격 데이터 테이블이 지원되는 경우, **데이터(DATA) > 데이터원본(DataSource) > 표(Table)** 섹션을 통해 SpreadJS 디자이너에서 원격 API를 구성하여 원격 데이터 테이블과 통신할 수 있습니다.
![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/ts-definecolumn-remoteConfiguration.2460d9.png?width=600)