AngularJS - Form changed to invalid when custom directive is inserted - javascript

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;
}
);
};
}
};
});

Related

Value of attribute in directive is always undefined

Here is my custom directive:
angular
.module('accountApp')
.directive('uniqueRecord', function($q, $timeout, $http) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$asyncValidators.uniqueRecord = function(modelValue, viewValue) {
var value = modelValue || viewValue;
var attributes = scope.$eval(attrs.uniqueRecord);
// Lookup effect by name
return $http.get(attributes.url + '/' + value + '/' + ((attributes.currentRecordName == '' || attributes.currentRecordName == 'nothing') ? '_' : attributes.currentRecordName))
.then(function resolved() {
//username exists, this means validation fails
return $q.reject('exists');
}, function rejected() {
//username does not exist, therefore this validation passes
return true;
});
};
}
}
});
Here is the HTML:
<input type="text" id="name" name="name" class="form-control form-input" ng-model="effect.name"
ng-disabled="isReadOnly" required
unique-record="{ url: '/api/effect', currentRecordName: {{currentEffectName == '' ? 'nothing' : currentEffectName}} }"
ng-uniqueRecord-err-type="duplicateRecord"/>
As you can see in the above HTML, I am passing the value of currentRecordName to directive. In directive the value of url is passed as is but the value of currentRecordName is always undefined. Why?
The problem comes from your utilisation of mustache in the evaled expression.
An evaled expression is already considered as an angular expression and does not need the mustache.
I suggest you to change your html to :
<input type="text" id="name" name="name" class="form-control form-input" ng-model="effect.name"
ng-disabled="isReadOnly" required
unique-record="{ url: '/api/effect', currentRecordName: currentEffectName }"
ng-uniqueRecord-err-type="duplicateRecord"/>
And handle the currentEffectName vs 'nothing' directly in the directive code.
var attributes = scope.$eval(attrs.uniqueRecord);
if (!attributes.currentRecordName) {
attributes.currentRecordName = 'nothing';
}

Custom directive not firing up

I have a directive that makes some value prices validation, and I thought that it was working well, until I notice a bug, for example if the next value price I'm validating in the input is the same of the previous one, the next one is not validated, I don't understand why, if it was different value it worked with no issues, but if the value is the same of the previous, the directive is not fired up, I have no idea if there is any relation with the '$asyncValidators', above I leave my custom directive.
Directive:
(function() {
'use strict';
angular
.module('myApp')
.directive('priceValidator', priceValidator);
priceValidator.$inject = ['$q', 'ServerValidatorService'];
/* #ngInject */
function priceValidator($q, ServerValidatorService) {
var directive = {
link: link,
restrict: 'A',
require: 'ngModel',
scope: {
costPrice: '=',
currentPvp: '='
}
};
return directive;
function link(scope, element, attrs, ctrl) {
ctrl.$asyncValidators.costPrice = function(costPrice) {
var pvp = scope.currentPvp;
scope.currentPrice = costPrice;
var cost = String(costPrice).replace(",", ".");
return ServerValidatorService.validateCostPrice(pvp, cost)
.then(function() {
return true;
},
function(response) {
ctrl.$setDirty();
if (response.data.errors.maximum[0])
scope.costPrice = response.data.errors.maximum[0];
return $q.reject(response.data.message);
});
};
}
}
})();
// Controller function that fill form with each product info:
function getMainProduct(productLine) {
if (typeof productLine !== "undefined") {
vm.discountId = productLine.discount_id;
vm.discountPercentage = productLine.discount_percentage;
vm.valuePrice = productLine.value_price;
}
}
HTML
<input type="text" class="form-control"
name="discount" ng-model="form.discPrice"
ng-model-options="{updateOn: 'default blur', debounce: {'default': 300, 'blur': 0} }"
ng-required="formCtrl.isP()" cost-price="form.maxP"
current-pvp="form.pvp" num-type="decimal"
ng-change="formCtrl.check()" price-validator>

Directive not called on input change

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

How can I count validated inputs (array) in Angular JS?

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?

AngularJs user validation making request for every characters

Hi i'm using an User Name validator in my project.But when I started writing some thing it's requesting for every characters after reach min length.
Here you can see in pic.
Here is my soruces;
Input parse in page:
<input type="text" id="regUserName" name="regUserName" required minlength="5" maxlength="15" class="form-control" placeholder="Kullanıcı Adı" data-ng-model="regUserName" username-validator />
And here validator in ValidationService.js:
angular.module('BlogAppModule').directive('usernameValidator', function ($http, $q) {
return {
require: 'ngModel',
link: function ($scope, element, attrs, ngModel) {
ngModel.$asyncValidators.usernameAvailable = function (username) {
return $http.get('/api/CheckUserName/' + username)
.then(function (response) {
var warning = document.getElementById("regUserName");
if (response.data != null) {
warning.style.backgroundColor = "red";
}
else {
warning.style.backgroundColor = "white";
}
})
}
}
};
});
Now here my some questions about this:
Will be a problem about this requests?(Like performance problem)
Can I set this validation like when I leave the textbox then check
username?If this is possible, what should I do?
When user write "." requesting don't work.How can I disable in
textinput special chars like : " . - , ! # " etc.
By default, changes will be written back to the model on every change.
Use ng-model-options="{ updateOn: 'blur' }" on the input element to update the binding when leaving the input field.
If you want to add client side validation as well, you can add a synchronous validator. $validators will be executed before $asyncValidatorss.
link: function ($scope, element, attrs, ngModel) {
ngModel.$asyncValidators.usernameAvailable = function (username) {
// your server side validation
};
ngModel.$validators.userNameOk = function(username) {
return username && username.indexOf('.') < 0;
};
}
http://plnkr.co/edit/Yd8C8Ut9VWPXEvDEMjFJ?p=preview

Categories