# Define Columns

## Content

TableSheet in SpreadJS allows you to add, update, or remove a column with meaningful column types to design the table at runtime.
To display the **Add Column** button behind the last column, set the `enableDefineColumn` TableSheet option to true. On right-clicking, it displays the context menu with options to modify, remove, and set the primary key of the column as depicted in the image below.

![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/ts-definecolumn.73a1e0.png)

The following sample code shows how to enable the **Add Column** button in the TableSheet.

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

## Built-In Column Types

TableSheet allows you to define **Column Type** with predefined column properties that are categorized according to the type of column. Here is the list of built-in column types- *Number, Text, Formula, Checkbox, Date, Currency, Percent, Phone, Email, URL, Lookup, CreatedTime, ModifiedTime, Attachment, Select, and Barcode.*
The main properties defined for the column types are as follows:

* **Value (Required)**: Includes column value but this field cannot hold formula or relation if its type is not Formula or Lookup type.
* **Caption (Optional)**: Includes the display name for the column header.
* **Type (Required)**: Allows to select a built-in column type. Some of the built-in columns are displayed in the image below.
<br>
    ![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/ts-definecolumn-button.35a9d7.png)

Data types and sample column options of all the supported column types are listed in the table below.

| **Column Type** | **Data Type** | **Sample Column Options** |
| ----------- | --------- | --------------------- |
| 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>} |

## **Remote Configuration**

SpreadJS allows you to communicate with the remote table using custom functions as mentioned below.

| **Custom Functions** | **Description** | **SampleDefinition** |
| ---------------- | ----------- | ---------------- |
| getColumns | Retrieve the columns from the table. | getColumns: {  <br>    url: apiColumnUrl   } |
| addColumn | Add a column to the table. | addColumn: {<br>    url: apiColumnUrl,<br>    method: 'POST'   } |
| updateColumn | Update the properties of a column in the table. | updateColumn: {<br>    url: apiColumnUrl,<br>    method: 'PUT'    } |
| removeColumn | Remove a column from the table. | removeColumn: {<br>    url: apiColumnUrl,<br>    method: 'DELETE'   } |
| batch | Changes are posted together in the batch mode. | batch: {<br>    url: apiUrl + 'Collection'  } |

The appropriate request and response linked to the custom functions during their interaction with the data structure are as follows:

| **Custom Functions** | **Request Data** | **Response Data** |
| ---------------- | ------------ | ------------- |
| getColumns | None | Column[] |
| addColumn | ColumnWithDefaultData | ColumnData |
| updateColumn | ModifiedColumnData | ModifiedColumnData |
| removeColumn | ColumnData | ColumnData |
| batch | BatchItemData[] | BatchResultData[] |

The column remote APIs can be handled by self-defined functions similar to Handle Requests in the **autoSync** and **batch** modes are as follows:

```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
 });
```

When the **autoSync** mode is enabled, any changes in the columns of TableSheet will sync to the remote table immediately. However, when the **batch** mode is enabled, the column changes will be stored in the local. Then on calling the `submitChange` command, the changed column information and changed data information are sent to the remote table together.

```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 }
]
```

Whereas, on calling the `cancelChange` command, it cancels all the changes. For the **batch** mode, the `getChanges` command on the TableSheet retrieves the column and data changes together.

## Commands to Define Columns

SpreadJS also provides functionality to add, update, or remove columns using commands like `DefineColumn`, `ModifyColumn`, and `RemoveColumn`. These commands enable you to add, update, and delete columns from a table by implementing their properties such as **value**, **dataType**, **defaultValue**, **isPrimaryKey**, **type**, or **name** from the **IColumn** interface.
Let's discuss some code samples to implement each of these commands.

### Define Column

The following sample code shows how to implement the `DefineColumn` command.

```javascript
spread.commandManager().execute({
  cmd: "DefineColumn",
  sheetName: spread.getActiveSheetTab().name(),
  // col: 3, // optional
  column: {
    type: 'Number', // specify the column type
    value: 'Number1', // the column identifier of the column instance from the column type
    caption: '# Number1', // the display of the column for the culture
    style: { formatter: '#,##0.00_);[Red](#,##0.00)' } // the formatter could be specified or be converted from the UI operations
  } as GC.Data.IColumn,
});
```

### Modify Column

The following sample code shows how to implement the `ModifyColumn` command.

```javascript
spread.commandManager().execute({
  cmd: "ModifyColumn",
  sheetName: spread.getActiveSheetTab().name(),
  col: 3,
  column: {
    type: 'Text', // specify the column type
    value: 'Text1', // the column identifier of the column instance from the column type
    caption: 'A Text1', // the display of the column for the culture
  } as GC.Data.IColumn,
});
```

### Remove Column

The following sample code shows how to implement the `RemoveColumn` command.

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

## Define Custom Columns

TableSheet provides the ability to customize the column types or add some special column types using the self-defined commands such as `defineColumnCommand`, `submitDefineColumnCommand` or update the built-in columns using `columnTypeItems` command.
The following code sample shows how to add some special column types or update the built-in column types.

```javascript
// Select some column types to show
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);

// Add self-defined column type
tableSheet.options.columnTypeItems.unshift({ name: 'CustomizingType', text: 'Progress', iconClass: 'gc-defined-column-type-icon-number'
});
```

The `defineColumnCommand` specifies the command to replace the default command that opens the defined column popup for the **Add Column** button or **Modify Column** menu item.  
The following sample code shows how to implement `defineColumnCommand`.

```javascript
// Handle opening defined column popup
const defineCommand = tableSheet.options.defineColumnCommand;
tableSheet.options.defineColumnCommand = 'defineColumnCustom';
  spread.commandManager().register('defineColumnCustom', {
    canUndo: false,
    execute: function (context, options) {
    // it could build self-defined UI on the modify/add column triggered
    // opening the default UI for the sample
       options.cmd = defineCommand;
       spread.commandManager().execute(options);
    }
});
```

The `submitDefineColumnCommand` specifies the command for submitting defined column options when the submit button is selected for the column defined popup.
The following sample code shows how to implement `submitDefineColumnCustom`.

```javascript
// Handle submitting defined column options
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);
}
});
```

## Using Designer

SpreadJS Designer component also provides the ability to define columns by selecting the **Enable Define Column** checkbox from the **TABLE SHEET DESIGN** \> **Options** group.

![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/Enable%20Define%20Column%20checkbox.4a70f0.png)

You can also configure the remote APIs to communicate with remote data tables in the SpreadJS Designer component from the **DATA** \> **DataSource** \> **Table** section, if the remote data table supports.

![image](https://cdn.mescius.io/document-site-files/images/ef9b66d1-0ae2-4e94-b8cb-f9f893aacc8d/ts-definecolumn-remoteConfiguration.2460d9.png)