Using a custom currency field on numerical input in AngularJS - angularjs

I am using the currency filter shown in the "best answer" in this thread: Apply currency filter to the input field in angularjs
The problem is, I also need the input field to be numerical only, i.e. type="number". It works perfectly with type="text", but when I try to combine the two, the input disappears when the textbox loses focus.
<div class="table-cell">
<div class="field-label table-cell">Estimated Yearly<br />Savings</div>
<input ng-model="model.yearlySavings" type="number" format="currency">
</div>
JS:
mainModule.directive('format', ['$filter', function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function (a) {
return $filter(attrs.format)(ctrl.$modelValue)
});
elem.bind('blur', function (event) {
var plainNumber = elem.val().replace(/[^\d|\-+|\.+]/g, '');
elem.val($filter(attrs.format)(plainNumber));
});
}
};
}]);
Note: I would have commented in that thread, but I don't have enough "rep" to leave a comment...

I ended up using a custom formatter from another SO thread: AngularJS - How to apply currency filter on textbox as I type?
The directive is called fcsa-number: https://github.com/FCSAmericaDev/angular-fcsa-number
It took some finesse, but I got it working like this:
<input ng-model="model.yearlySavings" name="ys" type="text"
ng-class="{ 'error': formName.ys.$invalid }" fcsa-number="{ prepend: '$', preventInvalidInput: true, maxDecimals: 2 }">
The "ng-class" bit is used to dynamically style the border to red when the element listed (in this case "yearlySavings" aka "ys") is invalid. In this example, it prepends my input with "$", it prevents invalid input from being entered at all (like regular text characters), and it sets the maximum decimals to 2. Oddly enough, it does not prevent 3+ decimals from being entered like I would expect (like the regular text characters), but it does invalidate the element with more than 2.

Create directive as below
HTML
<div ng-app="myApp">
<div ng-controller="myCtrl">
{{amount}}
<input format-to-currency amount="amount">
</div>
</div>
JS
angular.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.ampunt = 2;
})
.directive('formatToCurrency', function($filter) {
return {
scope: {
amount: '='
},
link: function(scope, el, attrs) {
el.val($filter('currency')(scope.amount));
el.bind('focus', function() {
el.val(scope.amount);
});
el.bind('input', function() {
scope.amount = el.val();
scope.$apply();
});
el.bind('blur', function() {
el.val($filter('currency')(scope.amount));
});
}
}
});
link http://jsfiddle.net/moL8ztrw/7/

Related

how to restrict <inpu type="text"> typed value not more than 10 using directive

I want to restrict input typed length should not be more than 10 using directive in angularjs?
i do not know how to get typed value into directive?
app.directive('findLength',function(){
return{
restrict : 'EA',
require:'ngModel',
link:function(scope,ele,attr,ctrl){
console.log(ctrl.ngModel);
}
}
});
You are looking for this. This should be useful to you
HTML
<div ng-app='app' ng-controller='mainCtrl'>
<input type='text' ng-model='test' allowed-length='10' check-length/>
<span style='color:red;' ng-if='test.length === 10'>* Maximum 10 characters allowed</span>
</div>
Notice that i have used the allowed-length attribute so that the directive checkLength can be reusable for all length values. This is why we use directive as they can be reused again and again. So the best way is to provide the limit of length to the directive rather than coding the length inside the directive.
The directive is
.directive('checkLength',function(){
return {
restrict: 'A',
scope:{
ngModel: '=',
allowedLength :'='
},
link: function (scope, element) {
element.on('keypress', function(event) {
if(scope.ngModel.length === scope.allowedLength){
event.preventDefault();
}else{
scope.ngModel = element.val();
}
});
}
}
})
This just works as expected. For your simplicity here is the attached link to JSFIDDLE
You can create a template to this directive using ng-maxlength.
Check this out: Example plnkr
You can "max-length" as attribute in your input instead of Ng-maxlength.
try this one
var app = angular.module('myApp', []);
app.directive('myDirective', function() {
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
angular.element(element).on("keypress", function(e) {
if (this.value.length > 10) {
mCtrl.$setValidity('charE', false);
e.preventDefault();
} else {
mCtrl.$setValidity('charE', true);
}
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<p>Try writing in the input field:</p>
<form name="myForm">
<input name="myInput" ng-model="myInput" required my-directive>
</form>
You can use ng-maxlength="10" angular pre defined Directive.
OR You can use Following Directive :-
app.directive('findLength',function(){
return{
restrict : 'EA',
require:'ngModel',
link:function(scope,ele,attr,ctrl){
angular.element(elem).on("keypress", function(e) {
if(this.value>10)
return false;
else
return true
}
}
}

angular js, how to restrict input values to only a few numbers

I'm building an app in angular js.
I want to create a restriction an input field to accept only 3, 6, 9, 12, 15 and so on.
It should mark it as no valid if the user writes for example an 8.
I couldn't find how to do this, help would be appreciated.
You can create a directive and add your own validator that checks for multiples of 3.
See plunker
Markup:
<input name="numberField" type="number" data-ng-model="model.number" data-multiple-validator="3"/>
JS:
app.directive('multipleValidator', [function(){
return {
restrict: 'AC',
require: 'ngModel',
link: function(scope, elem, attr, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
var multiple = parseInt(attr.multipleValidator);
if (!multiple) {
return;
}
ngModelCtrl.$validators.multipleValidator = function(modelValue, viewValue) {
return !!modelValue && modelValue%multiple === 0;
}
}
}
}])
If it's only the numbers 3, 6, 9, 12 and 15 then you can just use ng-pattern like so
<input name="numberField" type="number" data-ng-model="model.number" data-ng-pattern="/^(3|6|9|12|15)$/"/>
EDIT
If your multiple value is dynamic then you will have to watch for changes in your directive using the very handy $observe and re-validate when it changes. See updated plunker.
Markup:
<input placeholder="input" name="numberField" type="number" data-ng-model="model.number" data-multiple-validator="{{model.multiple}}"/>
JS:
app.directive('multipleValidator', [function(){
return {
restrict: 'AC',
require: 'ngModel',
link: function(scope, elem, attr, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
ngModelCtrl.$validators.multipleValidator = function(modelValue, viewValue) {
var multiple = parseInt(attr.multipleValidator);
if (!multiple) {
// If there is no multiple then assume that it is valid
return true;
}
return !!modelValue && modelValue%multiple === 0;
}
// Watch for changes to the multipleValidator attribute
attr.$observe('multipleValidator', function(newVal, oldVal) {
// Re-validate the input when a change is detected
ngModelCtrl.$validate();
});
}
}
}]);
There is no need to use a custom directive. Angular has built in directive called ng-pattern which lets you match the input field to your given regular expression.
Here is how i have achieved this..
CODE...
<html ng-app="num">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body ng-controller="numCtrl">
<form class="digits" name="digits">
<input type="text" placeholder="digits here plz" name="nums" ng-model="nums" required ng-pattern="/^([0369]|[258][0369]*[147]|[147]([0369]|[147][0369]*[258])*[258]|[258][0369]*[258]([0369]|[147][0369]*[258])*[258]|[147]([0369]|[147][0369]*[258])*[147][0369]*[147]|[258][0369]*[258]([0369]|[147][0369]*[258])*[147][0369]*[147])*$/" />
<p class="alert" ng-show="digits.nums.$error.pattern">Multiples of three only accepted.</p>
<br>
</form>
<script>
var app = angular.module('num',[]);
app.controller('numCtrl', function($scope, $http){
$scope.digits = {};
});
</script>
</body>
</html>
See this jsfiddle for a working example https://jsfiddle.net/s7mmvxq5/5/
The approach I took was to create a directive that just adds an additional $validator to the form field. Since the validator is just a function you can do whatever check you'd like in it.
This section of the angular docs might be of interest https://docs.angularjs.org/guide/forms#custom-validation

Angular filter not working

I am coding a filter that will format phone numbers in a contact form I've built however for some reason the value in the input is never being updated and I'm not sure what I'm doing wrong.
Here's my HTML:
<div class='form-group'>
<input name='phone' ng-model='home.contact.phone' placeholder='(Area code) Phone' required ng-bind='home.contact.phone | phone' />
</div>
and here's my filter:
(function () {
'use strict'
angular
.module('Allay.phoneFilter', [])
.filter('phone', function () {
return function (phone) {
if(!phone) return '';
var res = phone + '::' // Since this isn't working, I'm doing something super simple, adding a double colon to the end of the phone number.
return res;
}
});
})();
I'm not sure if you need this, but here's the controller:
(function () {
'use strict'
angular
.module('Allay', [
'Allay.phoneFilter'
])
.controller('HomeController', function () {
var home = this;
});
})();
If I add an alert(res) before 'return res' in the filter I see the value I expect '123::', however the value in the input it's self is still just 123.
You need create directive to change your ngModel, like this:
.directive('phoneFormat', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var setvalue = function() {
elem.val(ctrl.$modelValue + "::");
};
ctrl.$parsers.push(function(v) {
return v.replace(/::/, '');
})
ctrl.$render = function() {
setvalue();
}
elem.bind('change', function() {
setvalue();
})
}
};
});
Use in html:
<input name='phone' ng-model='contact.phone' placeholder='(Area code) Phone' required phone-format />
JS Fiddle: http://jsfiddle.net/57czd36L/1/
Your usage of ngBind on the input is not quite correct. From the documentation,
The ngBind attribute tells Angular to replace the text content of the specified HTML element with the value of a given expression, and to update the text content when the value of that expression changes
You do not need to replace the text content of the <input> element, that wouldn't make sense. You can instead extend the formatter pipeline of the NgModelController using a directive like
app.directive('phoneFormat', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
ngModel.$formatters.push(function (value) {
if (value)
return value + '::';
});
}
}
});
Then, in your HTML,
<input ng-model='home.contact.phone' phone-format />
In case you wanted to keep the filter you wrote (for other usages), you can actually re-use it in the directive like
app.directive('phoneFormat', [ '$filter', function ($filter) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
ngModel.$formatters.push($filter('phone'));
}
}
}]);
$filter('phone') simply returns the filter function registered under 'phone'. Here is a Plunker.
Note, this solution will only format data when you change the $modelValue of the NgModelController, for example like
$scope.$apply('home.contact.phone = "123-456-7890"');
If you are looking for something to update/format the value of the input as the user is typing, this is a more complicated task. I recommend using something like angular-ui/ui-mask.
Although a filter module is a good approach, I use an 'A' directive to do the dirty work because changing the element value will affect its ng-model.
However, I would only suggest this kind of solution if your actual data manipulation could sum in 3-4 lines of code; otherwise, a more thorough approach is needed.
This is an example that will delete anything which isn't an integer:
(function () {
'use strict'
angular.module('Allay').directive('phoneValidator', function () {
return {
restrict: 'A',
link: function(scope, element, attrs) {
angular.element(element).on('keyup', function() {
element.val(element.val().replace(/[^0-9\.]/, ''));
});
}
}
});
})();
And than in your HTML template :
<input name="phone" ng-model="home.contact.phone" placeholder="(Area code) Phone" phoneValidator required/>`
You should remove your "ng-bind" cause you are filtering it and what is presented is what in the ng-model. use value instead.
<input name='phone' ng-model='home.contact.phone | phone' value="{{contact.phone | phone}}" />
see working example: JsFiddle

How to access Angular form validation within a directive

I have a simple form with a directive:
<form name="testform">
<input name="testfield" type="text" ng-model="test.name" ng-minlength="3" my-validate>
</form>
Since Angular validates the field itself when entering less than 3 characters, it sets testform.testfield.$invalid.
What I want to achieve is to add some custom validation functionality in a directive and react to this angular validation/set the validation result e.g. with the help of watchers or events like so:
angular.module('myApp.directives').directive('myValidate', [function() {
return {
link: function(scope, elem, attrs) {
elem.on('keyup', function() {
// do some custom validation
// if valid, set elem to $valid
// else set $error
// something like elem.set('$valid');
}
}
};
}]);
How can I achieve the combination of HTML5, Angular and custom form validation while using Angulars testform.testfield.$invalid, testform.testfield.$valid, testform.testfield.$error etc. logic?
By default Angular provides some directives for input validation (like required, pattern, min, max etc.). If these directives doesn't suffice, custom validation is available. To be able to do that, the directive requires the model used for validation, then it can set the validity of the element like below:
.directive('myValidate', [ function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$setValidity('myValidate', false);
}
};
}]);
A Plunker with such a directive which will become invalid when you enter '0' is HERE.
You can read more about custom validation on Angular forms documentation.
You can do custom validation and combine it with Angular's built in validation, you just need to require ngModel, pass ctrl as the 4th value to your link function, and use ctrl.$setValidity to indicate if your custom validation passes or fails.
The example below will be invalid if the length < 3 OR if the value is the string 'invalid':
var app = angular.module('app', []);
app.controller('controller', ['$scope',
function(scope) {
scope.test = {
name: ''
};
}
]);
app.directive('myValidate', [
function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
ctrl.$parsers.unshift(function(value) {
var valid = true;
if (value === 'invalid') {
valid = false;
}
ctrl.$setValidity(attrs.name, valid);
return valid ? value : undefined;
});
}
};
}
]);
Markup:
<body ng-app="app">
<div ng-controller="controller">
<form name="testform">
<input name="testform" type="text" ng-model="test.name" ng-minlength="3" my-validate /><br/>
</form>
<span style="color:green" ng-show="testform.testform.$valid">** valid **</span>
<span style="color:red" ng-show="testform.testform.$invalid">** invalid **</span>
</div>
<script src="script.js"></script>
</body>
Here is a plunker with this code: http://plnkr.co/edit/6XIKYvGwMSh1zpLRbbEm
The Angular documentation talks about this, though it wasn't totally obvious: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
This blog helped me get a hold on this: http://www.benlesh.com/2012/12/angular-js-custom-validation-via.html

Angular, input field with a currency mask directive for money format on the fly

I'm trying to create an input mask for a EU money field using http://jquerypriceformat.com/
So far in my directive, the input shows correctly to the user with the mask applied, but I believe there's something wrong, because the POST values are being sent with weird formatting, totally different than what we see in the input field.
I include the priceformat.js
<script src="js/jquery.price_format.1.8.min.js"></script>
<input type="text" currency-input ng-model...>
And on angular:
app.directive('currencyInput', function() {
return {
require: '?ngModel',
link: function($scope, element, attrs, controller) {
element.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
}
};
});
My input shows the value with the mask correctly, but on POST data (called by angular) it's a different value, what am I missing?
input > 2.200,80 | post > 22,0080
Thanks
From your example I don't see that link returns something.
I would write directive something like:
.directive('format', ['$filter', function ($filter) {
return {
require: '?ngModel',
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
ctrl.$formatters.unshift(function (a) {
return $filter(attrs.format)(ctrl.$modelValue)
});
ctrl.$parsers.unshift(function (viewValue) {
elem.priceFormat({
prefix: '',
centsSeparator: ',',
thousandsSeparator: '.'
});
return elem[0].value;
});
}
};
}]);
Demo 1 Fiddle
If you want on start fire the filter, use $formatters:
Now link is:
link: function (scope, elem, attrs, ctrl) {
if (!ctrl) return;
var format = {
prefix: '',
centsSeparator: ',',
thousandsSeparator: ''
};
ctrl.$parsers.unshift(function (value) {
elem.priceFormat(format);
return elem[0].value;
});
ctrl.$formatters.unshift(function (value) {
elem[0].value = ctrl.$modelValue * 100 ;
elem.priceFormat(format);
return elem[0].value;
})
}
Demo 2 Fiddle
Push a $parser to the controller, and only update the value when it doesn't match the input using $setViewValue() and $render().
app.directive('currencyInput', function() {
return {
require: '?ngModel',
link: function($scope, element, attrs, controller) {
return ctrl.$parsers.push(function(inputValue) {
...
if (result != inputValue) {
controller.$setViewValue(res);
controller.$render();
}
});
}
};
});
Here's a fiddle with the logic I used for my currency input directive: Fiddle
Late to the party, but I believe this deserves another answer! I've been using the ng-currency module. It's absolutely fantastic.
I like Dubilla's approach cause of its simplicity and elegance. I decided to add (with due credits) some features on top of it to make it quite close to real world use case.
I've using it on a github project to create some useful finance directives github.
Notable extra features:
It does some rigorous checking of the inputs to give a valid response.
It has some keyboard shortcuts to make entering large numbers quicker.
I show how to integrate it with bootstrap and ngmodel css updates.
As a bonus I outputed the form's ngmonel as JSON to help people see how the form validation works in real time
It also uses a POJO as the ngmodel:
function Money() {
this.notional = 0;
this.maxValue = 99999999999.9;
this.maxValueString = "99,999,999,999.9";
this.maxPrecision = 10;
}
you can use it with Bootstrap 3 like so:
<h1>Currency Formatting directive</h1>
<div class="row">
<div class="col-md-6">
<form name="myForm">
<div class="form-group" ng-class="{'has-error': myForm.notional.$invalid && myForm.notional.$touched}">
<input type="text" ng-model="myForm.money.notional " money="money" money-input size="30" required
name="notional"
class="form-control"
placeholder="Enter notional amount"/>
<p class="help-block error" ng-show="myForm.notional.$error.required && myForm.notional.$touched">Required</p>
</div>
<button ng-disabled="myForm.$invalid" type="submit">SAVE</button>
</form>
<h2>Tips</h2>
<ol>
<li> Entering 'k' will multiply the amount by one thousand</li>
<li> Entering 'm' will multiply the amount by one million</li>
<li> Entering 'b' will multiply the amount by one billion</li>
</ol>
</div>
</div>
<p>Form debugger</p>
<pre>
form = {{ myForm | json }}
</pre>
Here's a way to handle this without jQuery using only an Angular directive. This example doesn't support decimals. It's easy to modify it to support that though, just change the $filter in the toView() function.
In my opinion this is a better approach to solve the same problem, as you can avoid loading in jQuery and the currency plugin mentioned by the author. Locale support for Euro should be supported by the use the $locale properties, but I've only tested this for use in USD.
(function() {
var app = angular.module('currencyMask', []);
// Controller
app.controller('ctrl', ['$scope', function($scope) {
$scope.amount = 100000;
}]);
// Directive
app.directive('inputCurrency', ['$locale', '$filter', function($locale, $filter) {
// For input validation
var isValid = function(val) {
return angular.isNumber(val) && !isNaN(val);
};
// Helper for creating RegExp's
var toRegExp = function(val) {
var escaped = val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
return new RegExp(escaped, 'g');
};
// Saved to your $scope/model
var toModel = function(val) {
// Locale currency support
var decimal = toRegExp($locale.NUMBER_FORMATS.DECIMAL_SEP);
var group = toRegExp($locale.NUMBER_FORMATS.GROUP_SEP);
var currency = toRegExp($locale.NUMBER_FORMATS.CURRENCY_SYM);
// Strip currency related characters from string
val = val.replace(decimal, '').replace(group, '').replace(currency, '').trim();
return parseInt(val, 10);
};
// Displayed in the input to users
var toView = function(val) {
return $filter('currency')(val, '$', 0);
};
// Link to DOM
var link = function($scope, $element, $attrs, $ngModel) {
$ngModel.$formatters.push(toView);
$ngModel.$parsers.push(toModel);
$ngModel.$validators.currency = isValid;
$element.on('keyup', function() {
$ngModel.$viewValue = toView($ngModel.$modelValue);
$ngModel.$render();
});
};
return {
restrict: 'A',
require: 'ngModel',
link: link
};
}]);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<div ng-app="currencyMask" ng-controller="ctrl">
<input input-currency ng-model="amount">
<p><strong>Amount:</strong> {{ amount }}</p>
</div>

Resources