[]
        
(Showing Draft Content)

테이블 시트 뷰

테이블 시트 뷰는 테이블 시트의 중심이며, 가져온 데이터 소스의 필드들로 구성됩니다. 이는 데이터 열을 풍부하게 하고 더 많은 기능을 부여하는 데 사용됩니다.



위의 흐름도 이미지에서 볼 수 있듯이, 테이블 시트 뷰는 다음과 같은 방식으로 표시될 수 있습니다:

  • 테이블 열(Table columns): 필드에서 직접 생성되는 기본 열로, 필드 이름 속성만 가지며 기본 보기로 사용됩니다.

  • 뷰 열(View columns): 스타일, 포맷 등 다양한 속성으로 사용자 정의할 수 있는 고급 열로, caption, style, width, headerStyle 등의 속성을 통해 테이블 시트 뷰에 렌더링됩니다.

테이블 시트 뷰는 다양한 IColumn 속성을 설정하여 사용자 지정할 수 있습니다. 예를 들어, 헤더 스타일, 헤더 핏 모드, 조건부 서식, 데이터 유효성 검사, 그리고 셀 타입 및 드롭다운과 같은 열 스타일을 설정할 수 있습니다.

열 표시 값

기존 열 데이터를 대신하여 사용자 정의 값을 표시하려면, 열의 style 속성에 포함된 formatter 수식을 사용할 수 있습니다. 이 수식은 [@] 연산자를 이용해 현재 행의 전체 데이터를 참조할 수 있습니다.

테이블 시트 열에서 사용자 지정 값을 표시하는 방법은 다음 두 가지가 있습니다:

  • formatter 안에 수식 사용

    =[@contactTitle] & ":" & [@employee.FirstName] & " " & [@employee.LastName]

  • formatter 안에 템플릿 수식 사용

    {{[@contactTitle]}} : {{=[@employee.FirstName] & " " & [@employee.LastName]}}

column-display-value.png

다음 코드 샘플은 formatter 안의 수식을 사용하여 사용자 정의 열 값을 표시하는 방법을 보여줍니다:

//세일즈 테이블과 고객 테이블 사이 관계 추가
dataManager.addRelationship(salesTable, "customerKey", "myCustomer", customerTable, "customerKey", "mySales");

//사용자 정의 뷰 추가 
customerTable.fetch().then(function () {

var myView = salesTable.addView("myView", [
       { value: "saleKey", width: 100, caption: "Sale Key" },
       { value: "salesPerson", width: 150, caption: "Sales Person" },
       { value: "myCustomer", width: 350, caption: "Customer Information", style: { formatter: '=[@myCustomer.primaryContact] & " " & [@myCustomer.postalCode]' } },
       { value: "address", width: 350, caption: "Address" },
       { value: "stockItem", width: 350, caption: "Stock Item" },
       { value: "quantity", width: 100, caption: "Quantity" }
          ]);

열 헤더 스타일

열 헤더 스타일은 뷰의 headerStyle 속성에 HeaderStyleOptions 를 정의하여 사용자 지정할 수 있습니다. HeaderStyleOptions는 배경색, 테마 글꼴, 자동 줄 바꿈 등과 같은 옵션을 포함합니다.



다음 코드 샘플은 테이블 시트 뷰에서 열 헤더 스타일을 설정하는 방법을 보여줍니다:

// a열 헤더에 대한 스타일 정의 및 addView() 메서드를 사용하여 뷰에 추가하는 동안 할당
var headerStyle = {
    font: "italic bold 13pt Calibri",
    borderTop: {
        color: "red",
        style: "thick"
    },
    borderLeft: {
        color: "red",
        style: "thin"
    },
    borderRight: {
        color: "red",
        style: "thin"
    },
    borderBottom: {
        color: "red",
        style: "thin"
    }
};


//테이블 시트에 뷰 바인딩
customerTable.fetch().then(function () {
    var view = customerTable.addView("myView", [
        { value: "customerKey", width: 130, headerStyle: headerStyle, caption: "Customer Key" },// set the style of column header using headerStyle property
        { value: "customer", width: 200, headerStyle: headerStyle, caption: "Customer" }, 
        { value: "billToCustomer", width: 200, headerStyle: headerStyle, caption: "Bill To Customer" },
        { value: "category", width: 120, headerStyle: headerStyle, caption: "Category" },
        { value: "buyingGroup", width: 180, headerStyle: headerStyle, caption: "Buying Group" },
        { value: "primaryContact", width: 170, headerStyle: headerStyle, caption: "Primary Contact" },
        { value: "postalCode", width: 120, headerStyle: headerStyle, caption: "Postal Code" },
        { value: "validFrom", width: 150, headerStyle: headerStyle, caption: "Valid From" },
        { value: "validTo", width: 180, headerStyle: headerStyle, caption: "Valid To" }

    ]);
    //테이블의 모든 기본 열을 가진 뷰
    sheet.setDataView(view);
});

열 헤더 맞춤 모드

테이블 시트 뷰에서 지정된 열의 헤더 레이아웃을 적절히 표시할 수 있도록 열 헤더 맞춤 모드를 설정할 수 있습니다.

열 데이터의 내용 길이는 짧지만 헤더 캡션이 길거나 일부 열이 관계를 가질 때 유용합니다.


headerFit 뷰 옵션 속성을 사용하여 다음과 같이 세 가지 유형의 헤더 맞춤 모드를 설정할 수 있습니다.

  • Normal: 열 헤더 텍스트 방향이 수평이며 왼쪽에서 오른쪽으로 표시됩니다. 기본 헤더 맞춤 모드입니다.



  • Vertical: 열 헤더 텍스트 방향이 수직이며 위에서 아래로 표시됩니다.




  • Stack: 열 너비가 열 헤더 텍스트를 표시하기에 충분하지 않을 때 헤더가 위에서 아래로 쌓여 표시됩니다.



    Stack 헤더 맞춤 모드는 인접한 열과 함께 표시될 경우 Vertical 모드와 교차하여 함께 표시됩니다.




테이블 시트 뷰에서 defaultStackRowHeight 옵션을 사용하여 Stack 행 높이를 설정할 수 있습니다. 값이 null일 경우 평균 행 높이를 계산하며, 유효한 숫자를 지정하면 위에서 아래로 그 값을 기준으로 높이를 계산합니다.


다음 코드 샘플은 열 헤더 맞춤 모드를 설정하고 테이블 시트 뷰에서 행 높이를 조정하는 방법을 보여줍니다.

//tablesheet에 뷰 바인딩
myTable.fetch().then(function () {
    var view = myTable.addView("myView", [
        { value: "saleKey", caption: "Sale Key", headerFit: "stack", headerStyle: headerStyle },// set headerFit를 스택에 설정
        { value: "cityKey", caption: "City Key", headerFit: "stack", headerStyle: headerStyle },
        { value: "stockItemKey", width: 50, caption: "Stock Item Key", headerFit: "stack", headerStyle: headerStyle },
        { value: "invoiceDateKey", width: 120, caption: "Invoice Date Key", headerFit: "vertical", headerStyle: headerStyle },
        { value: "deliveryDateKey", width: 120, caption: "Delivery Date Key", headerFit: "vertical", headerStyle: headerStyle },
        { value: "salesPerson", width: 150, caption: "Sales Person", headerFit: "vertical", headerStyle: headerStyle },
        { value: "address", caption: "Address", width: 300, headerFit: "normal", headerStyle: headerStyle }

    ]);
    sheet.setDataView(view);
    sheet.options.defaultStackRowHeight = 30;
    sheet.setDefaultRowHeight(220, 1);
});

참고: 행 높이로 전체 캡션을 표시할 수 없는 경우, 해당 열의 스택된 헤더는 잘려서 표시될 수 있습니다. 이 경우 setDefaultRowHeight 메서드를 사용하여 조정할 수 있습니다.

셀 유형 및 드롭다운

테이블 시트 뷰에서 다양한 셀 유형과 드롭다운을 설정하여 정보가 표시되는 방식을 지정하고, 데이터를 보다 쉽게 선택할 수 있도록 도와줍니다.


Cell Types and Dropdowns in SpreadJS TableSheet


테이블 시트에서 지원하는 셀 유형과 드롭다운은 다음과 같습니다:

  • 셀 유형 – 체크박스, 콤보박스, 하이퍼링크, 라디오 버튼 리스트, 체크박스 리스트, 범위 템플릿

  • 드롭다운 – 색상 선택기, 날짜 선택기, 시간 선택기, 월 선택기, 리스트, 슬라이더, 계산기, 워크플로우 리스트, 다중 열

다음 코드 샘플은 테이블 시트 뷰에 셀 유형과 드롭다운을 추가하는 방법을 보여줍니다.

//  RadioButtonList
var radioButtonListStyle = new GC.Spread.Sheets.Style();
var cellTypeRadioButtonList = new GC.Spread.Sheets.CellTypes.RadioButtonList();
cellTypeRadioButtonList.items([
    { text: "Tailspin Toys (Head Office)", value: "Tailspin Toys (Head Office)" },
    { text: "Wingtip Toys (Head Office)", value: "Wingtip Toys (Head Office)" },
]);
radioButtonListStyle.cellType = cellTypeRadioButtonList;

//  ButtonList
var buttonListStyle = new GC.Spread.Sheets.Style();
var buttonListCellType = new GC.Spread.Sheets.CellTypes.ButtonList();
buttonListCellType.items([
    { text: "Tailspin Toys", value: "Tailspin Toys" },
    { text: "Wingtip Toys", value: "Wingtip Toys" },
]);
buttonListStyle.cellType = buttonListCellType;

// Date DropDown
var dateStyle = {};
dateStyle.cellButtons = [
    {
        imageType: "dropdown",
        command: "openDateTimePicker",
        useButtonStyle: true,
    }
];
dateStyle.dropDowns = [
    {
        type: "dateTimePicker",
        option: {
            showTime: true
        }
    }
];

//tablesheet에 뷰 바인딩
customerTable.fetch().then(function () {
    var view = customerTable.addView("myView", [
        { value: "customerKey", width: 120, caption: "Customer Key" },
        { value: "billToCustomer", width: 400, caption: "Bill To Customer", style: radioButtonListStyle },
        { value: "buyingGroup", width: 250, caption: "Buying Group", style: buttonListStyle },
        { value: "primaryContact", width: 170, caption: "Primary Contact" },
        { value: "validFrom", width: 190, caption: "Valid From", style: dateStyle }
    ]);
    //테이블의 모든 기본 열을 가진 뷰
    sheet.setDataView(view);
});

열 캡션 다중 헤더

테이블 시트 뷰에서는 여러 열을 공통 헤더 아래로 그룹화할 수 있도록 열 캡션을 설정할 수 있습니다. 이는 관련 정보를 공통 범주로 묶어 보다 체계적으로 표현하는 데 도움이 됩니다.

addView 메서드에 전달되는 columnInfos 매개변수의 "caption" 속성은 문자열 배열을 받아 테이블 시트의 열 헤더를 여러 행으로 설정할 수 있습니다. 동일한 캡션 값을 가진 열들은 행 방향과 열 방향으로 자동 병합됩니다.

다음 예제에서는 Customer Key, Customer Name, Bill To Customer, Primary Contact와 같은 고객 정보를 “Customer Information”이라는 공통 캡션 아래에 그룹화하여 표시하며, 각 열의 기존 캡션은 유지됩니다. 이와 유사하게 제품 유효 기간도 하나의 공통 헤더 아래 표시할 수 있습니다.



다음 코드 샘플은 테이블 시트 뷰에서 여러 열에 동일한 캡션을 설정하는 방법을 보여줍니다.

Ask ChatGPT

//tablesheet 뷰에 바인딩
myTable.fetch().then(function () {
   var view = myTable.addView("myView", [
       { value: "customerKey", width: 130, caption: ["Customer Information", "Customer Key"] }, // Set string array to caption
       { value: "customer", width: 200, caption: ["Customer Information", "Customer"] },
       { value: "billToCustomer", width: 200, caption: ["Customer Information", "Bill To Customer"] },
       { value: "primaryContact", width: 170, caption: ["Customer Information", "Primary Contact"] },
       { value: "validFrom", width: 150, caption: ["Validity", "Valid From"], style: { formatter: "MM/dd/yyyy"} },
       { value: "validTo", width: 180, caption: ["Validity", "Valid To"], style: { formatter: "MM/dd/yyyy"} }
   ]);
   sheet.setDataView(view);
});

SpreadJS 디자이너에서 다중 헤더 열 캡션을 적용하는 방법은 테이블 시트 디자인 모드를 참고하시기 바랍니다.

자유 헤더 영역

테이블 시트 뷰에는 시트를 설명하거나 수식과 스파크라인을 사용하여 집계 데이터를 표시할 수 있는 헤더 영역을 추가할 수 있습니다. 자유 헤더 영역은 테이블 시트 뷰 상단에 할당되며, 아래 이미지와 같이 하나 이상의 행으로 구성될 수 있습니다.


열 헤더의 자유 레이아웃 영역을 구성하려면 applyFreeHeaderArea 메서드를 사용합니다. 이 메서드는 toJSON 메서드를 사용하여 생성한 워크시트 JSON을 인수로 받습니다. 시트 이름이 포함된 수식을 유지하려면 JSON을 생성하기 전에 keepUnknownFormulas 옵션을 true로 설정해야 합니다.


다음 코드 샘플은 템플릿 워크시트를 생성하고 테이블 시트 뷰에 자유 헤더 영역을 적용하는 방법을 보여줍니다.

Ask ChatGPT

//tablesheet에 뷰 바인딩
myTable.fetch().then(function () {
   var view = myTable.addView("myView", [

       { value: "stockItem", width: 300, caption: "Stock Item", headerStyle: headerStyle },
       { value: "unitPrice", width: 120, caption: "Unit Price", headerStyle: headerStyle },
       { value: "taxRate", width: 120, caption: "Tax Rate", headerStyle: headerStyle },
       { value: "recommendedRetailPrice", width: 190, caption: "Recommended Retail Price", style: currencyFormatter, headerStyle: headerStyle, conditionalFormats: [dataBarRule1] },
       { value: "=([@unitPrice] * [@taxRate])/100 + [@unitPrice]", caption: "Actual Retail Price", style: currencyFormatter, width: 190, headerStyle: headerStyle, conditionalFormats: [dataBarRule2] }, // 계산된 열 

   ]);

   // 자유 헤더 영역 json에 대한 템플릿 시트 생성
   var templateSheet = new GC.Spread.Sheets.Worksheet();
   templateSheet.options.keepUnknownFormulas = true;
   var currencyFormatterStyle = new GC.Spread.Sheets.Style();
   currencyFormatterStyle.formatter = formatter;
   currencyFormatterStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.left;
   templateSheet.setRowCount(6);
   var freeHeaderAreaStyle = new GC.Spread.Sheets.Style();
   freeHeaderAreaStyle.backColor = Colors.white;

   templateSheet.setValue(0, 0, "Retail Price Analysis");
   templateSheet.getCell(0, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 30px \"Calibri\"");
   templateSheet.addSpan(0, 0, 1, 5);
   var titleStyle = new GC.Spread.Sheets.Style();
   titleStyle.backColor = Colors.lightGreen;
   titleStyle.foreColor = Colors.black;
   templateSheet.setStyle(0, -1, titleStyle);
   templateSheet.setRowHeight(0, 80);

   templateSheet.getCell(1, 0)
       .value("Whether a Stock Item's Actual Retail Price is either under or over Recommended Retail Price is automatically calculated.")
       .font("italic 12px \"Calibri\"")
       .foreColor("rgb(120,120,120)")
       .textIndent(2);
   templateSheet.setStyle(1, -1, titleStyle);
   templateSheet.addSpan(1, 0, 1, 5);

   var headerDescriptionStyle = new GC.Spread.Sheets.Style();
   headerDescriptionStyle.backColor = Colors.lightGreen;
   headerDescriptionStyle.borderBottom = new GC.Spread.Sheets.LineBorder(Colors.middleGreen, GC.Spread.Sheets.LineStyle.thick);
   templateSheet.setStyle(2, -1, headerDescriptionStyle);
   templateSheet.addSpan(2, 0, 1, 5);
   templateSheet.setRowHeight(2, 16);

   templateSheet.setValue(3, 0, "Total Recommended Retail Price");
   templateSheet.getCell(3, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.darkGreen);
   templateSheet.addSpan(3, 0, 1, 2);
   templateSheet.setFormula(3, 2, '=SUM(TableSheet1[Recommended Retail Price])');
   templateSheet.getCell(3, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.darkGreen).formatter(formatter);
   templateSheet.setFormula(3, 3, '=HBARSPARKLINE(ROUND(C4/MAX(C4,C5),2),"' + Colors.darkGreen + '",false)');
   templateSheet.addSpan(3, 3, 1, 2);
   templateSheet.setStyle(3, -1, freeHeaderAreaStyle);

   templateSheet.setValue(4, 0, "Total Actual Retail Price");
   templateSheet.getCell(4, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.brown);
   templateSheet.addSpan(4, 0, 1, 2);
   templateSheet.setFormula(4, 2, '=SUM(TableSheet1[Actual Retail Price])');
   templateSheet.getCell(4, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.brown).formatter(formatter);
   var contentBorderStyle = new GC.Spread.Sheets.Style();
   contentBorderStyle.backColor = Colors.white;
   contentBorderStyle.borderBottom = new GC.Spread.Sheets.LineBorder(Colors.brown, GC.Spread.Sheets.LineStyle.thick);
   templateSheet.setStyle(4, -1, contentBorderStyle);
   templateSheet.setFormula(4, 3, '=HBARSPARKLINE(ROUND(C5/MAX(C4,C5),2),"' + Colors.brown + '",false)');
   templateSheet.addSpan(4, 3, 1, 2);

   templateSheet.setFormula(5, 0, '=IF(C4>C5,"Budget is under Total Recommended Retail Price by","Budget is over Total Recommended Retail Price")');
   templateSheet.getCell(5, 0).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").foreColor(Colors.black);
   templateSheet.addSpan(5, 0, 1, 2);
   templateSheet.setFormula(5, 2, '=C4-C5');
   templateSheet.getCell(5, 2).hAlign(GC.Spread.Sheets.HorizontalAlign.left).font("bold 14px \"Calibri\"").formatter(formatter).foreColor(Colors.black);
   templateSheet.setStyle(5, -1, freeHeaderAreaStyle);
   templateSheet.addSpan(5, 2, 1, 3);

   let template = templateSheet.toJSON();
   sheet.applyFreeHeaderArea(template);
   sheet.setDataView(view);
});

자유 헤더는 셀에서 수식을 편집하거나 생성할 때 수식 입력 상자를 지원합니다. 이를 통해 테이블 시트 열, 열 범위, 여러 열, 교차 시트 열에 대한 선택 작업을 수행할 수 있습니다.


SpreadJS 디자이너에서 자유 헤더 영역을 적용하는 방법은 테이블 시트 디자인 모드를 참고하시기 바랍니다.

트리거 수식

컬럼에 독립적인 데이터를 저장하면서도 트리거 수식을 사용하여 테이블 시트의 컬럼 데이터를 계산할 수 있습니다. 이는 선택된 조건에 따라 데이터를 다시 계산하거나, 새 값이 입력될 때 데이터를 정리하거나, 컬럼에 적절한 기본값을 설정하는 데 유용합니다.

SpreadJS는 트리거를 구성하기 위해 when, formula, fields와 같은 속성을 포함하는 새로운 인터페이스 GC.Data.ITriggerFormulaOptions를 제공합니다.

이름

설명

사용법

when

(필수)

언제 트리거할지를 나타냅니다.

  • onNew: 새로 생성된 데이터에 수식을 적용.

  • onNewAndUpdate: 새로 생성되거나 업데이트된 데이터에 수식을 적용.

formula

(필수)

적용할 수식을 나타냅니다.

평가된 수식 결과가 행 데이터에 설정됩니다.

fields

(필수)

수식 트리거에 영향을 줄 수 있는 필드를 나타냅니다.

“Any Field”는 모든 필드를 의미합니다.

“field1, field2, field3“ 은 쉼표로 구분된 관련 필드를 의미합니다.

when 값이 onNew인 경우 필드 지정이 필요 없으나, 그 외에는 필수입니다.

다음 코드 샘플은 트리거 수식을 구현한 예제입니다.

schema: {
        type: "csv",
        columns: {
            SKU: {
                dataType: "string",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=UPPER(TRIM([@SKU]))", // SKU 입력 텍스트 포맷팅
                    fields: "SKU" // SKU 필드가 입력되면 트리거가 발동되어 입력 텍스트를 포맷팅
                }
            },
            OrderDate: {
                dataType: "date",
                trigger: {
                    when: "onNew", // 새 주문이 추가될 때 주문 생성일 기록
                    formula: "=TODAY()"
               }
            },
            lastUpdatedTime: {
                dataType: "date",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=NOW()",
                    fields: "*" // 모든 필드가 업데이트되거나 주문이 추가되면 갱신 시간 기록
                }
            },
            lastUpdateUser: {
                dataType: "string",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=ACTIVEUSER()", // 사용자가 현재 업데이트한 사용자 정의 가능
                    fields: "*" // 모든 필드가 업데이트되거나 주문이 추가되면 업데이트 사용자 기록
                }
            },
            OrderPhone: {
                dataType: "string",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=IF(ISBLANK([@OrderPhone]),[@customer.Phone],[@OrderPhone])",
                    fields: "CustomerId,OrderPhone"// 주문 전화번호가 비어 있으면 고객 전화번호 제공
                }
            },
            Amount: {
                dataType: "number",
                trigger: {
                    when: "onNewAndUpdate",
                    formula: "=LET(amount, [@UnitPrice] * [@Quantity], IF([@Quantity] > 30, amount * 0.8, amount))",
                    fields: "UnitPrice,Quantity"  // 단가 또는 수량 변경 시 자동으로 계산된 결과를 Amount 필드에 설정
                }
            },
            CustomerId: {
                lookup: {
                    name: 'customer', columns: [
                        { value: "Id", width: 60 },
                        { value: "CompanyName", width: 200 },
                        { value: "ContactName", width: 140 },
                        { value: "ContactTitle", width: 140 }
                    ]
                }
            },
            Quantity: { dataType: "number" },
            UnitPrice: { dataType: "number" },
        }
    }
});

트리거 수식에는 다음과 같은 정책이 적용됩니다:

  • 새 행 데이터가 추가되면, onNewonNewAndUpdating 트리거가 평가되어 업데이트와 유사하게 동작합니다.

  • 행의 여러 필드 데이터가 업데이트되면, 트리거는 감시 중인 필드의 값을 변경합니다. 그렇지 않으면 트리거되지 않습니다.

  • 필드의 트리거가 자기 자신을 감시하면, 새 값으로 수식을 평가하고 그 결과를 해당 필드의 업데이트 값으로 처리합니다.

  • 트리거 수식에서 평가된 값은 해당 필드의 업데이트 값으로 간주되며, 이는 트리거 수식 평가 시 직접 참조될 수 있습니다.

  • 컬럼에 기본값과 트리거 수식이 동시에 적용된 경우, 기본값이 더 높은 우선순위를 가집니다.