I cannot figure out how can I count form fields which are valid:
For template i'm using common code:
<p class="input-group" ng-repeat="pomiar in pomiary track by $index">
<input type="number" class="form-control" ng-model="pomiar" is-float/>
<span class="input-group-btn">
<input class="btn btn-danger" ng-click="usunPomiar($index)"
type="button" value="X">
</span>
</p>
Validation is done via directive :
.directive('isFloat', function ($filter) {
var FLOAT_REGEXP_3 = /^\$?\d+(\.\d*)?$/; //Numbers like: 1123.56
var FLOAT_REGEXP_4 = /^\$?\d+(\,\d*)?$/; //Numbers like: 1123,56
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
if (FLOAT_REGEXP_3.test(viewValue)) {
ctrl.$setValidity('float', true);
return parseFloat(viewValue);
} else if (FLOAT_REGEXP_4.test(viewValue)) {
ctrl.$setValidity('float', true);
return parseFloat(viewValue.replace(',', '.'));
}else {
ctrl.$setValidity('float', false);
return undefined;
}
});
ctrl.$formatters.unshift(
function (modelValue) {
return $filter('number')(parseFloat(modelValue) , 2);
}
);
}
};
});
I would like to display nuber of fields(inputs) in the template or store it in variable.
No problem if input isn't array and name attribute 'pomiar' is set:
{{myForm.pomiar.$valid}}
Do I have to write function in controller to validate it once again, or is there simpler way?
Related
I'm trying to implement an asynchronous validation by using a custom directive.
This is the directive
moduloArea.directive('uniqueName', function($http, $q) {
return {
require : 'ngModel',
link: function($scope, element, attrs, ngModel) {
ngModel.$asyncValidators.nombre = function(modelValue, viewValue) {
return $http.get('/checkUsernameAvailability/'+viewValue).then(
function(response) {
if (!response.data.validUsername) {
return $q.reject(response.data.errorMessage);
}
return true;
}
);
};
}
};
});
The result in console
As you can see when the root username is typed the return is an JSON object because this username is already taken.
But in the HTML the form in $invalid when the directive unique-name is inserted.
<form name="registerUsernameForm" novalidate="novalidate">
<input type="text" name="username" data-ng-model="person.userName" data-unique-name="" required="required"/>
<span data-ng-show="registerUsernameForm.username.$error.uniqueName">This username is already taken.</span>
<button type="submit" data-ng-disabled="registerUsernameForm.$invalid || registerUsernameForm.$pending" data-ng-click="registerPerson(person)"> Save Person </button>
</form>
I'm using the data-unique-name="" (="") because if I don't then thymeleaf generate the following error:
Attribute name "data-unique-name" associated with an element type
"input" must be followed by the ' = ' character.
What do you think can be wrong?
You are validating nombre not uniqueName. For this reason, uniqueName is still stay invalid.
moduloArea.directive('uniqueName', function($http, $q) {
return {
require : 'ngModel',
link: function($scope, element, attrs, ngModel) {
ngModel.$asyncValidators.uniqueName= function(modelValue, viewValue) {
var value = modelValue || viewValue;
return $http.get('/checkUsernameAvailability/'+value ).then(
function resolved(response) {
if (response.data && !response.data.validUsername) {
return $q.reject(response.data.errorMessage);
}
return true;
}, function rejected() {
//username does not exist, therefore this validation passes
return true;
}
);
};
}
};
});
I have a directive that validates all the fields of a form that contain the "validate" directive. In all fields the word "logan" must exist to be correct. At any given time I want and need, when I click on the "Re_Evaluate all the form" button, validate if the form is correct, iterating in all the elements of the form, just like when the $timeout function is executed the first time .
app.directive('validate', function ($timeout,$compile) {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel){
return;
}
$timeout(function() {
console.log(ngModel.$viewValue); //I need this, iterate in all elements with the directive 'validate', when re_evaluate() is clicked
if(ngModel.$viewValue=='logan'){
ngModel.$setValidity('validate',true);
}
else{
ngModel.$setValidity('validate',false);
}
})
scope.re_evaluate = function(){
console.log('I need re-evaluate all the form!')
}
}
};
});
http://jsfiddle.net/hmk0yg42/
The HTML is
<section ng-app="app">
<article ng-controller="appCtrl" class="container">
<form class="form-horizontal well" name="zform" invalidate>
<input type="text" name="mytext1" ng-model="my_text" validate />
<input type="text" name="mytext2" ng-model="my_text2" validate />
<input type="text" name="mytext3" ng-model="my_text3" validate />
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-disabled="zform.$invalid">
Validate</button>
</form>
</article>
</section>
And the JS is
var app = angular.module('app', []);
app.controller('appCtrl', function ($scope) {
$scope.my_text='logan';
$scope.my_text2 = 'logan';
$scope.my_text3 = 'logan';
});
app.directive('validate', function () {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$parsers.unshift(function(value) {
var valid = value == 'logan';
ngModelCtrl.$setValidity('logan',valid);
return value;
});
ngModelCtrl.$formatters.unshift(function(value) {
var valid = value == 'logan';
ngModelCtrl.$setValidity('logan', valid);
return value;
});
}
};
});
you don't need a re-evaluate button
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.
I have this directive that prepends an <i> element before each <input> element with add-icon-element attribute. Now what i'm trying to do is to observe validation for each <input> element, so when the user types something in one of them the class of the <i> element that precedes will change to fa-check green-i. I tried to do it by using attrs.$observe to see when the class changes from ng-invalid to ng-valid but it fires only one time when the DOM is structured, it doesn't react to changes in the input element.
What am i doing wrong? is there a way to do it using the input $valid?
I saw some answers regarding one input with suggestions to add names to the form and input - but what can I do if I have multiple inputs that I need to validate and not just one?
angular.module('mean.theme')
.directive("addIconElement", function () {
return {
restrict: 'EA',
link: function (scope, inputElement, attrs) {
var $icon = $('<i class="fa"></i>');
inputElement.before($icon);
attrs.$observe('class', function(val){
if (val.indexOf("ng-valid") >= 0) {
inputElement.prev().addClass('fa-check green-i');
}
});
}
};
});
and this is one of my 'inputs' in the html:
<form role="form" name="createProjectForm" class="form-validation">
<div class="form-group">
<label class="control-label text-center center-block" for="project.name">
Name Your Project
</label>
<div class="input-icon right">
<input type="text" placeholder="type here" name="project.name"
ng-model="project.name" required="required" class="form-control" add-icon-element/>
</div>
</div>
<form>
You don't need create a directive for such case, you could achieve this by using ng-class directive, only change your field name from name="project.name" to name="project_name"
<div class="input-icon right">
<input type="text" placeholder="type here" name="project.name"
ng-class="{'fa-check green-i': createProjectForm.project_name.$valid}"
ng-model="project.name" required="required"
class="form-control"/>
</div>
Update
To make it generic way, you need to require ngModel directive on that element, which will give you access to the ngModelController.
angular.module('mean.theme')
.directive("addIconElement", function () {
return {
restrict: 'EA',
require: 'ngModel', //require to get access to `ngModelController`
link: function (scope, inputElement, attrs, ngModelCtrl) {
var $icon = $('<i class="fa"></i>');
inputElement.before($icon);
scope.$watch(function(){
return ngModelCtrl.$valid; //made watch on field validation.
}, function(val){
if (val.indexOf("ng-valid") >= 0) {
inputElement.prev().addClass('fa-check green-i');
}
});
}
};
});
I think this will do it for you:
https://stackoverflow.com/a/23871934/1636157
angular.module('mean.theme')
.directive("addIconElement", function () {
return {
restrict: 'EA',
require: '^form',
link: function (scope, inputElement, attrs, ctrl) {
var $icon = $('<i class="fa"></i>');
inputElement.before($icon);
scope.$watch(ctrl.$name + '.' + inputElement.attr('name') + '.$valid', function (valid) {
if(valid) {
inputElement.prev().addClass('fa-check green-i');
} else {
inputElement.prev().removeClass('fa-check green-i');
}
});
}
};
});
I have an input with a link directive. My question is: is it possible to detect within the link function in the directive when the input loses focus?
Here is my directive:
appDrct.directive('formattedDate', ['$filter', 'formatter', function ($filter, formatter) {
return {
link: function (scope, element, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
var val = element.val();
if(!val)
return viewValue;
var dateStr = $filter('date')(val,'dd.MM.yyyy');
if(dateStr == undefined)
return viewValue;
var parsed = viewValue;
if(**BLURRED**){
do something....
}
try
{
var dateParts = dateStr.split('.');
parsed = new Date(dateParts[2],dateParts[1]-1,dateParts[0]);
}
catch(e){
}
return parsed;
});
},
restrict: 'A',
require: 'ngModel'
};
}]);
JADE:
input.form-control(formatted-date='mybirth', name='birth' ,placeholder='jj.mm.aaaa', type="text", datepicker-popup="dd.MM.yyyy", ng-model="mybirth", is-open="$parent.opened[0]", close-text="Close")
Plunker of an example:
http://plnkr.co/edit/t9ERna33YcG8ubDqDPgy?p=preview
You may also use element.on('blur', yourFn) in the link function
You can use ng-blur directive please see here http://plnkr.co/edit/Umx2czSRtgh3Cb2rKXYf?p=preview
<p class="input-group">
<input id="birth" formattedDate="mybirth" placeholder="dd.mm.yyyy" type="text" datepicker-popup="dd.MM.yyyy"
ng-model="mybirth" is-open="opened"
ng-blur="action()"
close-text="Close" class="form-control"/><span class="input-group-btn">
<button type="button" ng-click="open($event)" class="btn btn-default"><i class="glyphicon glyphicon-calendar"></i></button></span>
</p>