<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap-css@3.2.0" data-semver="3.2.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js" data-semver="1.2.17" data-require="angular.js@1.2.17"></script>
<link rel="stylesheet" href="style.css" />
<script src="angular-fcsa-number.js"></script>
<script src="main-ctrl.js"></script>
</head>
<body ng-app="app" ng-controller="MainCtrl as ctrl">
<h2>FCSA Number Demo</h2>
<p><a href="https://github.com/FCSAmerica/angular-fcsa-number">https://github.com/FCSAmerica/angular-fcsa-number</a></p>
<p>Here is a quick demo of the FCSA Number Angular directive that validates and formats numbers in text boxes. The text box will turn red when an invalid number is entered.</p>
<form name="form">
<div class="form-group" ng-class="{ 'has-error': form.numberNoOptions.$invalid }">
<label>Default Functionality with No Options</label>
<input type="text" fcsa-number="{ }" ng-model="ctrl.model.numberNoOptions" name="numberNoOptions" class="form-control" />
<p class="help-block">
Notice how the comma is removed when you enter the text box, and then it's re-added when you leave the textbox.
</p>
</div>
<div class="form-group" ng-class="{ 'has-error': form.numberMax.$invalid }">
<label>fcsa-number="{ max: 10 }"</label>
<input type="text" fcsa-number="{ max: 10 }" ng-model="ctrl.model.numberMax" name="numberMax" class="form-control" />
<p class="help-block">
Any number above 10 will be marked invalid
</p>
</div>
<div class="form-group" ng-class="{ 'has-error': form.numberMin.$invalid }">
<label>fcsa-number="{ min: 0 }"</label>
<input type="text" fcsa-number="{ min: 0 }" ng-model="ctrl.model.numberMin" name="numberMin" class="form-control" />
<p class="help-block">
Any number below 0 will be marked invalid
</p>
</div>
<div class="form-group" ng-class="{ 'has-error': form.numberMaxDecimals.$invalid }">
<label>fcsa-number="{ maxDecimals: 2 }"</label>
<input type="text" fcsa-number="{ maxDecimals: 2 }" ng-model="ctrl.model.numberMaxDecimals" name="numberMaxDecimals" class="form-control" />
<p class="help-block">
Any number with more than 2 decimals will be marked invalid
</p>
</div>
<div class="form-group" ng-class="{ 'has-error': form.numberMaxDigits.$invalid }">
<label>fcsa-number="{ maxDigits: 3 }"</label>
<input type="text" fcsa-number="{ maxDigits: 3 }" ng-model="ctrl.model.numberMaxDigits" name="numberMaxDigits" class="form-control" />
<p class="help-block">
Any number with more than 3 digits will be marked invalid
</p>
</div>
<div class="form-group" ng-class="{ 'has-error': form.numberPrepend.$invalid }">
<label>fcsa-number="{ prepend: '$' }"</label>
<input type="text" fcsa-number="{ prepend: '$' }" ng-model="ctrl.model.numberPrepend" name="numberPrepend" class="form-control" />
<p class="help-block">
The '$' will be prepended to the number in the text box
</p>
</div>
<div class="form-group" ng-class="{ 'has-error': form.numberAppend.$invalid }">
<label>fcsa-number="{ append: '%' }"</label>
<input type="text" fcsa-number="{ append: '%' }" ng-model="ctrl.model.numberAppend" name="numberPrepend" class="form-control" />
<p class="help-block">
The '%' will be appended to the number in the text box
</p>
</div>
<div class="form-group" ng-class="{ 'has-error': form.numberMultipleOptions.$invalid }">
<label>fcsa-number="{ max: 10, min: 0, maxDecimals: 2 }"</label>
<input type="text" fcsa-number="{ max: 10, min: 0, maxDecimals: 2 }" ng-model="ctrl.model.numberMultipleOptions" name="numberMultipleOptions" class="form-control" />
<p class="help-block">
It's possible to set multiple options
</p>
</div>
</form>
</body>
</html>
// Code goes here
/* Styles go here */
body {
padding: 20px;
}
(function() {
angular.module('fcsa-number', []).directive('fcsaNumber', function() {
var addCommasToInteger, controlKeys, hasMultipleDecimals, isNotControlKey, isNotDigit, isNumber, makeIsValid, makeMaxDecimals, makeMaxDigits, makeMaxNumber, makeMinNumber;
isNumber = function(val) {
return !isNaN(parseFloat(val)) && isFinite(val);
};
isNotDigit = function(which) {
return which < 45 || which > 57 || which === 47;
};
controlKeys = [0, 8, 13];
isNotControlKey = function(which) {
return controlKeys.indexOf(which) === -1;
};
hasMultipleDecimals = function(val) {
return (val != null) && val.toString().split('.').length > 2;
};
makeMaxDecimals = function(maxDecimals) {
var regexString, validRegex;
if (maxDecimals > 0) {
regexString = "^-?\\d*\\.?\\d{0," + maxDecimals + "}$";
} else {
regexString = "^-?\\d*$";
}
validRegex = new RegExp(regexString);
return function(val) {
return validRegex.test(val);
};
};
makeMaxNumber = function(maxNumber) {
return function(val, number) {
return number <= maxNumber;
};
};
makeMinNumber = function(minNumber) {
return function(val, number) {
return number >= minNumber;
};
};
makeMaxDigits = function(maxDigits) {
var validRegex;
validRegex = new RegExp("^-?\\d{0," + maxDigits + "}(\\.\\d*)?$");
return function(val) {
return validRegex.test(val);
};
};
makeIsValid = function(options) {
var validations;
validations = [];
if (options.maxDecimals != null) {
validations.push(makeMaxDecimals(options.maxDecimals));
}
if (options.max != null) {
validations.push(makeMaxNumber(options.max));
}
if (options.min != null) {
validations.push(makeMinNumber(options.min));
}
if (options.maxDigits != null) {
validations.push(makeMaxDigits(options.maxDigits));
}
return function(val) {
var i, number, _i, _ref;
if (!isNumber(val)) {
return false;
}
if (hasMultipleDecimals(val)) {
return false;
}
number = Number(val);
for (i = _i = 0, _ref = validations.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
if (!validations[i](val, number)) {
return false;
}
}
return true;
};
};
addCommasToInteger = function(val) {
var commas, decimals, wholeNumbers;
decimals = val.indexOf('.') == -1 ? '' : val.replace(/^\d+(?=\.)/, '');
wholeNumbers = val.replace(/(\.\d+)$/, '');
commas = wholeNumbers.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
return "" + commas + decimals;
};
return {
restrict: 'A',
require: 'ngModel',
scope: {
options: '@fcsaNumber'
},
link: function(scope, elem, attrs, ngModelCtrl) {
var isValid, options;
options = {};
if (scope.options != null) {
options = scope.$eval(scope.options);
}
isValid = makeIsValid(options);
ngModelCtrl.$parsers.unshift(function(viewVal) {
var noCommasVal;
noCommasVal = viewVal.replace(/,/g, '');
if (isValid(noCommasVal) || !noCommasVal) {
ngModelCtrl.$setValidity('fcsaNumber', true);
return noCommasVal;
} else {
ngModelCtrl.$setValidity('fcsaNumber', false);
return void 0;
}
});
ngModelCtrl.$formatters.push(function(val) {
if ((options.nullDisplay != null) && (!val || val === '')) {
return options.nullDisplay;
}
if ((val == null) || !isValid(val)) {
return val;
}
ngModelCtrl.$setValidity('fcsaNumber', true);
val = addCommasToInteger(val.toString());
if (options.prepend != null) {
val = "" + options.prepend + val;
}
if (options.append != null) {
val = "" + val + options.append;
}
return val;
});
elem.on('blur', function() {
var formatter, viewValue, _i, _len, _ref;
viewValue = ngModelCtrl.$modelValue;
if ((viewValue == null) || !isValid(viewValue)) {
return;
}
_ref = ngModelCtrl.$formatters;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
formatter = _ref[_i];
viewValue = formatter(viewValue);
}
ngModelCtrl.$viewValue = viewValue;
return ngModelCtrl.$render();
});
elem.on('focus', function() {
var val;
val = elem.val();
if (options.prepend != null) {
val = val.replace(options.prepend, '');
}
if (options.append != null) {
val = val.replace(options.append, '');
}
elem.val(val.replace(/,/g, ''));
return elem[0].select();
});
if (options.preventInvalidInput === true) {
return elem.on('keypress', function(e) {
if (isNotDigit(e.which && isNotControlKey(e.which))) {
return e.preventDefault();
}
});
}
}
};
});
}).call(this);
angular.module('app', ['fcsa-number']).controller('MainCtrl', function() {
this.model = {
numberNoOptions: 1000,
numberMax: 9,
numberMin: 1,
numberMaxDecimals: 9.87,
numberMaxDigits: 393,
numberPrepend: 5.97,
numberAppend: 100,
numberMultipleOptions: 1.25
};
});