Angular validate only when input has data - angularjs

I am using angular 1.4 with ng-pattern and a custom async validator.
I have 2 fields with validation, one is required and the other is not. I need to validate the second filed WHEN it DOES have something in it. Issue is that I cannot submit the form when the second field's validators do not pass.
Is there an elegant way of doing this?
Thanks
<input
name="name"
type="text"
ng-model="model.software.name"
required>
<div ng-messages="myForm.myInput.$error" ng-if="myForm.name.$touched && myForm.name.$invalid">
<p ng-message="required">Required warning</p>
</div>
<input
name="myInput"
type="text"
ng-model="myInput"
ng-pattern="/someregex/"
async-validator>
<div ng-if="myForm.myInput.$pending">PENDING</div>
<div ng-messages="myForm.myInput.$error" ng-if="myForm.myInput.$touched && myForm.myInput.$invalid">
<p ng-message="pattern">Pattern warning</p>
<p ng-message="asyncValidator">async warning</p>
</div>
<button ng-click="process()" ng-disabled="myForm.$invalid">Process</button>
And here is my validator:
app.directive('asyncValidator', ['$http', '$q', function ($http, $q) {
return {
require : 'ngModel',
link : function (scope, element, attrs, ngModel) {
ngModel.$asyncValidators.asyncValidator = function (modelValue, viewValue) {
return $http.post('path', {value : viewValue}).then(
function (response) {
if (not_good) {
return $q.reject(not_good);
}
return true;
}
);
};
}
};
}]);

After a bit more digging I found that it was my asyncvalidator that was not allowing blank values. This works, but if there is better way please let me know.
Thanks Anid and to everyone.
The adjusted validator:
app.directive('asyncValidator', ['$http', '$q', function ($http, $q) {
return {
require : 'ngModel',
link : function (scope, element, attrs, ngModel) {
ngModel.$asyncValidators.asyncValidator = function (modelValue, viewValue) {
if (ngModel.$isEmpty(viewValue)) {
return $q.resolve();
}
return $http.post('path', {value : viewValue}).then(
function (response) {
if (not_good) {
return $q.reject(not_good);
}
return true;
}
);
};
}
};
}]);

Related

Using $resource with $asyncValidators in angularjs

This is what I wrote but angular keeps complaining about the $asyncValidators telling me is {}. I do not want to work with $http nor with $q. This is the faulty code I wrote:
.directive('checkEmail', ['toolBox', function(toolBox){
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel){
ngModel.$asyncValidators.emailExists = function(userEmail){
var promise = toolBox.checkEmailExist(userEmail);
return promise.get(function success(res){
return true;
}, function error(res){
return false;
});
};
}
};
}])
Has any of you worked validation with $resource? Where I get it wrong? toolBox.checkEmailExist(userEmail) comes from a service that looks like this
angular.module('toolBoxService', ['ngResource'])
.factory('toolBox', ['$resource','$log', function($resource, $log){
var dataObj = {};
dataObj.checkEmailExist = function(email){
return $resource('api/users/email', {email: email});
};
return dataObj;
}]);
and the form element looks like
<!-- Email field -->
<div class="input-group">
<span class="input-group-addon" id="userEmail">*</span>
<label class="control-label" for="userEmailField"></label>
<input class="form-control"
name="userEmail"
id="userEmailField"
required=""
placeholder="Email"
type="email"
ng-model="data.email"
ng-model-options="{ debounce: { default : 300, blur: 0 }}"
check-email>
</input>
</div>
<!-- Validation of the email -->
<div class="help-block form-error-messages"
ng-messages="registerForm.userEmail.$error"
ng-show="registerForm.userEmail.$touched"
role="alert"
ng-messages-multiple>
<div ng-messages-include="FACETS/errors/errorMessages.html"></div>
</div>
The only solution I've came up with is this
// checking for email in the database
.directive('checkEmail', ['toolBox', function(toolBox) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$validators.checkEmail = function(modelValue, viewValue){
var currentVal = modelValue || viewValue;
toolBox.checkEmailExist(currentVal)
.get(function success(resp){
if(resp.email == currentVal) {
ngModel.$setValidity('checkEmail', false);
} else {
ngModel.$setValidity('checkEmail', true);
}
});
};
} // end link
}; // end return
}])
$resource is not working with the $asyncValidators unfortunately. Took me 2 days to realise this. I hope there was an answer, a more expert voice on the matter.
It might be that you have to return the $promise attached to the resource instance object, rather than the object itself.
If your api returns an OK 200 response when the username does exist and an error status (e.g. 404 Not Found) when it does not, then the following approach works:
.directive('usernameExists', ['$resource', function ($resource) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ngModel) {
ngModel.$asyncValidators.usernameExists = function (userName) {
return $resource('/api/Account/:userName')
.get({ userName: userName }).$promise;
};
}
}
}])
I can solve it, it was matter of playing a bit more with promises
.directive('validateNbr',['$q','PreavisoService', function($q, PreavisoService) {
return {
require: 'ngModel',
restrict: '',
link: function(scope, elm, attrs, ngModel) {
ngModel.$asyncValidators.validateNbr = function(modelValue){
var def = $q.defer();
PreavisoService.checkUnitDigit({nbr:ngModel.$viewValue}).$promise.then(
function(result){
if(result.isValid === false){
def.reject();
}else{
def.resolve();
}
});
return def.promise;
};
}
};
}]);
hope it helps

How to show data returned by AngularJs $asyncValidator

What would be the best way to show data returned by $asyncValidator when form field is valid?
I can show an error besides the input field with ngMessage but would also like to show the resulting response from Restangular when validation passes.
'use strict';
angular.module('app')
.directive('productionOrder', function(Restangular) {
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attributes, ngModel) {
ngModel.$asyncValidators.exists = function(modelValue) {
return Restangular.one('production_orders', modelValue).get();
}
}
};
});
Html
<form name="workOrderForm">
<input type="text" name="productionOrderNo" ng-model="work_order.work_order.production_order_no" required production-order ng-model-options="{ debounce: 1000 }">
<div ng-messages="workOrderForm.productionOrderNo.$error">
<div ng-message="required">Production order no is mandatory</div>
<div ng-message="exists">Production order not found</div>
</div>
</form>
I'm sure this breaks at least a couple of best practises but this is how I got it working for now.
angular.module('oeeMaster')
.directive('productionOrder', function($q, Restangular) {
return {
restrict: "A",
require: "ngModel",
link: function($scope, element, attributes, ngModel) {
ngModel.$asyncValidators.exists = function(modelValue) {
var deferred = $q.defer();
Restangular.one('production_orders', modelValue).get().then(
function(response) {
ngModel.part_no = response.part_no;
deferred.resolve(response);
}, function() {
ngModel.part_no = null;
deferred.reject();
}
);
return deferred.promise;
}
}
};
});
HTML
<div ng-if="workOrderForm.productionOrderNo.part_no">Part number: {{ workOrderForm.productionOrderNo.part_no }}</div>

Same directive to multiple inputs. it's possible? AngularJS

I hope u can help me.
I have a directive:
.directive('checkField', ['$http', function($http) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, ele, attrs, c) {
scope.$watch(function() {
if (attrs.ngModel === 'data.gender_value' && ele.val() !== '') {
//valid
} else {
//error
}
if (attrs.ngModel === 'data.cardholder_value' && ele.val() !== '') {
//valid
} else {
//error
}
});
},
template: ''
};
}])
And i have multiple inputs in my html:
<input ng-model="data.cardholder_value" type="text" size="50" data-check-field />
<input ng-model="data.gender_value" type="text" ng-required="true" data-check-field />
The problem is that watch trigger only "see" the first input, no more.
I'm trying to use de same directive to multiple inputs, but doesn't work. If i do an alert, to check the attribute name of field, always display "data.cardholder_value", never other name field.
Thank u in advance.
Edit 1:
This is my html calling (ng-include):
<form method="post" id="formQuestion" name="formQuestion" ng-submit="sendForm()" novalidate ng-controller="questionForm">
{{data | json}}
<div class="slide-animate" ng-include="'/templates/default/partials/_fields/1_card_type.html'"></div>
<div class="slide-animate" ng-include="'/templates/default/partials/_fields/2_gender.html'"></div>
My app controller:
angular.module('app.controllers')
.directive('checkField', ['$http', function($http) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, ele, attrs, ctrl) {
scope.$watch(attrs.ngModel, function(val) {
console.log(attrs.ngModel, attrs.name, val)
});
},
template: ''
};
}])
.controller('questionForm', ['$scope', '$http', 'fieldApiService', function ($scope, $http, fieldApiService) {
...
All you just need it to watch the value of ng-model directive attribute, right now you provided the watcher function as your validation function which mean when it sees function as first argument for the watch it will just only look for the return value from that function to determine if watch listener needs to run or not during every digest cycle.
scope.$watch(attrs.ngModel, function(val) {
if (!val) {
//valid
} else {
//error
}
});
Also remember if you want to catch the user entered values you can always use the existing $viewChangeListener property on ngmodel, it will avoid reuse of existing internal watcher and no need to explicitly create one.
c.$viewChangeListeners.push(function(){
console.log('viewChange', ctrl.$viewValue)
});
Demo
angular.module('app', []).directive('checkField', ['$http',
function($http) {
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, ele, attrs, ctrl) {
scope.$watch(attrs.ngModel, function(val) {
console.log(attrs.ngModel, attrs.name, val)
});
ctrl.$viewChangeListeners.push(function(){
console.log('viewChange', ctrl.$viewValue)
});
},
};
}
])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<input ng-model="data.cardholder_value" name="cardholder" type="text" size="50" data-check-field />
<input ng-model="data.gender_value" name="gender" type="text" ng-required="true" data-check-field />
</div>

How to pass validation error details to view

In a custom validation directive I ensure that the entered value does not conflict with others in the database. If it does, I would like to tell the user not only that there's a conflict, but also the name of the item it is conflicting with.
Storing these details in the scope would probably work, but doesn't seem right to me at all. Is there a better way?
Directive:
angular.module('myApp')
.directive('conflictcheck', function (myServer) {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
var conflict = myServer.getConflict(viewValue);
if (!conflict) {
ctrl.$setValidity('conflictcheck', true);
return viewValue;
} else {
ctrl.$setValidity('conflictcheck', false);
// pass additional info regarding the conflict here
return undefined;
}
});
}
};
});
View:
<form name="myform" action="#">
<input ng-model="foo" conflictcheck />
<div ng-if="myform.foo.$error.conflictcheck">
Error: Foo conflicts with XXXXXXXX!
</div>
</form>
Validation errors are handled by FormController so it's reasonable to keep all validation-related data inside the form object itself. This could be implemented by attaching arbitrary data to myform instance:
app.factory('myServer', function(){
return {
getConflict: function(viewValue){
var comparedAgainst = 'existing#email.com';
if(viewValue === comparedAgainst){
return comparedAgainst;
}
}
}
});
app
.directive('conflictcheck', function (myServer) {
return {
require: ['ngModel', '^form'],
link: function (scope, elm, attrs, ctrls) {
var ngModelCtrl = ctrls[0];
var ngFormCtrl = ctrls[1];
ngModelCtrl.$parsers.unshift(function (viewValue) {
// getConflict returns conflicting value
var conflict = myServer.getConflict(viewValue);
if (!conflict) {
ngModelCtrl.$setValidity('conflictcheck', true);
return viewValue;
} else {
ngModelCtrl.$setValidity('conflictcheck', false);
// We attach validation-specific data to arbitrary data property
ngFormCtrl.foo.data = {
conflictcheck: conflict
};
return undefined;
}
});
}
};
});
<form name="myform" novalidate>
Email:
<input ng-model="foo" name="foo" conflictcheck />
<div ng-show="myform.foo.$error.conflictcheck">
Error: Foo conflicts with {{myform.foo.data.conflictcheck}}!
</div>
</form>
Plunker

how to do live responsive custom validation of form fields in angular

I have a form field that I want validated against a rest api as the user types in the form field. Sort of like an auto-complete but an auto-verify instead. I've started to lay some of the code down in angular but not sure how to observe the input onchange in angular or how to set the inavlid property of things.
Here's what I have so far
app.directive('purchaseCode', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (CODE_REGEXP.test(viewValue)) {
//how do I get the $http context/scope to to an ajax request here?
$http.get('check/user.code').success(function(data) {
ctrl.$setValidity('purchase_code', data.is_valid);
});
return viewValue;
} else {
ctrl.$setValidity('purchase_code', false);
return undefined;
}
});
}
};
});
function Controller($scope, $routeParams, $http) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.isUnchanged = function(user) {
return angular.equals(user, $scope.master);
};
$scope.reset();
}
and the HTML
<div class="container" ng-app>
<form no-validate id="myform" name='form' action="/upload" method="POST" role="form" ng-controller="Controller">
<div class="form-group has-error">
<label class="control-label" for="purchase_code">Purchase code</label>
<input type="text" class="form-control" id="purchase_code" placeholder="purchase code"
ng-model="user.code"
purchase-code
name="code">
</div>
<button type="submit" class="btn btn-block btn-primary" ng-click="update(user)"
ng-disabled="form.$invalid || isUnchanged(user)">Submit</button>
</form>
</div>
What do I do to have it watch the event of the form field being changes. Also a bonus question: how do I keep it from showing the invalid/dirty state until it gets to be more than 3 characters in length?
I tried looking at the custom validator sample on http://docs.angularjs.org/guide/forms but it doesn't seem to work if you copy and paste their code in verbatim.
I just needed to pass in $http into the method
app.directive('purchaseCode', function($http) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (CODE_REGEXP.test(viewValue)) {
$http.get('check/user.code').success(function(data) {
ctrl.$setValidity('purchase_code', data.is_valid);
});
return viewValue;
} else {
ctrl.$setValidity('purchase_code', false);
return undefined;
}
});
}
};
});

Resources