들어가며

B1-1, B1-2 강좌에서 Filter와 FilterAction의 기본적인 사용방법을 배웠습니다. 이 강좌에서는 FilterFilterAction을 응용하여 엑셀의 사용자 정의 필터와 유사한 UI로 구현하는 방법을 알아봅시다. FilterAction에 “사용자 정의 필터” 항목을 추가하여 이 FilterAction을 클릭하였을 때 별도의 Dialog에서 수식을 사용자가 선택하여 Filter로 추가하는 방법으로 강의를 진행하겠습니다.

이론

  1. GridBase.setColumnFilterActions()함수를 사용하여 각 컬럼에 사용자 정의 필터라고 표시되는 FilterAction을 추가합니다. FilterAction의 이름은 고유해야하므로 중복되지 않는 하나의 이름을 지정합니다. 이 강좌에서는 __customfilter라고 명명하였습니다.
  2. GridBase.onFilterActionClicked()콜백 함수를 지정하여 Click된 FilterAction이 __customfilter일 때 사용자 자동 필터 Dialog를 표시합니다.
  3. Dialog내의 입력 Control의 이름은 다음과 같습니다.
    dialog_condition1 : 첫번째 수식의 비교 조건
    dialog_value1 : 첫번째 수식의 비교 값
    dialog_condition2 : 두번째 수식의 비교 조건
    dialog_value2 : 두번째 수식의 비교 값
    dialog_logicOp : 논리 연산
    
  4. 비교 조건 dropdown의 내용은 다음과 같습니다.

     <option value="value = {text}">=</option>
     <option value="value <> {text}"><></option>
     <option value="value > {text}">></option>
     <option value="value >= {text}">>=</option>
     <option value="value < {text}"><</option>
     <option value="value <= {text}"><=</option>
     <option value="value like '{text}%'">시작 문자</option>
     <option value="value not like '{text}%'">제외할 시작 문자</option>
     <option value="value like '%{text}'">끝 문자</option>
     <option value="value not like '%{text}">제외할 끝 문자</option>
     <option value="value like '%{text}%'">포함</option>
     <option value="value not like '%{text}%">포함하지 않음</option>
  5. like 수식은 문자형 컬럼에서만 사용 가능합니다.
  6. 선택된 비교조건과 비교값 논리부호로 Filter에 사용할 수식을 만듭니다. 비교조건 dropdown의 선택값에서 {text}부분을 비교값으로 대체하면 가능합니다. 이 때 like에는 이미 dropdown의 값에 Quotation Mark(')가 포함되어 있지만 like가 아닌 경우 포함되어 있지 않으므로 문자열의 경우 추가해야 합니다.
  7. 다른 컬럼의 필터가 존재하면 결과가 다르게 나올 수 있으므로 GridBase.removeColumnFilters()을 사용하여 기존 custom_filter를 제거하고, GridBase.activateAllColumnFilters()을 사용하여 현재 활성화된 다른 filter들을 비활성화시켜야 합니다. 그 후 GridBase.addColumnFilters() 함수를 호출하여 새로운 custom_filter를 추가합니다. 이 때 추가되는 filter의 active속성을 true로 해서 추가와 동시에 활성화 시킵니다.

실습

  1. 전역 변수 선언

    GridBase와 DataProvider객체를 담기 위한 기본 변수와 FilterAction이 선택된 컬럼을 담기위한 filterColumn변수를 전역으로 선언합니다.

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

     dataProvider.setFields([{
       fieldName: "textField"
     }, {
       fieldName: "numberField",
       dataType: "number"
     }]);
    
     gridView.setColumns([{
       fieldName: "textField",
       name: "textColumn",
       width: 200,
       header: {text: "Text Column"},
       filters: [{
         name: "A",
         criteria: "value = 'A'"
       }, {
         name: "B",
         criteria: "value = 'B'"
       }, {
         name: "C",
         criteria: "value = 'C'"
       }, {
         name: "D",
         criteria: "value = 'D'"
       }, {
         name: "E",
         criteria: "value = 'E'"
       }]
     }, {
       fieldName: "numberField",
       name: "numberColumn",
       width: 200,
       header: {text: "Number Column"},
       filters: [{
         name: "1",
         criteria: "value = 1"
       }, {
         name: "2",
         criteria: "value = 2"
       }, {
         name: "3",
         criteria: "value = 3"
       }, {
         name: "4",
         criteria: "value = 4"
       }, {
         name: "5",
         criteria: "value = 5"
       }]
     }]);
    
     dataProvider.setRows([{
       textField: "A",
       numberField: 1
     }, {
       textField: "B",
       numberField: 2
     }, {
       textField: "C",
       numberField: 3
     }, {
       textField: "D",
       numberField: 4
     }, {
       textField: "E",
       numberField: 5
     }]);
  3. 각 컬럼에 “사용자 정의 필터” FilterAction 추가

     var actions = [{
       name: "__customfilter",
       text: "사용자 지정 필터 ..."
     }];
    
     gridView.setColumnFilterActions("textColumn", actions);
     gridView.setColumnFilterActions("numberColumn", actions);
  4. onFilterActionClicked 콜백 함수 작성

     gridView.onFilterActionClicked = function (grid, column, action, x, y) {
       if (action == "__customfilter") {
         filterColumn = gridView.columnByName(column);
    
         // 다이얼로그에 ColumnHeader.text를 표시
         var title = gridView.getColumnProperty(filterColumn, "header");
         $("#dialog_columnName").text(title.text);
    
         $("#filterDialog").modal('show');
       }
     }
  5. Dialog Apply button callback 함수

     $("#dialog_btnApply").click(function() {
       if (filterColumn == null)
         return;
    
       ...
     });
    
    
  6. Filter 수식 조합

    이 강좌에서는 환경적 요인으로 dropdown의 값 <, >가 자동으로 Encode 되어서 이를 Decode하였으나, 실제 개발 환경에서는 Decode는 하지 않아도 무방합니다.

     var expr;
     var cond1 = $("#dialog_condition1").val().replace("<","<").replace(">", ">");
     var val1 = $("#dialog_value1").val();
     if (val1 && val1.length > 0) {
       if (filterColumn.name == "textColumn" && cond1.indexOf("like") < 0)
         val1 = "'" + val1 + "'";
    
       expr = cond1.replace("{text}", val1);
    
       val2 = $("#dialog_value2").val();
       if (val2 && val2.length > 0) {
         if (filterColumn.name == "textColumn" && cond2.indexOf("like") < 0)
           val2 = "'" + val2 + "'";
         var cond2 = $("#dialog_condition2").val().replace("<","<").replace(">", ">");
         var expr2 = cond2.replace("{text}", val2);
    
         var logicOp = $("input:radio[name='dialog_logicOp']:checked").val();
         expr = "(" + expr + ") " + logicOp + " (" + expr2 + ")";
       }
     }
  7. Filter 등록

     if (expr) {
       // 모든 컬럼의 기존 custom_filter 제거 및 다른 filter deactivate
       var columns = gridView.getColumns();
       for(var c in columns) {
         gridView.removeColumnFilters(columns[c], ["custom_filter"]);
         gridView.activateAllColumnFilters(columns[c], false);
       }
    
       // 새로운 custom filter 추가
       var filters = [{
         name: "custom_filter",
         criteria: expr,
         active: 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;

  $(document).ready( function() {

    $("#dialog_btnApply").click(function() {
      if (filterColumn == null)
        return;

      var expr;
      var cond1 = $("#dialog_condition1").val().replace("&lt;","<").replace("&gt;", ">");
      var val1 = $("#dialog_value1").val();
      if (val1 && val1.length > 0) {
        if (filterColumn.name == "textColumn" && cond1.indexOf("like") < 0)
          val1 = "'" + val1 + "'";

        expr = cond1.replace("{text}", val1);

        val2 = $("#dialog_value2").val();
        if (val2 && val2.length > 0) {
          if (filterColumn.name == "textColumn" && cond2.indexOf("like") < 0)
            val2 = "'" + val2 + "'";
          var cond2 = $("#dialog_condition2").val().replace("&lt;","<").replace("&gt;", ">");
          var expr2 = cond2.replace("{text}", val2);

          var logicOp = $("input:radio[name='dialog_logicOp']:checked").val();
          expr = "(" + expr + ") " + logicOp + " (" + expr2 + ")";
        }
      }

      if (expr) {
        // 모든 컬럼의 기존 custom_filter 제거 및 다른 filter deactivate
        var columns = gridView.getColumns();
        for(var c in columns) {
          gridView.removeColumnFilters(columns[c], ["custom_filter"]);
          gridView.activateAllColumnFilters(columns[c], false);
        }

        // 새로운 custom filter 추가
        var filters = [{
          name: "custom_filter",
          criteria: expr,
          active: true
        }];
        gridView.addColumnFilters(filterColumn, filters);      
      }
      $("#filterDialog").modal('hide');
      filterColumn = null;
    });

    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"},
      filters: [{
        name: "A",
        criteria: "value = 'A'"
      }, {
        name: "B",
        criteria: "value = 'B'"
      }, {
        name: "C",
        criteria: "value = 'C'"
      }, {
        name: "D",
        criteria: "value = 'D'"
      }, {
        name: "E",
        criteria: "value = 'E'"
      }]
    }, {
      fieldName: "numberField",
      name: "numberColumn",
      width: 200,
      header: {text: "Number Column"},
      filters: [{
        name: "1",
        criteria: "value = 1"
      }, {
        name: "2",
        criteria: "value = 2"
      }, {
        name: "3",
        criteria: "value = 3"
      }, {
        name: "4",
        criteria: "value = 4"
      }, {
        name: "5",
        criteria: "value = 5"
      }]
    }]);

    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: "__customfilter",
      text: "사용자 지정 필터 ..."
    }];

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

    gridView.onFilterActionClicked = function (grid, column, action, x, y) {
      if (action == "__customfilter") {
        filterColumn = gridView.columnByName(column);
        var title = gridView.getColumnProperty(filterColumn, "header");
        $("#dialog_columnName").text(title.text);
        $("#filterDialog").modal('show');
      }
    }
  })
</script>
HTML
<div id="realgrid" style="width: 100%; height: 200px;"></div>

<div class="modal fade" id="filterDialog" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
        <h4 class="modal-title">사용자 지정 자동 필터</h4>
      </div><div class="modal-body">
        <label>찾을 조건:</label>
        <div style="margin-left: 10px">
          <label id="dialog_columnName">Column</label>
          <div style="margin-left: 10px">
            <form class="form-inline">
              <select id="dialog_condition1" class="form-control" />
                <option value="value = {text}">=</option>
                <option value="value <> {text}"><></option>
                <option value="value > {text}">></option>
                <option value="value >= {text}">>=</option>
                <option value="value < {text}"><</option>
                <option value="value <= {text}"><=</option>
                <option value="value like '{text}%'">시작 문자</option>
                <option value="value not like '{text}%'">제외할 시작 문자</option>
                <option value="value like '%{text}'">끝 문자</option>
                <option value="value not like '%{text}">제외할 끝 문자</option>
                <option value="value like '%{text}%'">포함</option>
                <option value="value not like '%{text}%">포함하지 않음</option>
              </select>
              <input type="text" id="dialog_value1" class="form-control" />
            </form>
            <form class="form-inline">
              <div class="radio">
                <label>
                  <input type="radio" name="dialog_logicOp" id="dialog_logicAnd" value="and">
                  그리고
                </label>
              </div><div class="radio">
                <label>
                  <input type="radio" name="dialog_logicOp" id="dialog_logicOr" value="or">
                  또는
                </label>
              </div></form>
            <form class="form-inline">
              <select id="dialog_condition2" class="form-control"/>
                <option value="value = {text}">=</option>
                <option value="value <> {text}"><></option>
                <option value="value > {text}">></option>
                <option value="value >= {text}">>=</option>
                <option value="value < {text}"><</option>
                <option value="value <= {text}"><=</option>
                <option value="value like '{text}%'">시작 문자</option>
                <option value="value not like '{text}%'">제외할 시작 문자</option>
                <option value="value like '%{text}'">끝 문자</option>
                <option value="value not like '%{text}">제외할 끝 문자</option>
                <option value="value like '%{text}%'">포함</option>
                <option value="value not like '%{text}%">포함하지 않음</option>
              </select>
              <input type="text" id="dialog_value2" class="form-control" />
            </form>
          </div></div></div><div class="modal-footer">
        <button type="button" class="btn btn-primary" id="dialog_btnApply">Apply</button>
        <button type="button" class="btn btn-default" id="dialog_btnClose" data-dismiss="modal">Close</button>
      </div></div></div>
</div>

관련 데모 페이지


API 참조