Fix: Date filtering works properly in Tokenizer (requires moment.js)

Feature: Selecting Start/Finish Date in Recipe list updates the search string.
This commit is contained in:
tanshu 2016-03-16 14:41:58 +05:30
parent 3c4f24989c
commit 3df2146ba2
6 changed files with 134 additions and 69 deletions

View File

@ -1,10 +1,10 @@
<h2>Products</h2>
<div layout-gt-sm="row">
<md-input-container flex-gt-sm>
<input type="text" ng-model="search"
<div layout="row">
<md-input-container flex="grow">
<input type="text" ng-model="search" md-autofocus
placeholder="Search: n:Name, a:Active, u:Units, t:Type and e:true for extended properties">
</md-input-container>
<md-input-container flex-gt-sm>
<md-input-container flex>
<md-button ng-href="/Product">Add <i
class="glyphicon glyphicon-plus"></i></md-button>
</md-input-container>

View File

@ -1,7 +1,7 @@
<form class="form-horizontal" role=form>
<h1>Recipe Detail</h1>
<div layout-gt-sm="row">
<div layout="row">
<md-autocomplete md-selected-item="recipe.Product" md-search-text="searchProd"
md-autofocus="recipe.RecipeItems.length === 0"
md-items="item in products(searchProd, {p: false})" md-item-text="item.Name" on-return="add()"
@ -30,11 +30,11 @@
value="{{costPrice | currency}} / {{costPrice * 100 / recipe.SalePrice | number:2}}%"/>
</md-input-container>
</div>
<div layout-gt-sm="row">
<div layout="row" class="md-inline-form">
<md-datepicker md-placeholder="Valid From" ng-model="recipe.ValidFrom"></md-datepicker>
<md-datepicker md-placeholder="Valid To" ng-model="recipe.ValidTo"></md-datepicker>
</div>
<div layout-gt-sm="row">
<div layout="row">
<md-autocomplete md-selected-item="product" md-search-text="searchIngredient" focus-on="foIngredient"
md-autofocus="recipe.RecipeItems.length > 0" md-items="item in products(searchIngredient, {})"
md-item-text="item.Name" on-return="add()"

View File

@ -1,35 +1,44 @@
<form class="form-horizontal" role=form>
<h2>Recipes
<div class="form-group col-md-9 pull-right">
<div class="col-md-10">
<input type="text" class="form-control" placeholder="Search" ng-model="search">
</div>
<div class="col-md-2">
<a href="/Recipe" class="btn btn-success btn-block">Add <i
class="glyphicon glyphicon-plus"></i></a>
</div>
</div>
</h2>
<table class="table table-condensed table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Valid From</th>
<th>Valid To</th >
<th class="text-right">Cost Price</th>
<th class="text-right">Sale Price</th>
<th class="text-right">Costing</th>
</tr>
</thead>
<tbody ng-repeat="items in info">
<tr ng-repeat="item in items.Prices">
<td ng-if="$first" rowspan="{{items.Prices.length}}">{{items.Name}}</td>
<td><a href="{{item.Url}}">{{item.ValidFrom}}</a></td>
<td>{{item.ValidTo}}</td>
<td class="text-right">{{item.CostPrice | currency}}</td>
<td class="text-right">{{item.SalePrice | currency}}</td>
<td class="text-right">{{item.Costing | percent}}</td>
</tr>
</tbody>
</table>
</form>
<h1>Recipes</h1>
<div layout="row">
<md-input-container flex="grow">
<input type="text" ng-model="search" md-autofocus placeholder="Search: n:Name, s:Start Date, f:Finish Date">
</md-input-container>
<md-input-container flex>
<md-button ng-href="/Recipe">Add <i
class="glyphicon glyphicon-plus"></i></md-button>
</md-input-container>
</div>
<div layout="row">
<div layout="column" flex>
<label class="md-caption">Start Date</label>
<md-datepicker md-placeholder="Start Date" ng-model="startDate"
ng-model-options="{ getterSetter: true }"></md-datepicker>
</div>
<div layout="column" flex>
<label class="md-caption">Finish Date</label>
<md-datepicker md-placeholder="Finish Date" ng-model="finishDate"
ng-model-options="{ getterSetter: true }"></md-datepicker>
</div>
</div>
<table class="table table-condensed table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Valid From</th>
<th>Valid To</th>
<th class="text-right">Cost Price</th>
<th class="text-right">Sale Price</th>
<th class="text-right">Costing</th>
</tr>
</thead>
<tbody ng-repeat="items in recipes">
<tr ng-repeat="item in items.Prices">
<td ng-if="$first" rowspan="{{items.Prices.length}}">{{items.Name}}</td>
<td><a href="{{item.Url}}">{{item.ValidFrom}}</a></td>
<td>{{item.ValidTo}}</td>
<td class="text-right">{{item.CostPrice | currency}}</td>
<td class="text-right">{{item.SalePrice | currency}}</td>
<td class="text-right">{{item.Costing | percent}}</td>
</tr>
</tbody>
</table>

View File

@ -270,33 +270,17 @@ overlordService.factory('Tokenizer', ['$filter', function ($filter) {
}
},
'date': function (obj, text) {
var reGoodDate = /^((0?[1-9]|[12][0-9]|3[01])[- /.](0?[1-9]|1[012]|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[- /.](19|20)?[0-9]{2})*$/i,
asDateObject = function (input) {
if (angular.isDate(input)) {
return input;
}
},
op = opLength(text),
ob = op ? asDateObject(obj) : '',
tex = op ? asDateObject(text.substr(op).trim()) : '';
if (op) {
return operators[text.substr(0, op)](ob, tex);
} else {
return obj.toString().indexOf(text) > -1;
}
if (text.indexOf('!') === 0) {
return obj.toLowerCase().indexOf(text.substr(1)) <= -1;
} else {
return obj.toLowerCase().indexOf(text) > -1;
}
var obDate = moment(obj, 'DD-MMM-YYYY'),
op = operatorLength(text),
operator = op ? text.substr(0, op) : '==',
textDate = op ? moment(text.substr(op).trim(), 'DD-MMM-YYYY') : moment(text, 'DD-MMM-YYYY');
return !obDate.isValid() || !textDate.isValid() || operators[operator](obDate.valueOf(), textDate.valueOf());
},
'boolean': function (obj, text) {
return obj === isTrue(text);
},
'numeric': function (obj, text) {
var op = opLength(text);
var op = operatorLength(text);
if (op) {
return operators[text.substr(0, op)](obj, Number(text.substr(op).trim()));
} else {
@ -323,7 +307,7 @@ overlordService.factory('Tokenizer', ['$filter', function ($filter) {
return {'Col': comparator.Col, 'Comparator': comparators[comparator.Comparator], 'Value': value};
}
function opLength(operator) {
function operatorLength(operator) {
var i,
ops = ['<=', '<', '>=', '>', '==', '!='];
for (i = 0; i < ops.length; i++) {

View File

@ -417,7 +417,7 @@ var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filte
}])
.config(['$mdDateLocaleProvider', function ($mdDateLocaleProvider) {
$mdDateLocaleProvider.formatDate = function (date) {
return moment(date).format('DD-MMM-YYYY');
return !angular.isUndefined(date) ? moment(date).format('DD-MMM-YYYY') : "";
};
$mdDateLocaleProvider.parseDate = function (date) {
return moment(date, 'DD-MMM-YYYY').toDate();

View File

@ -1,8 +1,80 @@
'use strict';
var RecipeListController = ['$scope', 'recipes', function ($scope, recipes) {
$scope.info = recipes;
}];
var RecipeListController = ['$scope', 'recipes', '$location', '$routeParams', 'Tokenizer', function ($scope, recipes, $location, $routeParams, Tokenizer) {
$scope.info = recipes;
$scope.search = $routeParams.q || '';
$scope.startDate = function (startDate) {
if (arguments.length === 0) {
return $scope._startDate;
}
$scope._startDate = startDate;
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = startDate === null ? '' : 's:>=' + moment(startDate).format('DD-MMM-YYYY');
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
};
$scope.finishDate = function (finishDate) {
if (arguments.length === 0) {
return $scope._finishDate;
}
$scope._finishDate = finishDate;
var re = /(('[f]'|"[f]"|[f]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
matches = $scope.search.match(re),
st = finishDate === null ? '' : 'f:<=' + moment(finishDate).format('DD-MMM-YYYY');
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
};
$scope.$watch('search', function (value) {
$scope.filterRecipes(value);
}, true);
$scope.$on('$destroy', function () {
Tokenizer.doFilter.cache = {};
});
$scope.searchInfo = {
comparator: {
'n': {'Col': 'Name', 'Comparator': 'text'},
's': {'Col': 'ValidFrom', 'Comparator': 'date'},
'f': {'Col': 'ValidTo', 'Comparator': 'date'}
},
def: 'n',
sorter: {
'n': 'Name',
's': 'ValidFrom',
'f': 'ValidTo',
'c': 'Costing',
'cp': 'CostPrice',
'sp': 'SalePrice'
},
flags: {}
};
$scope.filterRecipes = _.debounce(function (q) {
if (q !== $scope._search) {
$scope._search = q;
if (angular.isUndefined(q) || q === '') {
$location.path('/Recipes').search('q', null).replace();
} else {
$location.path('/Recipes').search({'q': q}).replace();
}
$scope.$apply(function () {
var matches = Tokenizer.parseFilterString(q, $scope.searchInfo);
$scope.recipes = _.map($scope.info, function (prod) {
var item = angular.copy(prod);
item.Prices = Tokenizer.doFilter(prod.ProductID + ' ' + q, prod.Prices, matches);
return item;
});
});
}
}, 350);
}]
;
RecipeListController.resolve = {
recipes: ['Recipe', function (Recipe) {