parsley.js: issue with custom validator - parsley.js

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

Related

how to disable radio button dynamically in angularjs using ng-repeat

I have developing some code in Angular JS and i need to disable radio button based on previous selection or change in text box
in JS controller:
PPCO.cusGender = [ {
id : '1',
key : 'Male',
value : 'Male',
disable:false
}, {
id : '2',
key : 'Female',
value : 'Female',
disable:false
}, {
id : '3',
key : 'TG',
value : 'TG',
disable:false
}];
PPCO.changeapplicant = function() {
switch (PPCO.p_SALUTATION.toLowerCase().trim()) {
case 'mrs.':
case 'miss.':angular.forEach(PPCO.cusGender, function(val, key) {
if(val.key != 'Male')
{
val.disable = false;
}
});
break;
}
};
in HTML:
<input type="text" ng-model="PPCO.changeapplicant" class="color" ng-change="PPCO.changeapplicant()">
<label class="radio" ng-repeat="option in PPCO.cusGender">
<input type="radio" name="gender"
ng-model="PPCO.cusgendername" value="{{option.value}}"
ng-disabled="option.disable">
<i></i>
</label>
My question is i able change the "ng-disabled =true" value but it is not enabling again. How to make that
I have created this plnkr for this case: https://plnkr.co/edit/F4JZcf6Nm5Csbxbg
I think you have 2 errors happening at the same time:
You're iterating over one array. So, you don't need to use angular.forEach, you can use array.forEach
Also, most important, you're setting false when the element is mrs. or miss. and it's ok. BUT, you're not setting back to true. So, you will have to include one else clause like this:
if (['mrs.', 'miss.'].includes($scope.applicant.toLowerCase().trim())) {
$scope.cusGender.forEach(function(element) {
element.disable = element.key == 'Male';
});
} else {
$scope.cusGender.forEach(function(element) {
element.disable = false;
});
}
I think that would be all!

I push an object to my Array in mongoose, array only stores its length

I try to push an object into an Array in mongoose, but whenever i do it, it puts its length like this(mongoose, the attribute is schoolComment at the bottom), I am using mlab.com for database.
{
"_id": {
"$oid": "58e17ee3e24dfb1f70d76460"
},
"schoolName": "Koc Universitesi",
"schoolIlce": "Sariyer",
"schoolSehir": "Istanbul",
"schoolId": 981299,
"__v": 5,
"schoolComments": [
3
]
}
this is my code in node JS (The comments are not appearing in html because of this reason)
app.post('/comment', function(req, res){
if(req.session.user && req.session){
User.findOne({email: req.session.user.email}, function(err, user){
if(err) {
res.send('error');
}
if(user){
if(req.session.user.password === user.password){
var thisID = user.userid;
Universite.findOne({schoolName: req.body.collegeName}, function(err, college){
if(err) res.send('error');
if(college){
college.set({schoolComments: college.schoolComments.push({thisID: req.body.comment})}).save(function(err){
if(err){
res.render('errors', {error:'Error'});
}else{
res.locals.college = college;
res.locals.user = user;
res.render('universiteinfoUser');
}
});
}
});
}else{
res.render('login', {});
}
}
});
}
});
and this is HTML DOM Form for it. The comments are not appearing because of this reasons.
<form onkeypress="enterPress();" action="/comment" method="post">
<textarea maxlength="100" style="font-size: 25px;" name="comment" rows="3" cols="50" placeholder="Yorumunuzu yazin..."></textarea><br>
<input style="display: none; visibility: hidden;" type="text" name="collegeName" value="<%=college.schoolName%>"></input>
<button type="submit" name="commentSubmit">Comment Submit</button>
</form>
<div class="userCommentDisplay">
<ul>
<%college.schoolComments.forEach(function(item, i){%>
<%var k = college.schoolComments[i]%>
<%for(key in k){%>
<%if(key === user.userid){%>
<li><%=k[key]%> Same</li>
<%}else{%>
<li><%=k[key]%></li>
<%}%>
<%}%>
<%})%>
</ul>
</div>
You may want to use findOneAndUpdate and build your comment item dynamically (to set the dynamic field name) :
var item = {};
item[user.userid] = req.body.comment;
Universite.findOneAndUpdate({
schoolName: req.body.collegeName
}, {
$push: {
"schoolComments": item
}
}, { new: true }, function(err, college) {
if (err) {
res.render('errors', { error: 'Error' });
} else {
res.locals.college = college;
res.locals.user = user;
res.render('universiteinfoUser');
}
});
Note that I've aded { new: true } in order to return the modified document college instead of the unaltered one.
FYI, in your code, you have used JS method Array.prototype.push() that will return the new length of the array by using college.schoolComments.push

AngularJS clear validation $error after input's changing

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.

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.

Angular: Reinclude null values when filter parameter is empty

I have a pretty simple textbox filtering an ng-repeat on some unordered lis. When I add a value to the textbox the items with the null values are removed and do not return even when the textbox is cleared. I have an idea of why this is happening (the search object now has an empty property which doesn't match the nulls), but I cannot figure out how to solve the problem. I've tried to pop() the property off of the search object with no luck.
HTML:
<div ng-controller="ListCtrl">
<input type="text" ng-model="search.age" placeholder="Age"></input>
<ul>
<li ng-repeat="item in items | filter:search">
{{item.name}} - {{item.age}}
</li>
</ul>
</div>
JS:
function ListCtrl($scope) {
$scope.items = [
{'name':'Carl', 'age':69},
{'name':'Neil', 'age':54},
{'name':'Richard'},
{'name':'Chris', 'age':58}
];
}
Please checkout the JSfiddle to better illustrate the issue.
I figured it out with the help of this answer. If I just add an ng-change to the textbox I can watch for an empty value and delete the property.
HTML:
<input type="text" ng-model="search.age" ng-change="clear()" placeholder="Age"></input>
JS:
$scope.clear = function(){
if($scope.search.age.length == 0){
delete $scope.search.age;
}
}
Updated fiddle. I am aware the current if prevents a user from filtering on a single space, but so far this does not seem to cause a problem for me.
BONUS: ! will return all null values and !! will return all not null values.
The cleanest solution I have found is writing a custom directive to modify the input field behaviour like this:
app.directive('deleteIfEmpty', function () {
return {
restrict: 'A',
scope: {
ngModel: '='
},
link: function (scope, element, attrs) {
scope.$watch("ngModel", function (newValue, oldValue) {
if (typeof scope.ngModel !== 'undefined' && scope.ngModel.length === 0) {
delete scope.ngModel;
}
});
}
};
});
And use it as follows:
<input type="text" ng-model="filter" delete-if-empty>
Modify the input ng-model:
<input type="text" ng-model="searchObj.age" placeholder="Age"></input>
Add this to your controller:
$scope.searchObj = {
}
And either of these will work in your html repeat:
ng-repeat="item in items | filter: searchObj.age"
Or
ng-repeat="item in items | filter: {age: searchObj.age || undefined}"
jsfiddle
You won't be able to use filter:search. Looking at the Angular code, if your obj with an undefined age gets filtered (even when the input is empty) it will fall through this switch statement and always return false. This switch doesn't get called the first time your ng-repeat is run because $scope.search.age is undefined. After your first entry into the input and clearing it out, now $scope.search.age is an empty string...so the filter will always be run.
switch (typeof obj) { ***<-- obj is undefined when you have a missing age***
case "boolean":
case "number":
case "string":
return comparator(obj, text);
case "object":
switch (typeof text) {
case "object":
return comparator(obj, text);
default:
for ( var objKey in obj) {
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
return true;
}
}
break;
}
return false;
case "array":
for ( var i = 0; i < obj.length; i++) {
if (search(obj[i], text)) {
return true;
}
}
return false;
default:
return false; ***<--falls through and just returns false***
}
You can try writing your own filter function, something like this.
http://jsfiddle.net/wuqu2/
<div ng-controller="ListCtrl">
<input type="text" ng-model="search.age" placeholder="Age"></input>
<ul>
<li ng-repeat="item in items | filter:checkAge">
{{item.name}} - {{item.age}}
</li>
</ul>
</div>
$scope.checkAge = function(item)
{
if($scope.search && $scope.search.age && $scope.search.age.length > 0)
{
return item.age && item.age.toString().indexOf($scope.search.age) > -1;
}
return true;
}

Resources