AngularJS clear validation $error after input's changing - angularjs

Updated question with fiddle.
Original is here: https://stackoverflow.com/questions/31874313/angularjs-clean-remote-validation-error-after-change-input
In my form I have two validations. First is local, second is remote.
So this is my example
<form ng-controller="MyCtrl" name="Form">
<div class="form-group">
<label class="control-label">
First Name
</label>
<input type="text" class="form-control" name="firstName" ng-model="myModel.firstName" required />
<span class="error" ng-if="Form.firstName.$dirty && Form.firstName.$invalid" ng-repeat="(e, b) in Form.firstName.$error">{{e}}</span>
</div>
<input type="submit" ng-click="submit(Form)">
</form>
Here is Controller
function MyCtrl($scope, $element) {
$scope.submit = function (form) {
if (form.$invalid) {
renderErrors(form);
return;
}
console.log('local validation passed');
// imitation of remote error
// send, then data
if($scope.myModel.firstName === 'Tom')
renderServerErrors({firstName: ['Already in use']}, form);
else
alert('Success');
}
/**
* Errors will appear below each wrong input
*/
var renderErrors = function(form){
var field = null;
for (field in form) {
if (field[0] != '$') {
if (form[field].$pristine) {
form[field].$dirty = true;
}
}
}
};
/**
* Server errors will appear below each wrong input
*/
var renderServerErrors = function(err, form){
var field = null;
_.each(err, function(errors, key) {
_.each(errors, function(e) {
form[key].$dirty = true;
form[key].$setValidity(e, false);
});
});
}
}
http://jsfiddle.net/uwozaof9/6/
If you type 'Tom' into input - you will never submit form more..
And I want to delete server errors from input's error stack on it's change.
Please help!

It seems you only set invalid but don't set valid after it was corrected. IF you are doing yourself you also have to implement setting $valid if the imput is valid.

Related

Remove select options based on button click in angular js

In my app i have a select html which has following options
"Addition","Deletion","Duplicate","Member Duplicate"
Above drop down page is common for both add and edit screen. As of now if we come from any addition click or edit click drop-down has all options. (Note: drop-down binds at the time of loading page itself. we will show/hide depending on click)
As per new requirement I need to remove all other options except "Addition" in addition click and remove "Addition" option in edit click.
select html:
<select name="ReasonID" required ng-model="member.ReasonID" class="form-control" ng-options="reason.ID as reason.Description for reason in reasons |orderBy: reason.Description"></select>
Js
$scope.manageMember = function (member) {
$scope.showGrid = false;
$scope.showForm = true;
reset();
$scope.memberTemp = member;
angular.extend($scope.member, member); };
Please let me know if you need more details from my end.
Update :
Here the full sample code and working demo with dummy data.
HTML
<div ng-app>
<h2>Todo</h2>
<div ng-controller="TodoCtrl">
<select name="ReasonID" required ng-model="member.ReasonID" class="form-control" ng-options="reason.ID as reason.Description for reason in reasons |orderBy: reason.Description"></select>
<br/>
<input type="button" ng-click="manageMember(undefined)" value="add"/>
<input type="button" ng-click="manageMember('bla bla bla')" value="edit"/>
</div>
</div>
JS
function TodoCtrl($scope) {
$scope.reasons = [{ID:1,Description :"Addition"}, {ID:2,Description :"Deletion"},{ID:3,Description :"Duplicate"},{ID:4,Description :"Member Duplicate"}];
var reasonsTemp =angular.copy($scope.reasons);
$scope.manageMember = function (member) {
console.log(reasonsTemp)
$scope.reasons=reasonsTemp;// assign global object to model
$scope.showGrid = false;
$scope.showForm = true;
$scope.memberTemp = member;
var EditArray=[];
for(var i = 0 ; $scope.reasons.length>i;i++)
{
if($scope.reasons[i].Description === ($scope.memberTemp == undefined ? "Addition" : "bla bla bla"))// condition for is this addition or not
{
EditArray = $scope.reasons[i];
break;
}
else // if is it not addition, then addition only offect that object. because we were already assigned original value globally
{
if($scope.reasons[i].Description!=="Addition")
{
EditArray.push($scope.reasons[i])
}
}
}
$scope.reasons=EditArray;
console.log($scope.reasons);
}
}
Working Demo On console window
Try this,
HTML
<select ng-model="selectedOption">
<option ng-show="reason.show" ng-repeat="reason.ID as reason.Description for reason in reasons |orderBy: reason.Description">{{reason.ID}}</option>
</select>
JS
$scope.manageMember = function (member) {
$scope.showGrid = false;
$scope.showForm = true;
reset();
$scope.memberTemp = member;
angular.extend($scope.member, member);
if(member){
for(var i = 0 ; $scope.reasons.length>i;i++)
{
$scope.reasons[i].show = true;
if($scope.reasons[i].ID == "Addition"){$scope.reasons[i].show = false;}
}
}else{
for(var i = 0 ; $scope.reasons.length>i;i++)
{
$scope.reasons[i].show = false;
if($scope.reasons[i].ID == "Addition"){$scope.reasons[i].show = true;}
}
}
}
Suppose you have two buttons as,
<input type="button" ng-click="toAdd=true">Add</input>
<input type="button" ng-click="toAdd=false">Edit</input>
And the select box code should be like,
<select ng-model="selectedOption">
<option ng-show="toAdd">Addition</option>
<option ng-show="!toAdd">Deletion</option>
<option ng-show="!toAdd">Duplicate</option>
<option ng-show="!toAdd">Member Duplicate</option>
</select>
Hope this helps.

parsley.js: issue with custom validator

I have a custom validator as shown below...
window.Parsley
.addValidator('invalidwords', {
requirementType: 'regexp',
validateString: function(value, requirement) {
var wordval = value.split(" ");
$.each(wordval,function(idx,item) {
return !/^\b(?:Stadium|GT|BB|HB|Simul|VNOSE|LT|combination|LT1|SSGT|BW|HBS|simul|combo|2hbs|4d|lt2|theatre)\b$/i.test(item)
});
},
messages: {
en: 'Invalid words detected.'
}
});
Basically what I want is to check a string to see if it contains any of the words in my regex.
Before I added the each() function it would work for single words, but it wouldn't work when i entered in something like gt lt so I had to put them in an array and check each one.
It appears to work when I debug as it does return false, but it seems as though parsley isn't seeing it or something to that effect.
Here is how I am calling it...
<div class="col-sm-6 col-lg-6">
<div class="form-group">
<input type="text" name="company" data-parsley-group="eventinfo" id="Company" class="form-control" placeholder="Company" maxlength="60" tabindex="3" title="Company" parsley-trigger="keyup" data-parsley-invalidwords="" value="#request.args.company#">
</div>
</div>
I also tried changing requirementType: 'regexp', to requirementType: 'string',
Got it figured out. Had to do the return outside the loop. here is my final code.
window.Parsley
.addValidator('invalidwords', {
requirementType: 'regexp',
validateString: function(value, requirement) {
var wordval = value.split(" ");
var valid = true;
$.each(wordval,function(idx,item) {
console.log(item,/^\b(?:Stadium|GT|BB|HB|Simul|VNOSE|LT|combination|LT1|SSGT|BW|HBS|simul|combo|2hbs|4d|lt2|theatre)\b$/i.test(item));
if(/^\b(?:Stadium|GT|BB|HB|Simul|VNOSE|LT|combination|LT1|SSGT|BW|HBS|simul|combo|2hbs|4d|lt2|theatre)\b$/i.test($.trim(item))){
valid = false;
}
});
return valid;
},
messages: {
en: 'Invalid words detected.'
}
});

Submit validation in anjularjs

Let say I have the following codes:-
<form name="table" ng-submit="createtable()">
<input type="number" ng-model="tab.num" required></input>{{win.numR}}
<button>Save</button>
</form>
I will be adding number in this order(1,2,3,4,5,6). What I want to achieve is e.g.
I have input 1,2, and then when I input 6 it prevents me from adding the 6 because I need to add the 3, the 4 and the 5 before the 6.
thanks for the help.
Here's a full Plunkr to help you out.
http://plnkr.co/edit/1GK1JjFLoCJQd4K3l6eh?p=preview
I am using ui-validate to simplify. I suggest using this module to simplify your validation code.
var application = angular.module("validator", ["ui.validate"]);
application.controller("ValidatorExampleController", ['$scope', function($scope) {
$scope.numberStationValidationFn = function(value) {
if(angular.isUndefined(value)) {
return true;
}
for(var i = 1; i <= value.length; i++) {
if(value[i - 1] != i) {
return false;
}
}
return true;
};
}]);
Add ng-valid attribute to your input and implement a method which will set the input valid to either true or false:
<input type="number" ng-model="tab.num" ng-valid="inputIsValid(tab.num)" required>
In your controller:
$scope.inputIsValid = function(str) {
// check if str is valid and return true or false
}

How can I cancel a form submit from an angularjs directive

I am creating a validation directive that shows a message when form fields are invalid in a form. I would like to show the message and cancel the submit if the fields are not valid.
I have succeeded in showing the validation messages on submit by requiring the ngModel and form controllers but then I can't seem to find a way to use the FormController to cancel the form submit.
I have prepared a plunker here with my issue.
As you can see, it shows the error but I can't prevent the submit function from firing.
// Code goes here
var directiveName = "fcValidate";
angular.module("app", [])
.directive(directiveName, ["$timeout", validatorDirective])
.controller("PageCtrl", [pageCtrl]);
function validatorDirective($timeout) {
return {
restrict: "A",
require: ["^ngModel", "?^form"],
link: link
};
function link(scope, element, attributes, controllers) {
var modelCtrl = controllers[0];
var formCtrl = controllers[1];
// Validation.
$timeout(run);
function run() {
var requiredMessage = "Please enter the %(field)s.",
minLengthMessage = "Sorry, but the %(field)s cannot be shorter than %(minLength)s characters.",
maxLengthMessage = "Sorry, but the %(field)s cannot be longer than %(maxLength)s characters.",
minValueMessage = "Sorry, but the %(field)s cannot be less than %(min)s.",
maxValueMessage = "Sorry, but the %(field)s cannot be greater than %(max)s.",
invalidNumberMessage = "Please ensure that the %(field)s is a valid number.";
var content = null;
var field = attributes.name;
if (!field) {
return;
}
var toWatch = function () {
if (formCtrl) {
return formCtrl.$submitted;
}
return modelCtrl.$error;
};
scope.$watchCollection(toWatch, function (newValues, oldValues) {
var error = modelCtrl["$error"];
var invalid = modelCtrl["$invalid"];
var dirty = modelCtrl["$dirty"];
if ((formCtrl && !formCtrl.$submitted) || (!formCtrl && (_.keys(newValues) === _.keys(oldValues))) || !invalid || !dirty) {
return;
}
var msgTpl = null;
var fieldName = attributes[directiveName];
if (fieldName) {
fieldName = fieldName.toLowerCase();
}
if (error.required) {
msgTpl = requiredMessage;
} else if (error.minlength) {
msgTpl = minLengthMessage;
} else if (error.maxlength){
msgTpl = maxLengthMessage;
} else if (error.min) {
msgTpl = minValueMessage;
} else if (error.max){
msgTpl = maxValueMessage;
} else if (error.number) {
msgTpl = invalidNumberMessage;
}
if (fieldName) {
var data = {
field: fieldName || "",
min: attributes.min,
max: attributes.max,
minLength: attributes.minlength,
maxLength: attributes.maxlength
};
if (msgTpl) {
content = _.string.sprintf(msgTpl, data);
} else {
content = fieldName;
}
}
// Show message...
alert(content);
// Cancel the form submit here...
});
}
}
}
function pageCtrl() {
var vm = this;
vm.user = {};
vm.submit = submit;
function submit() {
console.log(vm.user);
}
}
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.string/2.3.0/underscore.string.min.js"></script>
<script src="https://code.angularjs.org/1.3.5/angular.js"></script>
<body data-ng-app="app" data-ng-controller="PageCtrl as vm">
<form data-ng-submit="vm.submit()">
<input type="text" name="firstName" required="" minlength="2" placeholder="First Name" data-ng-model="vm.user.firstName" data-fc-validate="First Name" />
<button type="submit">Submit</button>
</form>
</body>
My question is, how can I cancel the form submit? Any help will be appreciated.
if it is like in jQuery, if you bind the function to a "submit" event you only need to return false in the callback.
Is disabling submit button enough for you? If yes, define name of form and then use it in ng-disabled on submit button
<form data-ng-submit="vm.submit()" name="validatingForm">
<input type="text" name="firstName" required="" minlength="2" placeholder="First Name" data-ng-model="vm.user.firstName" data-fc-validate="First Name" />
<button type="submit" ng-disabled="validatingForm.$invalid">Submit</button>
</form>
Also I think ng-messages can be usefull for your validation messages - https://docs.angularjs.org/api/ngMessages/directive/ngMessages

Need to require only one of a group of fields with Parsley

I am using Parsley.js for validating a form submission on a project. One of my needs is to have Parsley require that at least one of three fields have data in them, and only fail validation if none of the three fields has data.
I am not sure from the documentation, how to accomplish this. I already have Parsley validation working on the rest of the form.
You can do that with a custom validator like so
var CheckReccursion = 0;
window.Parsley.addValidator('min3', {
validateString: function (value, requirement, instance) {
var notice =$('#notice').html(' ');
var group = $(requirement);//a class
var FieldsEmpty = 0;
var FieldsNotEmpty = 0;
var count = 0
group.each(function () {
var _val = $(this).val()
var length = _val.length
if (length > 0) {
FieldsNotEmpty++;
}
else {
FieldsEmpty++;
}
count++;
})
var isValid = (FieldsNotEmpty >=1)
//recursively execute
group.each(function (index) {
if (CheckReccursion === index) {
CheckReccursion++;
$(this).parsley().validate();
CheckReccursion = 0;
}
})
return isValid;
}
});
$(function () {
var ok=false;
var notice =$('#notice');
$('#form1').parsley().on('form:validated', function(formInstance) {
ok = formInstance.isValid({force: true});
})
.on('form:submit', function() {
if(!ok){
notice.html('Please fill at least 1 field');
return false;
}
else{
notice.html('okay');
return false;//change to true to submit form here
}
});
});
then add parsley attributes to the group of fields like so:
<form id="form1" data-parsley-validate="true">
<input type="text" name="field1"
data-parsley-min3 = ".group1"
data-parsley-min3-message = "At least 1 must be filled"
class="group1">
<input type="text" name="field2"
data-parsley-min3 = ".group1"
data-parsley-min3-message = "At least 1 must be filled"
class="group1">
<input type="text" name="field3"
data-parsley-min3 = ".group1"
data-parsley-min3-message = "At least 1 must be filled"
class="group1">
<span id="notice"></span>
<input type="submit" value="Submit">
</form>
Check out this fiddle https://jsfiddle.net/xcoL5Lur/6/
My advice is to add hidden checkbox element with the attribute:
data-parsley-mincheck="1"
now just add javascript code that checks the hidden checkbox attribute when your form input has value (and the opposite).
notice that you will need to add extra attribute to your hidden checkbox:
data-parsley-error-message="Please fill at least one input"
Another approach is to using data-parsley-group and the isValid({group,force}) method.
<input type="text" name="input1" data-parsley-group="group1">
<input type="text" name="input2" data-parsley-group="group2">
<input type="text" name="input3" data-parsley-group="group3">
$('#myform').parsley().on('form:validate', function (formInstance) {
if(formInstance.isValid({group: 'group1', force: true}) ||
formInstance.isValid({group: 'group2', force: true}) ||
formInstance.isValid({group: 'group3', force: true})) {
//do nothing
}
else {
$('#errorContainer').html('You must correctly fill at least one of these three groups!');
formInstance.validationResult = false;
}
});
you can add as many as parsley's attributes as you wish, like data-parsley-type="email" that will be validated when the given input is not empty.
we set the force: true because it it forces validation even on non-required.fields.
the html render for the errorContainer is needed because the isValid method does not affect UI nor fires events.

Resources