I'm having problems binding a number value using AngularJS.
I've put a simplified example on JSFiddle: http://jsfiddle.net/treerock/ZvdXp/
<div ng-controller="MyCont" ng-app>
<input type="number" min="0" max="50" value="{{value}}" ng-model="value" />
<input type="text" value="{{value}}" ng-model="value" />
<input type="range" min="0" max="50" value="{{value}}" ng-model="value" />
{{value}}
</div>
This should be three different types of input fields, and if you update one, then all values should update. That's working except for the number input. e.g. If I type 20 in the first number box, it updates all other instances of value. But if I update the text or range inputs, the number input goes blank.
I was wondering if the issue was with how the number is represented/converted between fields. e.g. the number input is a float and the text input is a string?
You're right, it has to do with string vs number types. I used a $scope.watch statement to fix it: http://jsfiddle.net/ZvdXp/6/
You can also fix this with a directive. I created a directive to force input bound to numeric fields to be numeric.
Html:
myApp.directive('numericbinding', function () {
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
},
link: function (scope, element, attrs, ngModelCtrl) {
if (scope.model && typeof scope.model == 'string') {
scope.model = parseInt(scope.model);
}
}
};
});
You can add it to your numeric field like this:
<input data-ng-model="stringnumber" numericbinding type="number"/>
full example: http://jsfiddle.net/tdjager/cMYQ3/1/
I've expanded on Tim's answer to make it correct the data type after the user updates the control value as well.
myApp.directive('numericbinding', function () {
return {
restrict: 'A',
require: 'ngModel',
scope: {
model: '=ngModel',
},
link: function (scope, element, attrs, ngModelCtrl) {
if (scope.model && typeof scope.model == 'string') {
scope.model = parseInt(scope.model);
}
scope.$watch('model', function(val, old) {
if (typeof val == 'string') {
scope.model = parseInt(val);
}
});
}
};
});
If you prefer to save a numeric value in the model, you can use a directive that convert the string generated by the text input and by the range input in a numeric value through the angular parser, like so:
myApp.directive('numericsaving', function () {
return {
restrict: 'A',
require: '?ngModel',
scope: {
model: '=ngModel'
},
link: function (scope, element, attrs, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
ngModelCtrl.$parsers.push(function (value) {
if (!value || value==='' || isNaN(parseInt(value)) || parseInt(value)!==value) {
value=0;
}
return parseInt(value);
});
}
};
});
In the HTML, leave the number input as is and add the directive in the others inputs this way:
<input type="number" min="0" max="50" value="{{value}}" ng-model="value" />
<input type="range" min="0" max="50" value="{{value}}" ng-model="value" numericsaving/>
<input type="text" value="{{value}}" ng-model="value" numericsaving/>
The angular parser will translate the string input in a numeric value before saving it in model, so the numeric input will automatically work.
Here the complete fiddle.
Moreover, if the user inserts letters or any strange character in the text input, they will not be saved in the model, preventing invalid value in the single source of truth of your application.
Only '+' and '-' character at the begin of the text will be correctly parsed, so even negative value are allowed.
I hope this helps! :)
TypeScript version inspired ainos984, for the sake of posterity
export class ngIntegerDirective implements ng.IDirective {
static directiveKey: string = 'ngInteger';
require: string = 'ngModel';
link = (scope, ele, attr, ctrl: ng.INgModelController) => {
ctrl.$parsers.unshift(function (viewValue) {
let result: number = parseInt(viewValue,10);
if (isNaN(result)) {
result = 0;
}
return result;
});
}
public static Factory(): ng.IDirectiveFactory {
const directive = () => new ngIntegerDirective();
directive.$inject = []; //injecter les dépendances ici
return directive;
}
}
Related
I have a text box in my Application, i am using change-on-blur custom directive to validate that field on tab out that is, if i type ABCD initially in that text box then i delete D character and again i type D character so custom change-on-blur will call only one time.
For some issues i am using ng-model-options="{updateOn: 'blur'}" ng-change="validate(data)" instead ng-blur for that element. So if i type ABCD it will call the function, again i delete and type D character it will call that function again. Is there custom ng-change directive to call only once.
I am struggling with this for last 2 days. Kindly help
Html :
<input type="text" ng-model="data.taxNo" id="taxNo"
ngomitsplchar class="form-control taxNo"
maxlength="15" style="text-transform: uppercase;"
ng-paste="$event.preventDefault();"
ng-model-options="{updateOn: 'blur'}"
ng-change="validate(data)"
tabindex="5" spellcheck="false" autocomplete="nope">
JS:
$scope.validate = function (obj) {
alert("2"); // Prompting alert 2 times when using ng-change
// Prompting alert 1 times when using change-on-blur
}
app.directive('changeOnBlur', function($timeout) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, ngModelCtrl) {
if (attrs.type === 'radio' || attrs.type === 'checkbox')
return;
var expressionToCall = attrs.changeOnBlur;
var oldValue = null;
elm.bind('focus',function() {
//scope.$apply(function() {
oldValue = elm.val();
//});
});
elm.bind('blur', function() {
$timeout(function() {
var newValue = elm.val();
if (newValue !== oldValue){
scope.$eval(expressionToCall);
}
//alert('changed ' + oldValue);
});
});
}
};
});
I have found the solution. Need to change from ng-model-options="{updateOn: 'blur'}" to ng-model-options="{updateOn: '**change blur**'}". So its restricted duplicate call when changing same value in text box by using ng-change="validate(data)"
<input type="text" ng-model="data.taxNo" id="taxNo"
ngomitsplchar class="form-control taxNo"
maxlength="15" style="text-transform: uppercase;"
ng-paste="$event.preventDefault();"
ng-model-options="{updateOn: 'change blur'}"
ng-change="validate(data)"
tabindex="5" spellcheck="false" autocomplete="nope">
I have a form input. When the page is loaded the input has "ng-readonly=true" property and it only shows the data.
When I double click (ng-dblclick) the property "ng-readonly" changes to false and I can edit the input.
For all this, it is working currectly. But when the data
(ng-model="school.fax") a row data is empty it does do a focus, and I need to click on the input to focus and start writing.
It does not happen when the data is not empty (ng-model="school.fax" have value, get value from server API) and in this case, it's working correctly
The question:
How can I focus on the empty input and start writing without need to click the input row?
The code:
HTML
<label>
<input class="inputs"
type="text"
ng-readonly="!edit_school_fax"
ng-dblclick="editSchoolFax(true)"
ng-model="school.fax"/>
</label>
JS
$scope.editSchoolFax = function(edit) {
$scope.edit_school_fax = edit;
};
FYI
I try, and it does not work for me:
Add "autofocus" inside the input
<input autofocus
Use directive like this solution: LINK
add custom directive
link(scope, element, attrs) {
scope.$watch(attrs.someAttrs, (newVal, oldVal) => {
if (newVal === true && newVal !== oldVal) {
$timeout(() => {
element
.focus()
.select();
});
}
});
},
Use a custom directive that adds a focus method to the ngModelController:
app.directive("inputFormFocus", function() {
return {
require: "ngModel",
link: postLink
};
function postLink (scope, elem, attrs, ngModel) {
console.log("postLink");
console.log(ngModel);
ngModel.focus = function() {
elem[0].focus();
}
}
})
Usage
<form name="form1">
<input input-form-focus
name="fax"
class="inputs"
type="text"
ng-readonly="!edit_school_fax"
ng-dblclick="editSchoolFax(true)"
ng-model="school.fax"/>
</form>
$scope.form1.fax.focus();
The DEMO
angular.module("app",[])
.controller("ctrl",function($scope){
$scope.editSchoolFax = function(edit) {
$scope.edit_school_fax = edit;
};
$scope.school = { fax: "555-100-1234" };
$scope.faxFocus = function() {
$scope.edit_school_fax = true;
$scope.form1.fax.focus();
};
})
.directive("inputFormFocus", function() {
return {
require: "ngModel",
link: postLink
};
function postLink (scope, elem, attrs, ngModel) {
ngModel.focus = function() {
console.log(attrs.name + " focus");
elem[0].focus();
}
}
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl">
<form name="form1">
<input input-form-focus
name="fax"
class="inputs"
type="text"
ng-readonly="!edit_school_fax"
ng-dblclick="editSchoolFax(true)"
ng-model="school.fax"/>
</form>
<button ng-click="faxFocus()">Focus and Edit</button>
</body>
In my angular-material app I have a field with validation:
<input type="number" ng-model="myValue"
name="myValue"
min="0" max="100"
ng-pattern="/^[0-9]\d*(\.\d+)?$/" required>
It works fine. But now I need to make it to allow user to type .75 and get instead of 0.75
So the question how to apply my filter:
if( myValue.substring(0,1) == "." ){
myValue = "0" + myValue;
}
before angular material ng-pattern and type="number" validation
You can write some directive with $filter, $formatters and $parsers:
app.directive('parser', ['$filter',
function($filter) {
return {
restrict:'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
ctrl.$formatters.unshift(function (a) {
return $filter('myFilter')(ctrl.$modelValue)
});
ctrl.$parsers.unshift(function (viewValue) {
element[0].value = $filter('myFilter')(element[0].value);
return element[0].value;
});
}
};
}
]);
app.filter('myFilter', [
function() {
return function(input) {
if( input && input.substring(0,1) == "." ){
input = "0" + input;
}
return input;
};
}
]);
DEMO Plunkr
However you cannot use type number because as I remember . not supported and doesn't parses as integer
Hope it will give you direction
I am trying to create an angular Directive which returns an error when the input of the textfield is less than 5 and greater than 200 i am trying with this code and for some reason it isnt working any help would be appreciated.
JS
app.directive('numbersOnly', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
// this next if is necessary for when using ng-required on your input.
// In such cases, when a letter is typed first, this parser will be called
// again, and the 2nd time, the value will be undefined
if (inputValue == undefined) return ''
var transformedInput = inputValue.replace(/[^0-9]/g, '');
console.log("inputValue"+inputValue);
if(parseInt(inputValue) > 200 || parseInt(inputValue) < 5){
return '';
}
if (transformedInput!=inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
HTML
<div ng-controller="MyCtrl">
<input type="text" ng-model="number" required="required" numbers-only="numbers-only" />
</div>
The plunker that i created is this (http://plnkr.co/edit/QKifStiFmHBF8GhcH3Ds?p=preview)
Any help would be appreciated!
I have given a directive that takes care of your model value to always contain int values between 5 and 200. A 'ng-invalid' class will be added when you do setValidity to false. Using that you can use css to display error to the user. In case you want your input to be updated with the correct model value in case of error, you can do it in the blur event.
app.directive('numbersOnly', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
if(parseInt(inputValue) <= 200 && parseInt(inputValue) >= 5){
modelCtrl.$setValidity('numbersOnly', true);
return inputValue;
} else {
modelCtrl.$setValidity('numbersOnly', false);
return modelCtrl.$modelValue;
}
});
}
};
});
angular already has perfect directives for that. all you need is a form and use Min Max inside input tag
<form name="ue.form">
<input type="number" ng-model="ue.num" name="num" min="5" max="200" >
</form>
<p ng-if="ue.form.$error">
number must be less than 200 and greater than 5
</p>
or you can handle each error separately:
<p ng-if="ue.form.num.$error.min">
number must be greater than 5
</p>
<p ng-if="ue.form.num.$error.max">
number must be less than 200
</p>
if number is not in range 5-200 then form validotor throw an error.
min and max work only with input type="number".
https://plnkr.co/edit/?p=preview
Here's what I would suggest:
Use ng-model-options="{ updateOn: 'blur' }" so that model gets updated only on blur.
Try this code in directive:
app.directive('numbersOnly', function(){
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, element, attrs, ngModel) {
element.on('blur', function() {
if (ngModel.$viewValue < 5 || ngModel.$viewValue > 200) {
ngModel.$setViewValue('');
element.val('');
}
});
}
};
});
You can use ng-messages for these kind of validation purposes
We always can customize ng-messages with our needs.
you can create two directives for min and max. In your case your minimum value is 5 and max is 200, we dont need to hardcode our values inside the directives.
You dont need to worry for adding error messages in your directive. ng-messages will do it for you. You just need to put your messages inside ng-messages div.
Directive
module.directive("min", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attributes, ngModel) {
ngModel.$validators.min = function (modelValue) {
if (!isNaN(modelValue) && modelValue !== "" && attributes.min !== "")
return parseFloat(modelValue) >= attributes.min;
else
return true;
}
}
};
});
module.directive("max", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attributes, ngModel) {
ngModel.$validators.max = function (modelValue) {
if (!isNaN(modelValue) && modelValue !== "" && attributes.max !== "")
return parseFloat(modelValue) <= attributes.max;
else
return true;
}
}
};
});
Usage
<form name="myform">
<input type="text" name="minmax" ng-model="number" required="required" min="5" max="200"/>
<div data-ng-messages="myform.minmax.$error" class="error-messages">
<div data-ng-message="min">YOu cant enter below 5</div>
<div data-ng-message="max">You cant enter above 200</div>
</div>
</form>
Here is my pluker example
I am trying to write custom directive for USA telephone number using angularjs and need to preserve the data type of the field as integer.Here is the jsfiddle directive and need help to complete the directive.
If user enters a valid telephone no (exactly 10 numbers ie.1234567890) then input should split into 3 chunks as 123-456-7890 when the user moves to next control.otherewise I should show error message "not a valid number".
<div ng-app="myApp" ng-controller="myCtrl">
<form name="myForm">
<input type="text" ng-model="telephone" phoneformat name="input1" />
<span class="error" ng-show="myForm.input1.$error.telephone">Numbers only!</span>
<span class="error" ng-show="myForm.input1.$error.telephone">Exact 10 Numbers only!</span>
</form>
</div>
var myApp = angular.module("myApp", []);
var myCtrl = myApp.controller("myCtrl",["$scope", function($scope) {
$scope.telephone = "1234567890";
}]);
myApp.directive("phoneformat", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attr, ngModelCtrl) {
var phoneformat = function () {
}
}
};
});
It looks like you want to leverage the $error property of the form to drive validation. To do this, you will need to call $setValidity in the ngModelCtrl that you have required into your directive:
var myApp = angular.module("myApp", []);
var myCtrl = myApp.controller("myCtrl",["$scope", function($scope) {
$scope.telephone = "1234567890";
}]);
myApp.directive("phoneformat", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attr, ngModelCtrl) {
//Parsing is performed from angular from view to model (e.g. update input box)
//Sounds like you just want the number without the hyphens, so take them out with replace
var phoneParse = function (value) {
var numbers = value && value.replace(/-/g, "");
if (/^\d{10}$/.test(numbers)) {
return numbers;
}
return undefined;
}
//Formatting is done from view to model (e.g. when you set $scope.telephone)
//Function to insert hyphens if 10 digits were entered.
var phoneFormat = function (value) {
var numbers = value && value.replace(/-/g,"");
var matches = numbers && numbers.match(/^(\d{3})(\d{3})(\d{4})$/);
if (matches) {
return matches[1] + "-" + matches[2] + "-" + matches[3];
}
return undefined;
}
//Add these functions to the formatter and parser pipelines
ngModelCtrl.$parsers.push(phoneParse);
ngModelCtrl.$formatters.push(phoneFormat);
//Since you want to update the error message on blur, call $setValidity on blur
element.bind("blur", function () {
var value = phoneFormat(element.val());
var isValid = !!value;
if (isValid) {
ngModelCtrl.$setViewValue(value);
ngModelCtrl.$render();
}
ngModelCtrl.$setValidity("telephone", isValid);
//call scope.$apply() since blur event happens "outside of angular"
scope.$apply();
});
}
};
});
Working fiddle. This was just a quick way of demonstrating the parser and formatter pipelines that are used in ngModel, along with $setValidity -- which is used to populate the $error field(s).
Update: To use this same phone validation across multiple phones, use form with $error. Notice that each input gets a unique name that is used with myForm (name of form). Both use $error.telephone:
<form name="myForm">
Mobile Phone:
<input type="text" ng-model="mobilephone" phoneformat name="mobilephone" />
<span class="error" ng-show="myForm.mobilephone.$error.telephone">
Exact 10 Numbers only!
</span>
<br />
Home Phone:
<input type="text" ng-model="homephone" phoneformat name="homephone" />
<span class="error" ng-show="myForm.homephone.$error.telephone">
Exact 10 Numbers only!
</span>
</form>
Updated fiddle.
You might want to use http://angular-ui.github.io/ui-utils/ Mask directive.
Working Fiddle: http://jsfiddle.net/HB7LU/6581/
myApp.directive("phoneFormat", function () {
return {
restrict: "A",
link: function (scope, element, attr) {
element.bind('change', function() {
if ( this.value.length === 10 ) {
var number = this.value;
this.value = number.substring(0,3) + '-' + number.substring(3,6) + '-' + number.substring(6,10)
}
else {
document.querySelector('.helpblock').innerHTML = 'error in formatting';
}
});
}
};
});
Iv'e extended your original fiddle. here's the result:
http://jsfiddle.net/10k58awt/
You can find splittedNumber array (contains 3 parts of number) on form submission
js:
var myApp = angular.module("myApp", []);
var myCtrl = myApp.controller("myCtrl", ["$scope", function ($scope) {
$scope.telephone = "1234567890";
$scope.submit = function () {
var splittedNumber = [$scope.telephone.substring(0, 3), $scope.telephone.substring(3, 6), $scope.telephone.substring(6, 10)];
// Do something with splitted number
console.log(splittedNumber);
};
}]);
myApp.directive("phoneformat", function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (phoneInput) {
phoneInput = phoneInput.trim();
if (phoneInput && phoneInput.length == 10 && !isNaN(phoneInput)) {
ctrl.$setValidity('phoneformat', true);
return phoneInput;
} else {
ctrl.$setValidity('phoneformat', false);
return undefined;
}
});
}
};
});
html:
<div ng-app="myApp" ng-controller="myCtrl">
<form name="myForm" novalidate ng-submit="myForm.$valid && submit()">
<input type="text" ng-model="telephone" phoneformat name="input1" /> <span class="error" ng-show="myForm.input1.$error.phoneformat">Invalid US Phone number!</span>
<div>
<button class="btn btn-primary" type="submit" ng-class="{'disabled': !myForm.$valid}">submit</button>
</div>
</form>
</div>