B1-3 Column Filtering - Filter 응용 사용자 지정 필터 구현
들어가며
B1-1, B1-2 강좌에서 Filter와 FilterAction의 기본적인 사용방법을 배웠습니다.
이 강좌에서는 Filter와 FilterAction을 응용하여 엑셀의 사용자 정의 필터와 유사한 UI로 구현하는 방법을 알아봅시다.
FilterAction에 “사용자 정의 필터” 항목을 추가하여 이 FilterAction을 클릭하였을 때 별도의 Dialog에서 수식을 사용자가 선택하여 Filter로 추가하는 방법으로 강의를 진행하겠습니다.
이론
- GridBase.setColumnFilterActions()함수를 사용하여 각 컬럼에 사용자 정의 필터라고 표시되는 FilterAction을 추가합니다. FilterAction의 이름은 고유해야하므로 중복되지 않는 하나의 이름을 지정합니다. 이 강좌에서는__customfilter라고 명명하였습니다.
- GridBase.onFilterActionClicked()콜백 함수를 지정하여 Click된 FilterAction이 __customfilter일 때사용자 자동 필터Dialog를 표시합니다.
- Dialog내의 입력 Control의 이름은 다음과 같습니다.
    dialog_condition1 : 첫번째 수식의 비교 조건 dialog_value1 : 첫번째 수식의 비교 값 dialog_condition2 : 두번째 수식의 비교 조건 dialog_value2 : 두번째 수식의 비교 값 dialog_logicOp : 논리 연산
- 
    비교 조건 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>
- like수식은 문자형 컬럼에서만 사용 가능합니다.
- 선택된 비교조건과 비교값 논리부호로 Filter에 사용할 수식을 만듭니다.
비교조건 dropdown의 선택값에서 {text}부분을 비교값으로 대체하면 가능합니다.
이 때 like에는 이미 dropdown의 값에 Quotation Mark(')가 포함되어 있지만 like가 아닌 경우 포함되어 있지 않으므로 문자열의 경우 추가해야 합니다.
- 다른 컬럼의 필터가 존재하면 결과가 다르게 나올 수 있으므로 GridBase.removeColumnFilters()을 사용하여 기존 custom_filter를 제거하고, GridBase.activateAllColumnFilters()을 사용하여 현재 활성화된 다른 filter들을 비활성화시켜야 합니다. 그 후 GridBase.addColumnFilters() 함수를 호출하여 새로운 custom_filter를 추가합니다. 이 때 추가되는 filter의 active속성을 true로 해서 추가와 동시에 활성화 시킵니다.
실습
- 
    전역 변수 선언 GridBase와 DataProvider객체를 담기 위한 기본 변수와 FilterAction이 선택된 컬럼을 담기위한 filterColumn변수를 전역으로 선언합니다. var gridView; var dataProvider; var filterColumn = null; 
- 
    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 }]);
- 
    각 컬럼에 “사용자 정의 필터” FilterAction 추가 var actions = [{ name: "__customfilter", text: "사용자 지정 필터 ..." }]; gridView.setColumnFilterActions("textColumn", actions); gridView.setColumnFilterActions("numberColumn", actions);
- 
    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'); } }
- 
    Dialog Apply button callback 함수 $("#dialog_btnApply").click(function() { if (filterColumn == null) return; ... });
- 
    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 + ")"; } }
- 
    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("<","<").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 + ")";
        }
      }
      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 참조
 RealGrid HELP
 RealGrid HELP