들어가며

B1-1, B1-2 강좌에서 Filter와 FilterAction의 기본적인 사용방법을 배웠습니다. 이 강좌에서는 FilterFilterAction을 응용하여 엑셀의 자동 필터와 유사한 UI로 구현하는 방법을 알아봅시다.

이론

  1. 컬럼의 Filter Icon를 클릭하였을 때 다음의 조건이 모두 만족하면 Filter 목록이 표시되지 않고 바로 GridBase.onFilterActionClicked() 콜백이 발생합니다.
    • hidden속성이 false인 필터가 존재하지 않음
    • 유일한 FilterAction
  2. onFilterActionClicked에서 전달되는 x,y 좌표를 기준으로 별도의 division(divAutoFilter)을 표시합니다.
  3. divAutoFilter에는 DataProvider.getDistinctValues()을 사용하여 해당 컬럼의 데이터들을 중복 없이 순차 정렬하여 표시하고 각각의 데이터에 checkbox를 배치합니다.
  4. checkbox가 클릭되면 check된 데이터들을 하나의 수식으로 조합 후 GridBase.addColumnFilters()함수를 호출하여 Filter로 등록합니다.
  5. Filter를 등록할 때 hidden 속성이 false이면 다시 Filter Icon을 클랙했을 때 1번의 조건을 만족하지 않아서 Filter 목록이 표시되므로 hidden속성을 true로 등록합니다.

실습

  1. 전역 변수 선언

    GridBaseDataProvider객체를 담기 위한 기본 변수와 FilterAction이 선택된 컬럼을 저장하기 위한 filterColumn변수, division을 닫았다가 다시 띠을 때 이미 선택했던 값들을 저장하기 위한 Array autoFilterItems 변수를 전역으로 선언합니다.

     var gridView;
     var dataProvider;
     var filterColumn = null;
     var autoFilterItems = [];
  2. Field및 Column구성, 기초 데이터 구성

     dataProvider.setFields([{
       fieldName: "textField"
     }, {
       fieldName: "numberField",
       dataType: "number"
     }]);
    
     gridView.setColumns([{
       fieldName: "textField",
       name: "textColumn",
       width: 200,
       header: {text: "Text Column"}
     }, {
       fieldName: "numberField",
       name: "numberColumn",
       width: 200,
       header: {text: "Number Column"}
     }]);
    
     dataProvider.setRows([{
       textField: "A",
       numberField: 1
     }, {
       textField: "B",
       numberField: 2
     }, {
       textField: "C",
       numberField: 3
     }, {
       textField: "D",
       numberField: 4
     }, {
       textField: "E",
       numberField: 5
     }]);
  3. 각 컬럼에 “auto filter” FilterAction 추가

     var actions = [{
       name: "__autofilter",
       text: "auto filter"
     }];
    
     gridView.setColumnFilterActions("textColumn", actions);
     gridView.setColumnFilterActions("numberColumn", actions);
  4. onFilterActionClicked 콜백 함수 작성

     gridView.onFilterActionClicked = function (grid, column, action, x, y) {
       if (action == "__autofilter") {
         filterColumn = gridView.columnByName(column);
         ...
     }

    filterColumn의 데이터를 spanFilters의 하위에 label과 checkbox형태로 추가

     var values = dataProvider.getDistinctValues(filterColumn.fieldName, 100);
     var span = $("#spanFilters");
     span.empty();
     values.forEach(function (v) {
         var label = $("<label />").appendTo(span);
         var existsFilter = autoFilterItems.indexOf(v+"") >= 0;
         $("<input />", { type: "checkbox", name: "chkAutoFilterItem", value: v, onclick: "filterCheckChanged();", checked: existsFilter}).appendTo(label);
         label.append(v);
         span.append("
    "); });

    divAutoFilter가 표시될 좌표 계산 로직. 이 강좌에서는 페이지가 중앙 정렬되어 margin-left, padding-left값을 구해 absolute position의 좌표값에서 감하는 로직도 구현되었습니다. 실제 사용시에는 이와 다를 수 있으므로 참고하시기 바랍니다.

         
     // 페이지 offset 계산
     var page = $("div.page-content > div.wrapper");
     var pageOffset = page.offset();
     pageOffset.left += parseInt(page.css("padding-left"));
    
     // 그리드 offset 계산 및 x,y 결정
     var gridOffset = $("#realgrid").offset();
     x = x + gridOffset.left - pageOffset.left;
     y = y + gridOffset.top - pageOffset.top;
    
     // divAutoFilter를 컬럼의 우측에 정렬시키기 위해 컬럼의 폭에서 divAutoFilter의 폭를 뺀만큼 x를 증가 
     width = filterColumn.displayWidth;
    
     x += (width - $("#divAutoFilter").outerWidth());
    
     $("#divAutoFilter").css("left", x);
     $("#divAutoFilter").css("top", y);
     
     $("#divAutoFilter").show();
  5. checkbox를 변경했을 때 호출되는 filterCheckChanged 함수 선언

     function filterCheckChanged(e) {
       // 기존 auto_result 제거 및 다른 필더 비활성화
       var columns = gridView.getColumns();
       for(var c in columns) {
         gridView.removeColumnFilters(columns[c], ["auto_result"]);
         gridView.activateAllColumnFilters(columns[c], false);
       }
    
       // 선택된 checkbox를 하나의 filter식으로 조합. ex) (value = 'A') or (value = 'B')
       var filterExpr = "";
       var checkedFilters = $("input[name='chkAutoFilterItem']:checked");
       autoFilterItems = [];
       for (var i = 0; i < checkedFilters.length; i++) {
           autoFilterItems.push(checkedFilters[i].value);
           if (filterExpr != "")
               filterExpr += " or ";
           filterExpr += "(value = '" + checkedFilters[i].value + "')";
       };
    
       // auto_result filter 등록
       var filters = {
           name: "auto_result",
           criteria: filterExpr,
           active: true,
           hidden: true
       };
       gridView.addColumnFilters(filterColumn, filters);
     }

실행화면

각 컬럼의 필터아이콘을 클릭한 후 각 데이터 Text Column은 A, B, C와 같은 형태로 Quotation Mark(')가 빠진 상태로 입력합니다. Apply버튼을 누르면 입력한 사용자 지정 필터가 적용됩니다.

전체 소스코드

SCRIPT
<script type="text/javascript" src="/script/realgridjs-lic.js"></script>
<script type="text/javascript" src="/script/realgridjs_eval.1.0.14.min.js"></script>
<script type="text/javascript" src="/script/realgridjs-api.1.0.14.js"></script>

<script language="javascript">
  var gridView;
  var dataProvider;
  var filterColumn = null;
  var autoFilterItems = [];

  $(document).ready( function() {

    RealGridJS.setTrace(false);
    RealGridJS.setRootContext("/script");
    
    dataProvider = new RealGridJS.LocalDataProvider();
    gridView = new RealGridJS.GridView("realgrid");
    gridView.setDataSource(dataProvider);

    dataProvider.setFields([{
      fieldName: "textField"
    }, {
      fieldName: "numberField",
      dataType: "number"
    }]);

    gridView.setColumns([{
      fieldName: "textField",
      name: "textColumn",
      width: 200,
      header: {text: "Text Column"}
    }, {
      fieldName: "numberField",
      name: "numberColumn",
      width: 200,
      header: {text: "Number Column"}
    }]);

    dataProvider.setRows([{
      textField: "A",
      numberField: 1
    }, {
      textField: "B",
      numberField: 2
    }, {
      textField: "C",
      numberField: 3
    }, {
      textField: "D",
      numberField: 4
    }, {
      textField: "E",
      numberField: 5
    }]);

    var actions = [{
      name: "__autofilter",
      text: "auto filter"
    }];

    gridView.setColumnFilterActions("textColumn", actions);
    gridView.setColumnFilterActions("numberColumn", actions);

    gridView.onFilterActionClicked = function (grid, column, action, x, y) {
      if (action == "__autofilter") {
        filterColumn = gridView.columnByName(column);
        var values = dataProvider.getDistinctValues(filterColumn.fieldName, 100);
     
        var span = $("#spanFilters");
        span.empty();
        values.forEach(function (v) {
            var label = $("<label />").appendTo(span);
            var existsFilter = autoFilterItems.indexOf(v+"") >= 0;
            $("<input />", { type: "checkbox", name: "chkAutoFilterItem", value: v, onclick: "filterCheckChanged();", checked: existsFilter}).appendTo(label);
            label.append(v);
            span.append("<br/>");
        });
     
        var page = $("div.page-content > div.wrapper");
        var pageOffset = page.offset();
        pageOffset.left += parseInt(page.css("padding-left"));
        var gridOffset = $("#realgrid").offset();
        x = x + gridOffset.left - pageOffset.left;
        y = y + gridOffset.top - pageOffset.top;

        width = filterColumn.displayWidth;

        x += (width - $("#divAutoFilter").outerWidth());

        $("#divAutoFilter").css("left", x);
        $("#divAutoFilter").css("top", y);
     
        $("#divAutoFilter").show();
      }
    }

    $("#btnClose").click(function() {
      $("#divAutoFilter").hide();
    })
  })

  function filterCheckChanged(e) {
    var columns = gridView.getColumns();
    for(var c in columns) {
      gridView.removeColumnFilters(columns[c], ["auto_result"]);
      gridView.activateAllColumnFilters(columns[c], false);
    }

    var filterExpr = "";
    var checkedFilters = $("input[name='chkAutoFilterItem']:checked");
    autoFilterItems = [];
    for (var i = 0; i < checkedFilters.length; i++) {
        autoFilterItems.push(checkedFilters[i].value);
        if (filterExpr != "")
            filterExpr += " or ";
        filterExpr += "(value = '" + checkedFilters[i].value + "')";
    };
    var filters = {
        name: "auto_result",
        criteria: filterExpr,
        active: true,
        hidden: true
    };
    gridView.addColumnFilters(filterColumn, filters);
  }
</script>
HTML
<div id="realgrid" style="width: 100%; height: 350px;"></div>

<div id="divAutoFilter" style="display:none; position:absolute; height:200px; background-color:#ffffff; border:1px solid black;">
    <span id="spanFilters" style="overflow-y:scroll; display:block; width:100%; height:170px">
    </span>
    <input type="button" id="btnClose" value="Close" />
</div>

관련 데모 페이지


API 참조