Angular.js ui-grid custom date filter
Asked Answered
B

4

7

I am using the angular grid, ui-grid, located at ui-grid.info.

I am trying to make a custom filter that will filter the grid by date using date inputs controls, one for less than and one for greater than.

I seem to be able to put get the controls where i want them using this in the columnDefs: { field: 'mixedDate', cellFilter: 'date', filterHeaderTemplate: '<div>From <input type="date"> to <input type="date"></div>' }. I also can get some sort of filtering to work by setting the data-ng-model="colFilter.term" when putting these things in a different scope. The filtering doesn't seem to even do an equals though.

Does anyone have code for this that works or can point me in the right direction?

Here are some tutorials on the topic on their own site, but I'm not quite sure how to manipulate them to fit my needs or if it's even possible.

Brantbrantford answered 23/7, 2015 at 13:20 Comment(2)
Can you make a plnkr to show your issue.Acanthoid
Here's an example: plnkr.co/edit/R4cZa6dZllEjdHHMaHA9?p=preview. I can't seem to get the colFilter.term to work now though. It's based on the original sample from the ui-grid documentationBrantbrantford
K
4

Do you mean something like this ? enter image description here

First you should include jQuery UI Datepicker

Then you will also create a directive for it:

app.directive('datePicker', function(){
    return {
        restrict : "A",
        require: 'ngModel',
        link : function(scope, element, attrs, ngModel){
            $(function(){
                $(element).datepicker({
                     changeMonth: true,
                     changeYear: true,
                     closeText: 'Clear',
                     showButtonPanel: true,
                     onClose: function () {
                        var event = arguments.callee.caller.caller.arguments[0];
                        // If "Clear" gets clicked, then really clear it
                        if ($(event.delegateTarget).hasClass('ui-datepicker-close')) {
                            $(this).val('');
                            scope.$apply(function() {
                               ngModel.$setViewValue(null);
                            });
                        }
                    },
                    onSelect: function(date){
                        scope.$apply(function() {
                           ngModel.$setViewValue(date);
                        });
                    }
               });
            })
        }
    }
})

In your columnDefs you will also need to use customer filters and filter header template:

filters:[{ condition: checkStart}, {condition:checkEnd}],filterHeaderTemplate: '<div class="ui-grid-filter-container">from : <input style="display:inline; width:30%" class="ui-grid-filter-input" date-picker type="text" ng-model="col.filters[0].term"/> to : <input style="display:inline; width:30%" class="ui-grid-filter-input" date-picker type="text" ng-model="col.filters[1].term"/></div>'

Assume you are using momentjs The filter functions will be like this:

function checkStart(term, value, row, column) {
        term = term.replace(/\\/g,"")
        var now = moment(value);
        if(term) {
            if(moment(term).isAfter(now, 'day')) return false;;
        } 
        return true;
    }

    function checkEnd(term, value, row, column) {
        term = term.replace(/\\/g,"")
        var now = moment(value);
        if(term) {
            if(moment(term).isBefore(now, 'day')) return false;;
        } 
        return true;
    }
Kujawa answered 23/7, 2015 at 19:35 Comment(4)
Yes exactly that assuming the filter actually works correctly.Brantbrantford
Does the code for checkStart and checkEnd work in angularJS other than momentjs ? And did this code work jgerstle ?Lesseps
Can you add more description with example in plnkr.co ?Lesseps
I haven't tested it because I abandoned the project before this answer was given.Brantbrantford
C
3

If anyone else is looking for a solution to this, I implemented custom FROM and TO filters in the grid header using ui-bootstrap modals with datepickers in them. See this plunker for details.

It's obvious in retrospect, but one of the tripwires in finding the solution was making sure that the date values populating the date column in the grid were actually of type Date. As it turns out, comparing the dates selected from the datepickers to strings won't get you very far.

Note: this solution depends on lodash for some data transformation.

var app = angular.module('app', ['ngAnimate', 'ui.grid', 'ui.grid.selection', 'ui.bootstrap']);

app.controller('MainCtrl', ['$scope', '$http', 'uiGridConstants', function ($scope, $http, uiGridConstants) {

  $scope.gridOptions = {
    enableFiltering: true,
    onRegisterApi: function(gridApi){
      $scope.gridApi = gridApi;
    },
    columnDefs: [
      {
        field: 'DATE_TIME',
        displayName: 'Date Time',
        enableFiltering: true,
        enableCellEdit: false,
        filterHeaderTemplate: '<div class="ui-grid-filter-container row"><div ng-repeat="colFilter in col.filters" class="col-md-6 col-md-offset-0 col-sm-6 col-sm-offset-0 col-xs-6 col-xs-offset-0"><div custom-grid-date-filter-header></div></div></div>',
        filters: [
          {
            name: 'From',
            condition: uiGridConstants.filter.GREATER_THAN_OR_EQUAL
          },
          {
            name: 'To',
            condition: uiGridConstants.filter.LESS_THAN_OR_EQUAL
          }
        ],
        cellFilter: 'date:"M/d/yyyy h:mm:ss a"',
        width: '40%'
      },
      {
        field: 'QTY',
        displayName: 'Quantity',
        enableCellEdit: false,
        enableFiltering: false
      },
      {
        field: 'UNIT_COST',
        displayName: 'Unit Cost',
        enableCellEdit: false,
        enableFiltering: false
      },
      {
        field: 'TOTAL_COST',
        displayName: 'Total Cost',
        enableCellEdit: false,
        enableFiltering: false
      }
    ]
  };

  // in plnkr, grab the following data from external file
  // $http.get('grid-data.json')
  //    .success(function(data) {
  //    $scope.gridOptions.data = data;
  
  $scope.gridOptions.data = [
    {
      "DATE_TIME": "2015-10-12T10:46:27.000Z",
      "QTY": 3,
      "UNIT_COST": 0.25,
      "TOTAL_COST": 0.75
    },
    {
      "DATE_TIME": "2015-10-18T06:09:27.000Z",
      "QTY": 4,
      "UNIT_COST": 0.25,
      "TOTAL_COST": 1.00
    },
    {
      "DATE_TIME": "2015-10-05T11:57:27.000Z",
      "QTY": 6,
      "UNIT_COST": 0.55,
      "TOTAL_COST": 0.90
    },
    {
      "DATE_TIME": "2015-10-21T03:42:27.000Z",
      "QTY": 8,
      "UNIT_COST": 0.25,
      "TOTAL_COST": 2.00
    },
    {
      "DATE_TIME": "2015-09-29T18:25:27.000Z",
      "QTY": 3,
      "UNIT_COST": 0.45,
      "TOTAL_COST": 1.35
    },
    {
      "DATE_TIME": "2015-09-19T21:13:27.000Z",
      "QTY": 5,
      "UNIT_COST": 0.25,
      "TOTAL_COST": 1.25
    },
    {
      "DATE_TIME": "2015-08-31T15:46:27.000Z",
      "QTY": 7,
      "UNIT_COST": 0.10,
      "TOTAL_COST": 0.70
    },
    {
      "DATE_TIME": "2015-10-12T10:14:27.000Z",
      "QTY": 2,
      "UNIT_COST": 0.65,
      "TOTAL_COST": 1.30
    }
  ];

  // make sure date values are Date objects
  _.forEach($scope.gridOptions.data, function (val) {
    val.DATE_TIME = new Date(val.DATE_TIME);
  });

}])

.controller('gridDatePickerFilterCtrl', ['$scope', '$timeout', '$uibModal', 'uiGridConstants', function( $scope, $timeout, $uibModal, uiGridConstants) {

  $timeout(function() {
    console.log($scope.col); 
    var field = $scope.col.colDef.name;

    var allDates = _.map($scope.col.grid.appScope.gridOptions.data, function(datum) {
      return datum[field];
    });
      
    var minDate = _.min(allDates);
    var maxDate = _.max(allDates);

    $scope.openDatePicker = function(filter) {
      
      var modalInstance = $uibModal.open({
        templateUrl: 'custom-date-filter.html',
        controller: 'customGridDateFilterModalCtrl as custom',
        size: 'md',
        windowClass: 'custom-date-filter-modal',
        resolve: {
          filterName: [function() {
            return filter.name;
          }],
          minDate: [function() {
            return new Date(minDate);
          }],
          maxDate: [function() {
            return new Date(maxDate);
          }],
        }
      });
  
      modalInstance.result.then(function(selectedDate) {
        
        console.log('date', selectedDate);
        $scope.colFilter.listTerm = [];
        
        console.log(typeof selectedDate);
        console.log(selectedDate instanceof Date);
        
        $scope.colFilter.term = selectedDate;
      });
    };
      
  });
  

}])
.controller('customGridDateFilterModalCtrl', ['$scope', '$rootScope', '$log', '$uibModalInstance', 'filterName', 'minDate', 'maxDate', function($scope, $rootScope, $log, $uibModalInstance, filterName, minDate, maxDate) {
  
    var ctrl = this;

    console.log('filter name', filterName);
    console.log('min date', minDate, 'max date', maxDate);
    
    ctrl.title = 'Select Dates ' + filterName + '...';
    ctrl.minDate = minDate;
    ctrl.maxDate = maxDate;
    ctrl.customDateFilterForm;
  
    ctrl.filterDate = (filterName.indexOf('From') !== -1) ? angular.copy(ctrl.minDate) : angular.copy(ctrl.maxDate);
    
    function setDateToStartOfDay(date) {
      return new Date(date.getFullYear(), date.getMonth(), date.getDate());
    }

    function setDateToEndOfDay(date) {
      return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59);
    }

    ctrl.filterDateChanged = function () {
      ctrl.filterDate = (filterName.indexOf('From') !== -1) ? setDateToStartOfDay(ctrl.filterDate) : setDateToEndOfDay(ctrl.filterDate);
      $log.log('new filter date', ctrl.filterDate);
    };
    
    ctrl.setFilterDate = function(date) {
      $uibModalInstance.close(date);
    };

    ctrl.cancelDateFilter = function() {
      $uibModalInstance.dismiss();
    };
  
}])

.directive('customGridDateFilterHeader', function() {
  return {
    template: '<button class="btn btn-default date-time-filter-buttons" style="width:90%;padding:inherit;" ng-click="openDatePicker(colFilter)">{{ colFilter.name }}</button><div role="button" class="ui-grid-filter-button-select cancel-custom-date-range-filter-button ng-scope" ng-click="removeFilter(colFilter, $index)" ng-if="!colFilter.disableCancelFilterButton" ng-disabled="colFilter.term === undefined || colFilter.term === null || colFilter.term === \'\'" ng-show="colFilter.term !== undefined &amp;&amp; colFilter.term != null" tabindex="0" aria-hidden="false" aria-disabled="false" style=""><i class="ui-grid-icon-cancel cancel-custom-date-range-filter" ui-grid-one-bind-aria-label="aria.removeFilter" aria-label="Remove Filter">&nbsp;</i></div>',
    controller: 'gridDatePickerFilterCtrl'
  };
})
;
<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script src="https://cdn.jsdelivr.net/lodash/4.6.1/lodash.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.2/ui-bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.2/ui-bootstrap-tpls.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.7/angular-material.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/3.1.1/ui-grid.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/3.1.1/ui-grid.min.css" type="text/css" />
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.7/angular-material.min.css" />
    <link data-require="bootstrap-css@*" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
    <script src="script.js"></script>
  </head>

  <body> 
    
    <div ng-controller="MainCtrl">
      <div id="grid1" ui-grid="gridOptions" class="grid"></div>
    </div>
    
    <script type="text/ng-template" id="custom-date-filter.html">
    
      <div class="col-md-12 col-md-offset-0 col-sm-12 col-sm-offset-0 col-xs-12 col-xs-offset-0">
  
        <div class="modal-header">
          <p class="modal-title well custom-date-filter-header">
            <span class="custom-date-filter-title-text">
              {{ custom.title }}
            </span>
          </p>
        </div>
  
        <div class="row modal-body custom-date-filter-container-row">
  
          <form name="custom.customDateFilterForm"
                ng-submit="custom.setFilterDate(custom.filterDate)"
                no-validation>

            <div class="row custom-filter-date-input-row">
            
              <div class="well col-md-8 col-md-offset-2 col-sm-8 col-sm-offset-2 col-xs-10 col-xs-offset-1 custom-date-filter-input">
              
                <uib-datepicker ng-model="custom.filterDate" 
                    min-date="custom.minDate" 
                    max-date="custom.maxDate"
                    ng-change="custom.filterDateChanged()"
                    class="well well-sm">
                </uib-datepicker>

              </div>

            </div>

            <div class="row modal-footer custom-date-filter-submit-buttons-row">

              <div class="custom-date-filter-submit-buttons-div col-lg-8 col-lg-offset-2 col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1 col-xs-10 col-xs-offset-1">

                <button class="btn btn-success btn-lg custom-date-filter-submit-button"
                        type="submit">
                  Apply
                </button>

                <button type="button"
                        class="btn btn-warning btn-lg custom-date-filter-cancel-button"
                        ng-click="custom.cancelDateFilter()">
                  Cancel
                </button>

              </div>

            </div>

          </form>
  
        </div>
  
      </div>
    
    </script>
  </body>

</html>
Cylindroid answered 29/4, 2016 at 20:4 Comment(1)
the above date range filter i tried to implement its working fine. thanks for your good work. currently i need only one input element in which from calendar date and to date calendar should be in a single pop.Dabble
F
2

I am using angular-ui-grid 3.1.1 and angular-ui-bootstrap 1.3.2.

My solution is based on ui-grid filter help and Munna S / Bennett Adams comments. I Obtained a solution with less code.

It works with two filters, and a Custom template ('ui-grid/custom-ui-grid-filter').

  { field: 'DATE_TIME', name: 'Date Time', cellTooltip: true,
    cellFilter: 'date:\'yyyy-MM-dd\'',
    cellTemplate: 'ui-grid/date-cell',
    filterHeaderTemplate: 'ui-grid/custom-ui-grid-filter',
    width: '40%',
    filters: [
        {
          condition: function(term, value, row, column){
                if (!term) return true;
                var valueDate = new Date(value);
                return valueDate >= term;
            },
          placeholder: 'Greater than or equal'
        },
        {
          condition: function(term, value, row, column){
                if (!term) return true;
                var valueDate = new Date(value);
                return valueDate <= term;
            },
          placeholder: 'Less than or equal'
        }
    ],
    headerCellClass: $scope.highlightFilteredHeader }

Remember that in the condition function, 'term' comes from the DatePicker filter and is a Date. Also, value is an string formatted as 'yyyy-MM-dd' in my example.

Here is my Plunkr Hope it helps and if you have any improvement please let me know.

Farl answered 16/8, 2016 at 18:1 Comment(3)
same filter i need but out side of the grid "custom one" not a inside gird cell filter. Please help me how to assign minDate and maxDate to filter from object?Staceestacey
Great, it works perfect. Also, no change for ui-grid div tag, just add some js.Hindenburg
This should be the top best solution so far.Hindenburg
M
1

i have done it using a custom javascript method

condition: function(term, value){
    if (!term) return true;
    var valueDate = new Date(value);
    var replaced = term.replace(/\\/g,'');
    var termDate = new Date(replaced);
    return valueDate < termDate;
},
placeholder: 'less than'
Moorer answered 21/6, 2016 at 18:16 Comment(1)
For a more browser agnostic solution I would switch to valueDate.getTime() < termDate.getTime(); based on this queston. #338963 You also might want a try, catch or a check for invalid dates.Nonsuch

© 2022 - 2024 — McMap. All rights reserved.