Ag-Grid Filter Comma - angularjs

im using ag-Grid, but there is a issue when it filters my data, when i filter my data in the price column, it only works with numbers dot and not with commas.
Link: https://plnkr.co/edit/LDdrRbANSalvb4Iwh5mp?p=preview
Practical Example:
In the Price column select box equal and above insert "1.5" and than try inserting "1,5"

This is because this filter is a native one.
If you want to handle custom behaviour, define your own filter.
Documentation : https://www.ag-grid.com/angular-grid-filtering/index.php
A quick and dirty solution would be to monkey patch the NumberFilter like this :
NumberFilter.prototype.doesFilterPass = function (node) {
if (this.filterNumber === null) {
return true;
}
var value = this.valueGetter(node);
if (!value && value !== 0) {
return false;
}
var valueAsNumber;
if (typeof value === 'number') {
valueAsNumber = value;
}
else {
valueAsNumber = parseFloat(value.replace(',','.'));
}
switch (this.filterType) {
case EQUALS:
return valueAsNumber === this.filterNumber;
case LESS_THAN:
return valueAsNumber < this.filterNumber;
case GREATER_THAN:
return valueAsNumber > this.filterNumber;
default:
// should never happen
console.warn('invalid filter type ' + this.filterType);
return false;
}
};
Then changed line is here :
valueAsNumber = parseFloat(value.replace(',','.'));

So i found the problem, first i had to convert the value has a string than i needed to replace the dot by the comma, the problem with the answer above was first because of the data type and than the order of the properties of the replace function, but the problem now is that is not filtering correctly, if i search using equal option if gives me 2 values, instead a fixed one, code looks something like this:
Code:
NumberFilter.prototype.doesFilterPass = function (node) {
if (this.filterNumber === null) {
return true;
}
var value = this.valueGetter(node);
if (!value && value !== 0) {
return false;
}
var valueAsNumber;
if (typeof value === 'number') {
value = value.toString()
valueAsNumber = parseFloat(value.replace('.',','));
}
else {
valueAsNumber = parseFloat(value.replace('.',','));
}
switch (this.filterType) {
case EQUALS:
return valueAsNumber === this.filterNumber;
case LESS_THAN:
return valueAsNumber < this.filterNumber;
case GREATER_THAN:
return valueAsNumber > this.filterNumber;
default:
// should never happen
console.warn('invalid filter type ' + this.filterType);
return false;
}
};

Possible Solution:
NumberFilter.prototype.onFilterChanged = function () {
var filterText = utils_1.default.makeNull(this.eFilterTextField.value);
if (filterText && filterText.trim() === '') {
filterText = null;
}
var newFilter;
if (filterText !== null && filterText !== undefined) {
console.log(filterText);
// replace comma by dot
newFilter = parseFloat(filterText.replace(/,/g, '.'));
console.log(newFilter);
}
else {
newFilter = null;
}
if (this.filterNumber !== newFilter) {
this.filterNumber = newFilter;
this.filterChanged();
}
};

Related

AngularJS: How to ignore null values while filtering?

I'm trying to filter instructor_name but this table has many null values, how should I ignore those null fields and search/filter only from fileds which are not null
filter:
`item.instructor_name.toLowerCase().indexOf($scope.courses_search.toLowerCase()) != -1`
and because, there are many fields which are null Im getting an error
TypeError: Cannot read property 'instructor_name' of null
$scope.livesearch = function(item) {
if ($scope.courses_search == undefined) {
return true;
} else {
if (item.subject_cd.toLowerCase().indexOf($scope.courses_search.toLowerCase()) != -1 ||
item.acad_group.toLowerCase().indexOf($scope.courses_search.toLowerCase()) != -1) {
return true;
}
}
}
$scope.filterFunc = function() {
return function(item) {
if (item !== null && typeof item.class_instructor_name !== 'undefined') {
return item.class_instructor_name.toLowerCase().indexOf($scope.courses_search.toLowerCase()) != -1;
}
return false;
};
};
You are comparing item to null, but if item is undefined, the next validation will take place because (undefined !== null), so it will try to look up instructor_name on undefined, which obviously will not work.
AngularJS has it's own checkers, angular.isDefined() and angular.isUndefined(). You can use one of those, or do a slightly different check, such as:
if (item && item.instructor_name) ...
If you're using lodash, you can simply do:
if (_.get(item, 'instructor_name')) ...
One option is to add the filter on the controller and handle null checks from there.
Remeber to check if the value is defined before appling the toLowerCase() method.
$scope.filterFunc = function() {
return function(item) {
if (typeof $scope.courses_search !== 'undefined' && item !== null) {
if(typeof item.subject_cd !== 'undefined'){
return item.subject_cd.toLowerCase().indexOf($scope.courses_search.toLowerCase()) != -1;
}
if(typeof item.acad_group !== 'undefined'){
return item.acad_group.toLowerCase().indexOf($scope.courses_search.toLowerCase()) != -1;
}
}
return false;
};
};
Then in view
ng-repeat="item in items | filter:filterFunc"

Issue using filter AngularJS

I'm using $filter to iterate through an array and fetch a specific value
Below is my code:
var selected = $filter('filter')($scope.folders, {url: el.selected[0] });
This code is working, but I got a problem when the url contain an accent and space like so :
/Users/Me/project/products/Poste à souder
In that case the string comparaison isn't working anymore.
What is the cleaner way to solve this situation ?
That true. As a francophone, I've often encounter encoding/decoding issues with angularjs.
The source code of the default filter is as follow
function filterFilter()
{
return function(array, expression, comparator)
{
if (!isArrayLike(array))
{
if (array == null)
{
return array;
}
else
{
throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
}
}
var expressionType = getTypeForFilter(expression);
var predicateFn;
var matchAgainstAnyProp;
switch (expressionType)
{
case 'function':
predicateFn = expression;
break;
case 'boolean':
case 'null':
case 'number':
case 'string':
matchAgainstAnyProp = true;
//jshint -W086
case 'object':
//jshint +W086
predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
break;
default:
return array;
}
return Array.prototype.filter.call(array, predicateFn);
};
}
and the predicate generator stand as follow: it generate the default comparator if the provided one is not a function
function createPredicateFn(expression, comparator, matchAgainstAnyProp)
{
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
var predicateFn;
if (comparator === true)
{
comparator = equals;
}
else if (!isFunction(comparator))
{
comparator = function(actual, expected)
{
if (isUndefined(actual))
{
// No substring matching against `undefined`
return false;
}
if ((actual === null) || (expected === null))
{
// No substring matching against `null`; only match against `null`
return actual === expected;
}
if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual)))
{
// Should not compare primitives against objects, unless they have custom `toString` method
return false;
}
actual = lowercase('' + actual);
expected = lowercase('' + expected);
return actual.indexOf(expected) !== -1;
};
}
predicateFn = function(item)
{
if (shouldMatchPrimitives && !isObject(item))
{
return deepCompare(item, expression.$, comparator, false);
}
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
};
return predicateFn;
}
Too much speech. You have the choice:
Provide a comparator to your filter see the doc
but remember that you can't define inline function in angular template
you can define a function in that scope, but it will only be available in that scope
You can write your own filter
.filter('myCustomFilter', function()
{
return function(input, criteria)
{
... // your logic here
return ...// the filtered values
};
})
Maybe it's best to write your own filter:
app.filter("customFilter", function () {
//the filter will accept an input array, the key you want to look for and the value that the key should have
return function (array, key, value) {
return array.filter(function(x){
return (x.hasOwnProperty(key) && (x[key] === value));
});
};
});
And use it in your controller like:
$scope.filtered = $filter("customFilter")($scope.folders, "url", "/Users/Me/project/products/Poste à souder");
Check out a working demo here.

How to add thousand separating commas for numbers in angularJS?

I simply want to convert a string of numbers to a number which will be displayed using thousand separated commas.
var value = "123456";
I want to display "123,465" in a grid.
I have looked some documentation on this but everything is about displaying it in HTML.
I want to display this in a dynamic grid.
function numberRenderer (params) {
return new Number (params.value);
}
I want to format the number so that I can convert that into a string for display.
Use a filter ...
HTML usage
{{ number_expression | number : fractionSize}}
Js usage
$filter('number')(number, fractionSize)
I appreciated the answer from #jbrown, but I was also hoping to find some type of solution to add commas to an input field as the user enters numbers. I ended up finding this directive which proved to be exactly what I needed.
HTML
<input type="text" ng-model="someNumber" number-input />
JAVASCRIPT
myApp.directive('numberInput', function($filter) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
ngModelCtrl.$formatters.push(function(modelValue) {
return setDisplayNumber(modelValue, true);
});
// it's best to change the displayed text using elem.val() rather than
// ngModelCtrl.$setViewValue because the latter will re-trigger the parser
// and not necessarily in the correct order with the changed value last.
// see http://radify.io/blog/understanding-ngmodelcontroller-by-example-part-1/
// for an explanation of how ngModelCtrl works.
ngModelCtrl.$parsers.push(function(viewValue) {
setDisplayNumber(viewValue);
return setModelNumber(viewValue);
});
// occasionally the parser chain doesn't run (when the user repeatedly
// types the same non-numeric character)
// for these cases, clean up again half a second later using "keyup"
// (the parser runs much sooner than keyup, so it's better UX to also do it within parser
// to give the feeling that the comma is added as they type)
elem.bind('keyup focus', function() {
setDisplayNumber(elem.val());
});
function setDisplayNumber(val, formatter) {
var valStr, displayValue;
if (typeof val === 'undefined') {
return 0;
}
valStr = val.toString();
displayValue = valStr.replace(/,/g, '').replace(/[A-Za-z]/g, '');
displayValue = parseFloat(displayValue);
displayValue = (!isNaN(displayValue)) ? displayValue.toString() : '';
// handle leading character -/0
if (valStr.length === 1 && valStr[0] === '-') {
displayValue = valStr[0];
} else if (valStr.length === 1 && valStr[0] === '0') {
displayValue = '';
} else {
displayValue = $filter('number')(displayValue);
}
// handle decimal
if (!attrs.integer) {
if (displayValue.indexOf('.') === -1) {
if (valStr.slice(-1) === '.') {
displayValue += '.';
} else if (valStr.slice(-2) === '.0') {
displayValue += '.0';
} else if (valStr.slice(-3) === '.00') {
displayValue += '.00';
}
} // handle last character 0 after decimal and another number
else {
if (valStr.slice(-1) === '0') {
displayValue += '0';
}
}
}
if (attrs.positive && displayValue[0] === '-') {
displayValue = displayValue.substring(1);
}
if (typeof formatter !== 'undefined') {
return (displayValue === '') ? 0 : displayValue;
} else {
elem.val((displayValue === '0') ? '' : displayValue);
}
}
function setModelNumber(val) {
var modelNum = val.toString().replace(/,/g, '').replace(/[A-Za-z]/g, '');
modelNum = parseFloat(modelNum);
modelNum = (!isNaN(modelNum)) ? modelNum : 0;
if (modelNum.toString().indexOf('.') !== -1) {
modelNum = Math.round((modelNum + 0.00001) * 100) / 100;
}
if (attrs.positive) {
modelNum = Math.abs(modelNum);
}
return modelNum;
}
}
};
});
AngularJS Directive was found from: AngularJS number input formatted view
https://jsfiddle.net/benlk/4dto9738/
Very appreciative of what Anguna posted. The only thing it was missing for me was handling the decimal places like currency. I wanted it to automatically add 2 decimal places to the displayed value. However, this should only occur on initial display and then again when leaving a field. I updated the code to handle that scenario.
var app = angular.module("myApp", []);
app.directive('currencyInput', function ($filter) {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ngModelCtrl) {
ngModelCtrl.$formatters.push(function (modelValue) {
var displayValue = setDisplayNumber(modelValue, true);
displayValue = setDecimal(displayValue);
return displayValue;
});
// it's best to change the displayed text using elem.val() rather than
// ngModelCtrl.$setViewValue because the latter will re-trigger the parser
// and not necessarily in the correct order with the changed value last.
// see http://radify.io/blog/understanding-ngmodelcontroller-by-example-part-1/
// for an explanation of how ngModelCtrl works.
ngModelCtrl.$parsers.push(function (viewValue) {
setDisplayNumber(viewValue);
return setModelNumber(viewValue);
});
// occasionally the parser chain doesn't run (when the user repeatedly
// types the same non-numeric character)
// for these cases, clean up again half a second later using "keyup"
// (the parser runs much sooner than keyup, so it's better UX to also do it within parser
// to give the feeling that the comma is added as they type)
elem.bind('keyup focus', function () {
setDisplayNumber(elem.val());
});
elem.bind('blur', function () {
// Add Decimal places if they do not exist
var valStr = elem.val().toString();
valStr = setDecimal(valStr);
elem.val(valStr);
});
function setDisplayNumber(val, formatter) {
var valStr, displayValue;
if (typeof val === 'undefined') {
return 0;
}
valStr = val.toString();
displayValue = valStr.replace(/,/g, '').replace(/[A-Za-z]/g, '');
displayValue = parseFloat(displayValue);
displayValue = (!isNaN(displayValue)) ? displayValue.toString() : '';
// handle leading character -/0
if (valStr.length === 1 && valStr[0] === '-') {
displayValue = valStr[0];
} else if (valStr.length === 1 && valStr[0] === '0') {
displayValue = '';
} else {
displayValue = $filter('number')(displayValue);
}
// handle decimal
if (!attrs.integer) {
if (displayValue.indexOf('.') === -1) {
if (valStr.slice(-1) === '.') {
displayValue += '.';
} else if (valStr.slice(-2) === '.0') {
displayValue += '.0';
} else if (valStr.slice(-3) === '.00') {
displayValue += '.00';
}
} // handle last character 0 after decimal and another number
else {
if (valStr.slice(-1) === '0') {
displayValue += '0';
}
}
}
if (attrs.positive && displayValue[0] === '-') {
displayValue = displayValue.substring(1);
}
if (typeof formatter !== 'undefined') {
return (displayValue === '') ? 0 : displayValue;
} else {
elem.val((displayValue === '0') ? '' : displayValue);
}
}
function setModelNumber(val) {
var modelNum = val.toString().replace(/,/g, '').replace(/[A-Za-z]/g, '');
modelNum = parseFloat(modelNum);
modelNum = (!isNaN(modelNum)) ? modelNum : 0;
if (modelNum.toString().indexOf('.') !== -1) {
modelNum = Math.round((modelNum + 0.00001) * 100) / 100;
}
if (attrs.positive) {
modelNum = Math.abs(modelNum);
}
return modelNum;
}
function setDecimal(val) {
// Add Decimal places if they do not exist
var valStr = val.toString();
// If no decimal then add it
if (valStr.indexOf('.') === -1) {
valStr += '.00';
}
else {
var decimalDigits = valStr.length - (valStr.indexOf('.') + 1);
var missingZeros = 2 - decimalDigits;
for (var i = 1; i <= missingZeros; i++) {
valStr += '0';
}
}
return valStr;
}
}
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js"></script>
<div ng-app="myApp">
<input type="text" ng-model="myModelValue" currency-input />
</div>

How to check if expression will have a value after evaluating

Let's say I have a following template:
"foo['x'] = '{{ myVar }}';"
Is there an angular way of checking if evaluating this against my current scope will give myVar some value ? I've got an array of such small templates and I only want to include them in the document when values are truthy. I was hoping either $interpolate, $parse or $eval might come in handy here. I know for sure that $interpolate is useless. What about the other two ? Maybe it's at least possible to get the name of the assigned value/expression ?
EDIT
I wasn't specific enough. What I was trying to achieve, was checking in advance if for example template '{{ myVar }}' evaluated against the current scope will return an empty string or value of the scope variable (if it exists). The case was really specific - when traversing an array of short templates I wanted to know if a template will return as an empty string or not, and only include it in my final html if it doesn't.
I'm not sure what are you trying to achieve, but to if you want to check if myVar is truthy in current scope, you can:
{{myVar ? "aw yiss" : "nope"}}
Evaluates to "aw yiss" if myVar is truthy and "nope" otherwise.
I ended up with a modified $interpolate provider but maybe someone knows a shorter solution :
app.provider('customInterpolateProvider', [
function $InterpolateProvider() {
var startSymbol = '{{';
var endSymbol = '}}';
this.startSymbol = function(value){
if (value) {
startSymbol = value;
return this;
} else {
return startSymbol;
}
};
this.endSymbol = function(value){
if (value) {
endSymbol = value;
return this;
} else {
return endSymbol;
}
};
this.$get = ['$parse', '$sce', function($parse, $sce) {
var startSymbolLength = startSymbol.length,
endSymbolLength = endSymbol.length;
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
index = 0,
expressions = [],
parseFns = [],
textLength = text.length,
exp;
var getValue = function (value) {
return trustedContext ?
$sce.getTrusted(trustedContext, value) :
$sce.valueOf(value);
};
var stringify = function (value) {
if (value == null) {
return '';
}
switch (typeof value) {
case 'string':
break;
case 'number':
value = '' + value;
break;
default:
value = angular.toJson(value);
}
return value;
};
var parseStringifyInterceptor = function(value) {
try {
return stringify(getValue(value));
} catch(err) {
console.err(err.toString());
}
};
while(index < textLength) {
if ( ((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1) ) {
exp = text.substring(startIndex + startSymbolLength, endIndex);
expressions.push(exp);
parseFns.push($parse(exp, parseStringifyInterceptor));
index = endIndex + endSymbolLength;
} else {
break;
}
}
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (!mustHaveExpression) {
var compute = function(values) {
for(var i = 0, ii = expressions.length; i < ii; i++) {
if (allOrNothing && angular.isUndefined(values[i])) {
return;
}
expressions[i] = values[i];
}
return expressions.join('');
};
return angular.extend(function interpolationFn(context) {
var i = 0;
var ii = expressions.length;
var values = new Array(ii);
try {
if (ii && !parseFns.length) {
return expressions[0];
} else {
for (; i < ii; i++) {
values[i] = parseFns[i](context);
}
return compute(values);
}
} catch(err) {
console.err(err.toString());
}
}, {
exp: text,
expressions: expressions,
$$watchDelegate: function (scope, listener, objectEquality) {
var lastValue;
return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
var currValue = compute(values);
if (angular.isFunction(listener)) {
listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
}
lastValue = currValue;
}, objectEquality);
}
});
}
}
return $interpolate;
}];
}
]);
Lines below were added because in some cases I have a predefined text in my short template and I always want to render it :
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (ii && !parseFns.length) {
return expressions[0];
} else {

How can I control the format of a Ext.form.NumberField value when a form submits?

I've extended Ext.form.Numberfield to show thousands separators and always show two decimal places:
Ext.override(Ext.form.NumberField, {
baseChars: "0123456789,",
setValue: function(v){
v = typeof v == 'number' ? v : String(v).replace(this.decimalSeparator, ".").replace(/,/g, "");
//v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
v = isNaN(v) ? '' : Ext.util.Format.number(this.fixPrecision(String(v)), "0,000,000.00");
this.setRawValue(v);
return Ext.form.NumberField.superclass.setValue.call(this, v);
},
fixPrecision: function(value){
var nan = isNaN(value);
if (!this.allowDecimals || this.decimalPrecision == -1 || nan || !value) {
return nan ? '' : value;
}
return parseFloat(value).toFixed(this.decimalPrecision);
},
validateValue: function(value){
if (!Ext.form.NumberField.superclass.validateValue.call(this, value)) {
return false;
}
if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
return true;
}
value = String(value).replace(this.decimalSeparator, ".").replace(/,/g, "");
if (isNaN(value)) {
this.markInvalid(String.format(this.nanText, value));
return false;
}
var num = this.parseValue(value);
if (num < this.minValue) {
this.markInvalid(String.format(this.minText, this.minValue));
return false;
}
if (num > this.maxValue) {
this.markInvalid(String.format(this.maxText, this.maxValue));
return false;
}
return true;
},
parseValue: function(value){
value = parseFloat(String(value).replace(this.decimalSeparator, ".").replace(/,/g, ""));
return isNaN(value) ? '' : value;
}
});
The problem is that on form submit, the value sent in the POST includes the commas, forcing me to parse as a string on the server side. Is there a way to send the raw number value instead of this special comma-formatted value?
Instead of sending these parameters:
referenceSales 10,000,000.00
salesGoal 11,000,000.00
I want to send these:
referenceSales 10000000.00
salesGoal 11000000.00
Of course you realize NumberField extends TextField, so there is no raw value. (wysiwyg) I suggest using a regular expression on submission.

Resources