I have the following JSfiddle example.
HTML
<div ng-app="app" ng-controller="Example">
<input type="number" ng-model="data.mainOdd1" placeholder="#1 Main Odd" onfocus="this.placeholder=''" min="0" step="any" ui-blur="testfn('mainOdd1', $event, '#1 Main Odd');">
</div>
Javascript
angular
.module('app', [])
.directive('uiBlur', function($parse) {
return function(scope, elem, attrs) {
elem.bind('blur', function(event) {
scope.$apply(function() {
$parse(attrs.uiBlur)(scope, {
$event: event
});
});
});
};
})
.controller('Example', function($scope) {
$scope.data = {
'mainOdd1' : '',
};
$scope.testfn = function(propertyName, $event, placeHolder) {
debugger;
if (($event.target.validity.valid == false) ||
($scope.data[propertyName] == ''))
{
$scope.data[propertyName] = '';
$event.target.placeholder = placeHolder;
return;
}
debugger;
$scope.data[propertyName] = $scope.data[propertyName].toFixed(2);
};
});
I try to keep only two decimals.
I see two issues, the first one is that the number in the view does not change, although mainOdd1 does change. and the second one is that I get an error in the console about using the toFixed function.
What am I doing wrong here?
Thanks
Hi toFixed Error happening because of while converting toFIxed it will become String but your input type in number so it wont accept so u have to change to float
$scope.testfn = function(propertyName, $event, placeHolder) {
debugger;
if (($event.target.validity.valid == false) ||
($scope.data[propertyName] == ''))
{
$scope.data[propertyName] = '';
$event.target.placeholder = placeHolder;
return;
}
debugger;
$scope.data[propertyName] = parseFloat($scope.data[propertyName].toFixed(2));
};
Your input is type="number".You must use this
$scope.data = {
'mainOdd1' : 0,
};
and can use step=0.01 for input in order to have only two decimals
The problem is with this line
$scope.data[propertyName].toFixed(2)
Use Unary plus to convert it to number
$scope.data[propertyName] = +$scope.data[propertyName].toFixed(2);
run the snippet to solve both of your issues:
angular
.module('app', [])
.directive('uiBlur', function($parse) {
return function(scope, elem, attrs) {
elem.bind('blur', function(event) {
scope.$apply(function() {
$parse(attrs.uiBlur)(scope, {
$event: event
});
});
});
};
})
.controller('Example', function($scope) {
$scope.data = {
'mainOdd1' : '',
};
$scope.testfn = function(propertyName, $event, placeHolder) {
if (($event.target.validity.valid == false) ||
($scope.data[propertyName] == ''))
{
$scope.data[propertyName] = '';
$event.target.placeholder = placeHolder;
return;
}
$scope.data[propertyName] = parseFloat($scope.data[propertyName]).toFixed(2);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.js"></script>
<div ng-app="app" ng-controller="Example">
<input type="text" ng-model="data.mainOdd1" placeholder="#1 Main Odd" onfocus="this.placeholder=''" min="0" step="any" ui-blur="testfn('mainOdd1', $event, '#1 Main Odd');">
</div>
Related
something similar may have been answered (ng-pattern + ng-change) but all responses were unable to fix this issue.
I have two imbricated directives for creating a form input, a parent directive to control name, label, validator etc. and a child directive to set pattern and input type specific stuff.
However, when setting a pattern, the value on my model is set to undefined when ng-pattern return false.
Directives:
<input-wrapper ng-model="vm.customer.phone" name="phone" label="Phone number">
<input-text type="tel"></input-text>
</input-wrapper>
Generated HTML:
<label for="phone">Phone number:</label>
<input type="text" name="phone"
ng-model="value"
ng-model-options="{ updateOn: \'blur\' }"
ng-change="onChange()"
ng-pattern="/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/">
JS:
angular.module('components', [])
.directive('inputWrapper', function() {
return {
restrict: 'E',
require: 'ngModel',
scope: true,
link: function (scope, element, attrs, ngModel) {
scope.name = attrs.name;
scope.label = attrs.label;
scope.onChange = function () {
ngModel.$setViewValue(scope.value);
};
ngModel.$render = function () {
scope.value = ngModel.$modelValue;
};
}
}
})
.directive('inputText', function() {
return {
restrict: 'E',
template: '<label for="{{name}}">{{label}}:</label><input type="text" name="{{name}}" ng-model="value" ng-model-options="{ updateOn: \'blur\' }" ng-change="onChange()" ng-pattern="pattern">',
link: function (scope, element, attrs) {
if (attrs.type === 'tel') {
scope.pattern = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;
}
}
}
});
angular.module('app',['components'])
.controller('ctrl',function($scope){
var vm = this;
vm.customer = {
phone: '010203040506'
};
});
What am I doing wrong ?
Codepen for use case: https://codepen.io/Yosky/pen/yVrmvw
By default in angular if a validator fail, undefined value assigned to ng-model, You can change this setting as follow :
<div ng-model-options="{ allowInvalid: true}">
read here for detail docs
I had some requirements that meant that I really really didn't want ng-model to write out undefined to the scope when validation was invalid, and I didn't want the invalid value either, so allowInvalid didn't help. In stead I just wanted ng-model do not write anything, but I couldn't find any option for this.
So I couldn't see any way forward except for doing some monkey patching of the ng-model controller.
So first I required ngModel in the component I was building require: { model: 'ngModel' } and then I did this in the $onInit hook:
const writeModelToScope = this.model.$$writeModelToScope;
const _this = this;
this.model.$$writeModelToScope = function() {
const allowInvalid = _this.model.$options.getOption('allowInvalid');
if (!allowInvalid && _this.model.$invalid) {
return;
}
writeModelToScope.bind(this)();
};
I also didn't want to take in a new model value while the value was invalid and the component had focus, so I did:
const setModelValue = this.model.$$setModelValue;
this.model.$$setModelValue = function(modelValue) {
_this.lastModelValue = modelValue;
if (_this.model.$invalid) {
return;
}
if (_this.hasFocus) {
return;
}
setModelValue.bind(this)(modelValue);
};
element.on('focus', () => {
this.hasFocus = true;
});
element.on('blur', (event) => {
this.hasFocus = false;
const allowInvalid = this.model.$options.getOption('allowInvalid');
if (!allowInvalid && this.model.$invalid) {
this.value = this.lastModelValue;
}
event.preventDefault();
});
Feel free to judge me, just know that I already feel dirty.
The code below works only when the input type is text, and it doesn't work when the type is number.
<div ng-app="myApp" ng-controller="myCtrl as model">
<input type="text" ng-model="cero" ng-decimal >
</div>
angular
.module("myApp",[])
.controller('myCtrl', function($scope){
var model=this;
})
.directive('ngDecimal', function ($parse) {
var linkFunction =function(scope, element, attrs){
element.bind("keypress", function(event) {
if(event.which === 13) {
scope.$apply(function(){
scope.$eval(attrs.format, {'event': event});
if(scope.cero===undefined || scope.cero===''){
scope.cero="0.",
event.preventDefault();
}else{
}
});
}
});
};
return{
restrict : 'A',
scope:{
cero: '=ngModel'
},
link: linkFunction
}
});
What I need help with is changing the type to number and still making the code work. The code is also on CodePen.
Updated pen : http://codepen.io/anon/pen/QKOVkP?editors=1011
Works with number, constraint being you cannot assign
scope.cero = "0." // string value
to a type="number" so replace it with the minimum number you want to assign, maybe
scope.cero = parseFloat("0.01") // parseFloat("0.") won't work
In the else condition add this.
scope.cero = parseFloat(scope.cero).toFixed(2);
Convert string to decimal
Here is the code: working code
I was working in angular project, There I had come across a situation in which I need to validate custom component having a textbox.
<dollar-text-validate ng-model="ctrl.value" required name="myDir"></dollar-text-validate>
My Component
angular.module("myApp", []);
angular.module("myApp").controller('MyController',function(){
var ctrl = this;
ctrl.value = 56;
});
angular.module("myApp").component('dollarTextValidate',{
bindings: {
ngModel :'='
},
template: '<div><input type="text" ng-focus="ctrl.focus()" ng-blur="ctrl.blur()" ng-model="ctrl.amount1"><input type="hidden" ng-model="ctrl.ngModel"></div>',
controller: function() {
var ctrl = this;
// ctrl.amount1 =
ctrl.amount1 =ctrl.ngModel===undefined||ctrl.ngModel==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
ctrl.focus=function(){
ctrl.amount1 = ctrl.amount1 === undefined ? '' : ctrl.amount1.slice(1);
ctrl.ngModel = ctrl.amount1;
console.log(ctrl.ngModel);
}
ctrl.blur=function(){
ctrl.ngModel = ctrl.amount1;
ctrl.amount1 = ctrl.amount1==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
}
},
controllerAs:'ctrl'
})
This component is used to set $ symbol in front of entered value. So $ appended value will be available in textbox and original value which is to be validated in hidden field.
How can I validate hidden field. I tried with required attribute in hidden tag but nothing happening. Also tried with custom tag.
Sorry to break it to you, but you might wanna go for directive, and then use $parsers, $formatter and $validators properties of ngModelController.
Component can be used for this, but it is just easier with normal directive.
angular.module('myApp', []);
angular.module("myApp").directive('dollarTextValidate', function() {
return {
require: 'ngModel',
link: function($scope, $element) {
var regexp = /^\$(\d+(\.\d+)?)$/;
var ngModel = $element.controller('ngModel');
ngModel.$formatters.push(function(value) {
return value ? '$' + value : '';
});
ngModel.$parsers.push(function(value) {
var matched = value.match(regexp);
if (matched) {
return +matched[1];
}
});
ngModel.$validators.greaterThan10 = function (modelVal, viewVal) {
var value = modelVal || viewVal;
return value > 10;
};
},
controllerAs: 'ctrl'
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="myApp" ng-form="form">
dollar-text-validate = <input dollar-text-validate ng-model="value" required name="myDir"><br>
number input = <input type="number" ng-model="value" required><br>
value = {{value}}<br>
errors = {{form.myDir.$error}}
</div>
I'm facing an issue which I can't seem to solve.
I have several inputs with each a directive to validate the input value, like this:
<div class="row form-group">
<div class="col-sm-6">last name</div>
<div class="col-sm-6">
<div class="input-group" ng-class="{'has-error': form.lastname.$invalid && (form.lastname.$touched || form.$submitted)}">
<input type="text" name="lastname" class="form-control"
model-blur
validator-lastname
ng-trim="true"
ng-model="fields.lastname.value"
ng-maxlength="fields.lastname.validation.maxLength">
<input-group-addon class="input-group-addon"
iga-char=""
iga-form="form"
iga-field="form.lastname"
iga-if-touched="true">
</input-group-addon>
</div>
<form-message-list fml-form="form"
fml-field="form.lastname"
fml-label="Last name"
fml-fieldData="fields.lastname">
</form-message-list>
</div>
</div>
This field required the following pattern: /^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/
My issue is this:
When I add an invalid value to my input, like this: / , my invalid message remains and ng-invalid-pattern remains on my field.
When I add this pattern to my field like this: ng-pattern="/^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/" I don't have any issues. But when I try to validate via my directive validator-lastname it only checks one time. When I fill the input with an invalid value and then change it to empty, which is allowed, the ng-invalid-pattern error remains.
This is my directive:
angular.module('app')
.directive('validatorLastname', validatorLastname);
/* #ngInject */
function validatorLastname() {
var directive = {
require: 'ngModel',
link: link
};
return directive;
function link(scope, element, attrs, modelCtrl) {
var valid = false;
var formatter = function (inputValue) {
if (inputValue) {
var res = inputValue.match(/^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/);
if (res && res.length > 0) {
valid = true;
}
modelCtrl.$setValidity('pattern', valid);
valid = false;
}
return inputValue;
};
modelCtrl.$parsers.push(formatter);
if (scope[attrs.ngModel] && scope[attrs.ngModel] !== '') {
formatter(scope[attrs.ngModel]);
}
}
}
I made a JSFiddle to reproduce the problem: http://jsfiddle.net/sZZEt/537/
I hope someone can point me in the right direction.
Thanks in advance.
You should update your directive code to make everything work fine.
angular.module('app')
.directive('validatorLastname', validatorLastname);
/* #ngInject */
function validatorLastname() {
var directive = {
require: 'ngModel',
link: link
};
return directive;
function link(scope, element, attrs, modelCtrl) {
var valid = false;
var formatter = function (inputValue) {
if (inputValue) {
var res = inputValue.match(/^[\'a-zA-Z_]+( [\'a-zA-Z_]+)*$/);
if (res && res.length > 0) {
valid = true;
}
modelCtrl.$setValidity('pattern', valid);
valid = false;
}else{
modelCtrl.$setValidity('pattern', true);
}
return inputValue;
};
modelCtrl.$parsers.push(formatter);
if (scope[attrs.ngModel] && scope[attrs.ngModel] !== '') {
formatter(scope[attrs.ngModel]);
}
}
}
I have created a plunk for your problem...
It is because if inputValue is null then your $setValidity method will not invoke and could not perform validation again. You should set pattern validity to true inside else part. if you want to make field valid for no-input.
You can now refer to updated plunk https://plnkr.co/edit/N3DrsB?p=preview
I have written a custom directive to ensure that a text box can take only integer values.
But After using the directive, my $error.required flag always stay false irrespective of whether I have a value in the text field or not.
It works fine if I use the native input type,
<input name="name" ng-model="testvalue.number" required />
but when I use the custom directive,
<number-only-input input-value="testvalue.number" input-name="name"/>
shippingForm.name.$error.required is always false and it doesn't show the error "Please enter a value" even if the field is empty
This is the code
<body ng-controller="ExampleController">
<form name="shippingForm" novalidate>
<!--<input name="name" ng-model="testvalue.number" required />-->
<number-only-input input-value="testvalue.number" input-name="name"/>
<span class="error" ng-show="shippingForm.name.$error.required">
Please enter a value
</span>
</form>
</body>
<script>
angular.module('TextboxExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.testvalue = {number: 1, validity: true}
}])
.directive('numberOnlyInput', function () {
return {
restrict: 'EA',
template: '<input name="{{inputName}}" ng-model="inputValue" required />',
scope: {
inputValue: '=',
inputName: '='
},
link: function (scope) {
scope.$watch('inputValue', function(newValue,oldValue) {
if (newValue == undefined || newValue == null || newValue == "") {
return;
}
if (isNaN(newValue))
{
scope.inputValue = oldValue;
return;
}
});
}
};
});
</script>
Please guide
Sunil
You code has some flaws, but i think this is because you made the example incorrect, the main issue may be here:
inputName: '=' should be replaced with inputName: '#' to hold the string value of the input name
I'm not quite sure what's wrong with your example, it just doesn't work... Anyway here's a workaround which you can use to implement your functionality.
HTML:
<div ng-app="TextboxExample" ng-controller="ExampleController">
<form name="shippingForm" novalidate>
<input number-only-input type="text" name="name" ng-model="testvalue.number" required />
<!-- <number-only-input input-value="testvalue.number" input-name="name"/> -->
<span class="error" ng-show="shippingForm.name.$error.required">
Please enter a value
</span>
</form>
</div>
Controller:
angular.module('TextboxExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.testvalue = {number: 1, validity: true};
}])
.directive('numberOnlyInput', function () {
return {
link: function (scope, element, attrs) {
var watchMe = attrs["ngModel"];
scope.$watch(watchMe, function(newValue,oldValue) {
if(newValue == undefined || newValue == null || newValue == ""){
return;
} else if (isNaN(newValue)) {
var myVal = watchMe.split(".");
switch (myVal.length) {
case 1:
scope[myVal[0]] = oldValue;
break;
case 2:
scope[myVal[0]][myVal[1]] = oldValue;
}
}
});
}
};
});