# 표 바인딩

## Content

SpreadJS에서는 셀 수준 바인딩(cell-level binding)을 사용하여 표를 데이터 원본에 바인딩할 수 있습니다.
표 바인딩은 두 가지 방법으로 수행할 수 있습니다. 사용자는 스프레드시트 작업 중 [bindColumns](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#bindColumns) 메서드 또는 [bind](/spreadjs/api/classes/GC.Spread.Sheets.Worksheet#bind) 메서드를 사용하여 표를 데이터 원본에 바인딩할 수 있습니다.
워크시트에서 표 바인딩 작업을 수행할 때는 다음 규칙을 반드시 염두에 두어야 합니다:

* 값을 설정하면 데이터 원본이 변경됩니다.
* 행을 추가하거나 삭제하면 데이터 원본이 변경됩니다.
* 열을 추가하거나 삭제해도 데이터 원본은 변경되지 않습니다.
* 표를 제거, 지우기, 이동 또는 크기 조정해도 데이터 원본은 변경되지 않습니다.
* 바인딩은 표의 행 수를 자동으로 조정합니다(열 수는 동일하게 유지됩니다).
* 수식은 데이터 원본에 저장되지 않습니다.
<br>

## 표에 열 바인딩하기

워크시트의 모든 표에 열을 바인딩하려면, 시트를 데이터 원본에 바인딩한 다음 [Table](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table) 클래스의 [bindColumns](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#bindColumns) 및 [bindingPath](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#bindingPath)메서드를 설정하면 됩니다. 또한 [TableColumn](/spreadjs/api/classes/GC.Spread.Sheets.Tables.TableColumn) 클래스의 [dataField](/spreadjs/api/classes/GC.Spread.Sheets.Tables.TableColumn#dataField) 및 [name](/spreadjs/api/classes/GC.Spread.Sheets.Tables.TableColumn#name) 메서드를 사용하여 표의 열에 대한 정보를 지정할 수도 있습니다.
다음 코드는 [bindColumns](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#bindColumns) 메서드 및 [bindingPath](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#bindingPath) 메서드를 사용하여 표를 데이터 원본에 바인딩하는 예시입니다.

```javascript
$(function ()
{
    //두 개의 데이터 원본 생성 
    function Company(name, logo, slogan, address, city, phone, email) {
        this.name = name;
        this.logo = logo;
        this.slogan = slogan;
        this.address = address;
        this.city = city;
        this.phone = phone;
        this.email = email;
    }
    function Customer(id, name, company) {
        this.id = id;
        this.name = name;
        this.company = company;
    }
    function Record(description, quantity, amount) {
        this.description = description;
        this.quantity = quantity;
        this.amount = amount;
    }
    function Invoice(company, number, date, customer, receiverCustomer, records) {
        this.company = company;
        this.number = number;
        this.date = date;
        this.customer = customer;
        this.receiverCustomer = receiverCustomer;
        this.records = records;
    }
    var company1 = new Company("Baidu", null, "We know everything!", "Beijing 1st road", "Beijing", "010-12345678", "baidu@baidu.com"),
    company2 = new Company("Tecent", null, "We have everything!", "Shenzhen 2st road", "Shenzhen", "0755-12345678", "tecent@qq.com"),
    company3 = new Company("Alibaba", null, "We sell everything!", "Hangzhou 3rd road", "Hangzhou", "0571-12345678", "alibaba@alibaba.com"),
    customer1 = new Customer("A1", "employee 1", company2),
    customer2 = new Customer("A2", "employee 2", company3),
    records1 = [new Record("Finance charge on overdue balance at 1.5%", 1, 150), new Record("Invoice #100 for $1000 on 2014/1/1", 1, 150)],
    records2 = [new Record("Purchase server device", 2, 15000), new Record("Company travel", 100, 1500), new Record("Company Dinner", 100, 200)],
    invoice1 = new Invoice(company1, "00001", new Date(2014, 0, 1), customer1, customer1, records1),
    invoice2 = new Invoice(company2, "00002", new Date(2014, 6, 6), customer2, customer2, records2),
    dataSource1 = new GC.Spread.Sheets.Bindings.CellBindingSource(invoice1),
    dataSource2 = new GC.Spread.Sheets.Bindings.CellBindingSource(invoice2);
    function BindingPathCellType() {
        GC.Spread.Sheets.CellTypes.Text.call(this);
    }
    BindingPathCellType.prototype = new GC.Spread.Sheets.CellTypes.Text();
    BindingPathCellType.prototype.paint = function (ctx, value, x, y, w, h, style, context) {
        if \(value === null \|\| value === undefined\) \{
            var sheet = context.sheet, row = context.row, col = context.col;
            if \(sheet && \(row === 0 \|\| \!\!row\) && \(col === 0 \|\| \!\!col\)\) \{
                var bindingPath = sheet.getBindingPath(context.row, context.col);
                if (bindingPath) {
                    value = "[" + bindingPath + "]";
                }
            }
        }
        GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
    };
    var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), {sheetCount: 1});
    activeSheet = spread.getActiveSheet();
    spread.suspendPaint();
    activeSheet.name("FINANCE CHARGE");
    var bindingPathCellType = new BindingPathCellType();
    activeSheet.getCell(1, 2).bindingPath("company.slogan").cellType(bindingPathCellType).vAlign(GC.Spread.Sheets.VerticalAlign.bottom);
    activeSheet.getCell(1, 4).value("INVOICE").foreColor("#58B6C0").font("33px Arial");
    activeSheet.getCell(3, 1).bindingPath("company.name").cellType(bindingPathCellType).foreColor("#58B6C0").font("bold 20px Arial");
    activeSheet.getCell(5, 1).bindingPath("company.address").cellType(bindingPathCellType);
    activeSheet.getCell(5, 3).value("INVOICE NO.").font("bold 15px Arial");
    activeSheet.getCell(5, 4).bindingPath("number").cellType(bindingPathCellType);
    activeSheet.getCell(6, 1).bindingPath("company.city").cellType(bindingPathCellType);
    activeSheet.getCell(6, 3).value("DATE").font("bold 15px Arial");
    activeSheet.getCell(6, 4).bindingPath("date").cellType(bindingPathCellType).formatter("MM/dd/yyyy").hAlign(GC.Spread.Sheets.HorizontalAlign.left);
    activeSheet.getCell(7, 1).bindingPath("company.phone").cellType(bindingPathCellType);
    activeSheet.getCell(7, 3).value("CUSTOMER ID").font("bold 15px Arial");
    activeSheet.getCell(7, 4).bindingPath("customer.id").cellType(bindingPathCellType);
    activeSheet.getCell(8, 1).bindingPath("company.email").cellType(bindingPathCellType);
    activeSheet.getCell(10, 1).value("TO").font("bold 15px Arial");
    activeSheet.getCell(10, 3).value("SHIP TO").font("bold 15px Arial");
    activeSheet.getCell(11, 1).bindingPath("customer.name").cellType(bindingPathCellType).textIndent(10);
    activeSheet.getCell(12, 1).bindingPath("customer.company.name").cellType(bindingPathCellType).textIndent(10);
    activeSheet.getCell(13, 1).bindingPath("customer.company.address").cellType(bindingPathCellType).textIndent(10);
    activeSheet.getCell(14, 1).bindingPath("customer.company.city").cellType(bindingPathCellType).textIndent(10);
    activeSheet.getCell(15, 1).bindingPath("customer.company.phone").cellType(bindingPathCellType).textIndent(10);
    activeSheet.getCell(11, 4).bindingPath("receiverCustomer.name").cellType(bindingPathCellType);
    activeSheet.getCell(12, 4).bindingPath("receiverCustomer.company.name").cellType(bindingPathCellType);
    activeSheet.getCell(13, 4).bindingPath("receiverCustomer.company.address").cellType(bindingPathCellType);
    activeSheet.getCell(14, 4).bindingPath("receiverCustomer.company.city").cellType(bindingPathCellType);
    activeSheet.getCell(15, 4).bindingPath("receiverCustomer.company.phone").cellType(bindingPathCellType);
    activeSheet.addSpan(17, 1, 1, 2);
    activeSheet.getCell(17, 1).value("JOB").foreColor("#58B6C0").font("bold 12px Arial");
    activeSheet.addSpan(17, 3, 1, 2);
    activeSheet.getCell(17, 3).value("PAYMENT TERMS").foreColor("#58B6C0").font("bold 12px Arial");
    activeSheet.addSpan(18, 1, 1, 2);
    activeSheet.getCell(18, 1).backColor("#DDF0F2");
    activeSheet.addSpan(18, 3, 1, 2);
    activeSheet.getCell(18, 3).value("Due on receipt").backColor("#DDF0F2").foreColor("#58B6C0").font("12px Arial");
    activeSheet.getRange(17, 1, 2, 4, GC.Spread.Sheets.SheetArea.viewport).setBorder(new GC.Spread.Sheets.LineBorder("#58B6C0", GC.Spread.Sheets.LineStyle.thin), {
            top: true,
            bottom: true,
            innerHorizontal: true
        });
    var table = activeSheet.tables.add("tableRecords", 20, 1, 4, 4, GC.Spread.Sheets.Tables.TableThemes.light6);
    table.autoGenerateColumns(false);
    var tableColumn1 = new GC.Spread.Sheets.Tables.TableColumn();
    tableColumn1.name("DESCRIPTION");
    tableColumn1.dataField("description");
    var tableColumn2 = new GC.Spread.Sheets.Tables.TableColumn();
    tableColumn2.name("QUANTITY");
    tableColumn2.dataField("quantity");
    var tableColumn3 = new GC.Spread.Sheets.Tables.TableColumn();
    tableColumn3.name("AMOUNT");
    tableColumn3.dataField("amount");
    table.bindColumns([tableColumn1, tableColumn2, tableColumn3]);
    table.bindingPath("records");
    table.showFooter(true);
    table.setColumnName(3, "TOTAL");
    table.setColumnValue(2, "TOTAL DUE");
    table.setColumnDataFormula(3, "=[@QUANTITY]\*[@AMOUNT]");
    table.setColumnFormula(3, "=SUBTOTAL(109,[TOTAL])");
    activeSheet.getCell(26, 1).formula('="Make all checks payable to "&B4&". THANK YOU FOR YOUR BUSINESS!"').foreColor("gray").font("italic 14px Arial");
    activeSheet.options.allowCellOverflow = true;
    //열 너비 및 행 높이 조절
    activeSheet.setColumnWidth(0, 5);
    activeSheet.setColumnWidth(1, 300);
    activeSheet.setColumnWidth(2, 115);
    activeSheet.setColumnWidth(3, 125);
    activeSheet.setColumnWidth(4, 155);
    activeSheet.setRowHeight(0, 5);
    activeSheet.setRowHeight(1, 40);
    activeSheet.setRowHeight(2, 10);
    activeSheet.setRowHeight(17, 0);
    activeSheet.setRowHeight(18, 0);
    activeSheet.setRowHeight(19, 0);
    activeSheet.setRowHeight(25, 10);
    activeSheet.options.gridline = {showVerticalGridline: false, showHorizontalGridline: false};
    //데이터 원본 설정
    //activeSheet.setDataSource(dataSource1);
    activeSheet.setDataSource(dataSource2);
    spread.resumePaint();
})
```

## 표 열에 포맷터 및 셀 유형 바인딩하기

SpreadJS에서 제공하는 고급 열 바인딩 기능을 사용하면, 사용자들은 데이터 필드 및 이름 뿐만 아니라 포맷터와 다양한 셀 유형도 표 열에 바인딩할 수 있습니다. 또한, 표 열 내의 수식 함수도 변환할 수 있습니다.
[bind](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#bind) 메서드는 표를 여러 레코드를 포함한 필드에 바인딩하고, 표 열을 해당 레코드의 데이터 필드에 바인딩하는 데 사용할 수 있습니다. 사용자가 표 데이터를 채우기 위한 다른 데이터 원본을 설정하면, 표는 해당 레코드에 자동으로 바인딩됩니다.
다음 코드는 [bind](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#bind) 메서드를 사용하여 표 열의 데이터를 바인딩하는 방법을 보여줍니다.

```javascript
// Spread 초기화
var spread = new GC.Spread.Sheets.Workbook(document.getElementById('ss'), { sheetCount: 1 });
       
// activesheet 가져오기
var activeSheet = spread.getActiveSheet();
       
// 데이터
var data = {
    name: 'Jones', region: 'East',
    sales: [
        { orderDate: '1/6/2013', item: 'Pencil', units: 95, cost: 1.99, isMakeMoney: true },
        { orderDate: '4/1/2013', item: 'Binder', units: 60, cost: 4.99, isMakeMoney: false },
        { orderDate: '6/8/2013', item: 'Pen Set', units: 16, cost: 15.99, isMakeMoney: false }
    ]
};

var convert = function (item) {
    return item['cost'] + '$';
}
       
// 표 추가
var table = activeSheet.tables.add('tableSales', 0, 0, 5, 5);
var tableColumn1 = new GC.Spread.Sheets.Tables.TableColumn(1, "orderDate", "Order Date", "d/M/yy");
var tableColumn2 = new GC.Spread.Sheets.Tables.TableColumn(2, "item", "Item");
var tableColumn3 = new GC.Spread.Sheets.Tables.TableColumn(3, "units", "Units");
var tableColumn4 = new GC.Spread.Sheets.Tables.TableColumn(4, "cost", "Cost", null, null, convert);
var tableColumn5 = new GC.Spread.Sheets.Tables.TableColumn(5, "isMakeMoney", "IsMakeMoney", null, new GC.Spread.Sheets.CellTypes.CheckBox());
  
table.autoGenerateColumns(false);
        
// bind() 메서드를 이용하여 표 바인딩
table.bind([tableColumn1, tableColumn2, tableColumn3, tableColumn4, tableColumn5], 'sales', data);
for (var i = 0; i < 5; i++)
    activeSheet.setColumnWidth(i, 110.0, GC.Spread.Sheets.SheetArea.viewport);
```


## 데이터 매니저 테이블에 변경 사항 저장하기

SpreadJS에서는 워크시트 표가 데이터 매니저 테이블에 바인딩되어 있을 때, 해당 표에서 수행된 변경 사항을 저장할 수 있습니다. 데이터 매니저 테이블이 배치 모드(batch mode)에 있는 경우, `GC.Spread.Sheets.Commands` 네임스페이스의 `tableSubmitChanges` 명령을 사용하여 바인딩된 표의 변경 내용을 저장할 수 있습니다. 이 명령은 실행 취소/다시 실행(undo/redo) 작업을 지원하며, 시트 이름 및 표 이름과 같은 특정 옵션과 함께 실행할 수 있습니다.

## 바인딩된 시트 표의 가져오기/내보내기

바인딩된 워크시트를 SJS 또는 SSJSON 파일 형식으로 내보낼 때는 변경 사항이 자동으로 저장됩니다. 그러나 Xlsx 형식으로 저장할 때는 `ExportXlsxOptions`의 `includeBindingSource` 옵션을 `true`로 설정해야 합니다.
Excel 파일을 가져올 때, `ImportXlsxOptions`에서 `convertSheetTableToDataTable` 옵션을 `true`로 지정하면 모든 시트 표를 데이터 매니저 테이블로 변환할 수 있습니다. 이때 셀 기반 수식(cell-based formula)은 가져오기 중 제거되지만, 열 기반 수식(column-based formula)은 가져오기 및 내보내기 시 변환됩니다.

### SpreadJS 디자이너 사용하기

* Xlsx 형식 파일을 가져올 때 시트 테이블을 데이터 테이블로 변환하려면 **시트 테이블을 데이터 테이블로 변환(Convert Sheet Tables To Data Tables)** 옵션을 활성화해줍니다.

![importing in designer](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/1.1a6dda.png)

* Excel로 내보낼 때는 **바인딩 소스 포함(Include Binding Source)** 옵션을 활성화해야 합니다.

![export](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/216c2e7e-5246-4c37-8843-3854aa344a6a/Export.4fd541.d211d3.png)

## 바인딩된 표 행 확장하기

SpreadJS는 바인딩된 표의 행을 확장하는 기능을 제공합니다. 이를 위해 [Table](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table) 타입의 [expandBoundRows](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#expandBoundRows) 메서드를 사용하여 시트를 직접 확장하거나 바인딩된 표에 행을 삽입/삭제할 수 있습니다.
![](https://gcdocumentsitekrblob.blob.core.windows.net/document-site-files/images/8d606653-16a0-474d-b9dc-e2b4d01c2446/images/expand_rows.png)
다음 코드는 [expandBoundRows](/spreadjs/api/classes/GC.Spread.Sheets.Tables.Table#expandBoundRows) 메서드를 사용하여 바인딩된 표의 행을 확장하는 방법을 보여줍니다.

```javascript
// Spread 초기화
var spread = new GC.Spread.Sheets.Workbook(document.getElementById('ss'), { sheetCount: 3 });
// activesheet 가져오기
var sheet = spread.getSheet(0);
// 데이터 생성
var data = {
    name: 'Jones', region: 'East',
    sales: [
        { orderDate: '1/6/2013', item: 'Pencil', units: 95, cost: 1.99 },
        { orderDate: '4/1/2013', item: 'Binder', units: 60, cost: 4.99 },
        { orderDate: '6/8/2013', item: 'Pen Set', units: 16, cost: 15.99 },
        { orderDate: '8/1/2013', item: 'Pencil', units: 20, cost: 24.99 },
        { orderDate: '10/8/2013', item: 'Binder', units: 31, cost: 16.99 }
    ]
};
//  "table1"로 표 이름 설정
var table1 = sheet.tables.add('tableRecords', 0, 0, 4, 4);
table1.autoGenerateColumns(true);
//"table2"로 표 이름 설정
var table2 = sheet.tables.add('tableBelow', 4, 0, 4, 7);
// table1 바인딩
table1.expandBoundRows(true);
table1.bindingPath('sales');
// 데이터 원본 설정
var dataSource = new GC.Spread.Sheets.Bindings.CellBindingSource(data);
sheet.setDataSource(dataSource);
```