ParsleyJS Custom Math Validators - parsley.js

I've successfully adapted parts of ParsleyJS to my needs in my form. Now I need to make some custom math validators, but I am struggling to figure out why my validators are not working.
What I need to accomplish:
Input 1: x
Input 2: can not be more than 10 different from x
an example form:
<form id="demo-form" data-parsley-validate="">
<label for="input1">Input 1</label>
<input type="text" class="form-control" id="input1" name="input1" required>
<label for="input2">Input 2</label>
<input type="text" class="form-control" id="input2" name="input2" required data-parsley-not-more-than="input1" data-parsley-trigger="keyup" data-parsley-validation-threshold="0">
<input type="submit" class="btn btn-default pull-right">
</form>
from the custom validator demo example I figured it'd be solved pretty easily like so:
window.Parsley.addValidator('notMoreThan', {
validateNumber: function(value, requirement) {
return (value - requirement) <= 10;
},
requirementType: 'integer',
messages: {
en: 'This value can not be more than 10 different from %s'
}
});
but.. Uncaught Requirement is not a integer: "input1"
fine. we'll change to a string then.
window.Parsley.addValidator('notMoreThan', {
validateNumber: function(value, requirement) {
return (requirement - value) <= 10;
},
requirementType: 'string',
messages: {
en: 'This value can not be more than 10 different from %s'
}
});
but this always returns false, validation fails, whatever the input is.
then I look at the extras (greater than, less than or equal to) in the src/extra directory and they do this:
// gt, gte, lt, lte, notequalto extra validators
var parseRequirement = function (requirement) {
if (isNaN(+requirement))
return parseFloat(jQuery(requirement).val());
else
return +requirement;
};
// Greater than or equal to validator
window.Parsley.addValidator('gte', {
validateString: function (value, requirement) {
return parseFloat(value) >= parseRequirement(requirement);
},
priority: 32
});
So I guess, with my limited javascript knowledge, that they "interpret" the requirement value to return the right string if it's "not a number".
So, I try the same approach.
// gt, gte, lt, lte, notequalto extra validators
var parseRequirement = function (requirement) {
if (isNaN(+requirement))
return parseFloat(jQuery(requirement).val());
else
return +requirement;
};
// my not more than 10 different validator
window.Parsley.addValidator('notMoreThan', {
validateString: function (value, requirement) {
return ( parseFloat(value) - parseRequirement(requirement) ) <= 10;
},
priority: 32
});
But this always returns false as well.
I'm just at a complete loss. It may be my lack of javascript skills.. Hope this answer doesn't get downvoted for that. I'm trying to learn as best I can.
In conclusion, I need a solution to craft custom validators with simple math validations depending on fields. "Field B can be no more than X different from Field A" "Field D can not differ more than f from ((Field C divided by x) minus y)" and so on so on.
Hope someone can help me out.

I found out the solution, hope it helps people who face similar issues, as documentation is lacking for newbies in this regard.
window.Parsley.addValidator('notMoreThan', {
validateNumber: function(value, requirement) {
// requirement is passing the id of the input 1 so ve have to obtain the value of it
var requirement_value = $('#'+requirement).val() ;
return value - requirement_value <= 10 ;
},
requirementType: 'string',
messages: {
en: 'This value can not be more than 10 different from %s'
}
});
in order to make this work, we have to obtain the value of the requirement first. This is what was lacking.

Related

AngularJS form validation triggered by other fields

I have an angularjs application and have to do form validation with custom business rules.
The problem is that my validation rules for a particular input field is dependent on other fields, and I don't know how to trigger the validation other than when the actual modelvalue changes.
The case is a dynamic list of employees each with a dynamic list of times of day to be entered. One rule is that these times must not overlap, which means one value can be invalid due to another value being changed and vice-versa.
I also have to show an error message for each field.
The form content is generated from the datamodel with a few layers of nested repeaters.
I have made a custom directive that contains the different validation rules and it triggers nicely when that field changes.
I'm using ngMessages to show the appropriate errormessage based on what business rule is violated.
The question is, how do I trigger validation on all other fields, when one particular field is changed? Preferably I should just trigger validation of all fields for the employee, whos value is being changed, since the values for one employee doesn't affect validation of other employees.
The fiddle here has a simplified version of my case, where the "overlap" rule just checks if two numbers are the same.
The html:
<form name="demoForm">
<div ng-repeat="employee in list">
<div ng-bind="employee.name"></div>
<div ng-repeat="day in employee.days" ng-form="employeeForm">
<input ng-model="day.hours" name="hours" custom-validate="{day: day, days: employee.days}" ng-model-options="{allowInvalid:true}" />
<span ng-messages="employeeForm.hours.$error">
<span ng-message="number">Should be a number.</span>
<span ng-message="businessHours">The number is outside business hours.</span>
<span ng-message="max">The number is too large.</span>
<span ng-message="overlap">The number must be unique for each employee.</span>
</span>
</div>
<br/>
</div>
</form>
The validation directive:
angular.module('app').directive('customValidate', [validator]);
function validator() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
data: '=customValidate'
},
link: linkFunc,
};
function linkFunc(scope, element, attrs, ctrl) {
ctrl.$validators.number = function(value) {
return value === "" || Number.isInteger(+value);
}
ctrl.$validators.businessHours = function(value) {
// imagine other validation data here
return value === "" || (value >= 1 && value <= 10);
}
ctrl.$validators.overlap = function(value) {
if (value === "") {
return true;
}
// find all other entries with identical value excluding self
var identical = scope.data.days.filter(function(x) {
return x !== scope.data.day && +x.hours === +value;
});
return identical.length === 0;
};
}
}
Fiddle here:
http://jsfiddle.net/maxrawhawk/dvpjdjbv/
The answer:
This little piece of code in the end of the directives link function:
scope.$watch('data', function(){
ctrl.$validate();
}, true);
Watch the data related to validation given from the markup with the most important detail 'true' as third parameter, making $watch check for object equality.
Updated fiddle:
http://jsfiddle.net/maxrawhawk/dvpjdjbv/12/

ng-list length validation in AngularJS

I need validation for ng-list, means should restrict the user to enter more than three array list like ['121','565','435'] and if user tries to enter like ['121','565','435','787'] should give error like only 3 vin can enter.
Also if the user enter ['**1214**','565','435'] like above it should tell only 3 digits are allowed.
This is my input field:
<input type="text" name="vin" id="vin" class="form-control"
ng-model="vm.user.vin" ng-list required max-length="3"/>
I am new in AngularJS.
I don't know the default validate properties for ng-list, but you can customise the validation like
<input type="text" name="vin" id="vin" class="form-control"
ng-model="vm.user.vin" ng-list required max="3" ng-change="isBigEnough(vm.user.vin)" />
<span ng-show="vm.user.vin.length > 3">array length should below than three</span>
<span ng-show="IsElementLength">element length should below than three</span>
and your controller side code
$scope.isBigEnough = function (values) {
if (values != undefined) {
values.every(function (element, index, array) {
if (element.length > 3) {
$scope.IsElementLength = true;
return false;
}
$scope.IsElementLength = false;
return true;
})
}
}
Result
['121','565','435'] - No error message
['1211','565','435'] -element length should below than three
['121','565','435','543']- array length should below than three
['1211','565','435','543']- array length should below than three and element length should below than three
i have bring the code on plunker, but i did't test it on plunker.

AngularJS dependant inputs link to the same scope variable

I'm new to angularJS and I'm trying to design a form including 2 input fields which are internally tied to the same scope variable: saying the first field is the speed in km/h and the second field is the speed in m/s. The behavior I'd like to obtain is: whatever field the user changes the other field would be updated.
The problem I'm running into is the following:
When the speed in m/s is changed by the user, the speed in km/h is updated which lead to update again the speed in m/s, which update the speed in km/h until no value change. The side effect is when the speed in m/s is a round number saying 1 and the user change the value to make it decimal entering the decimal dot - the update loop lead to remove the dot because from a calculus point of view 1. is 1.
What is the best way to avoid that?
Here are the fragments of code (html):
<label for="SpeedKm">Speed (Km/h):</label>
<input id="SpeedKm" name="SpeedKm" type="text"
placeholder="km/h"
ng-model="speedKmh"
ng-model-options="{ getterSetter: true}">
>
<label for="SpeedMs">Speed (m/s):</label>
<input id="SpeedMs" name="SpeedMs" type="text"
placeholder="m/s"
ng-model="speedMs"
ng-model-options="{ getterSetter: true}">
>
and the controller
angular.module('speedconverter')
.controller('MainCtrl', ['$scope', function ($scope) {
$scope.speed = 10; //km/h
$scope.speedKmh = function(newValue) {
if (angular.isDefined(newValue)) {
$scope.speed = newValue;
}
return $scope.speed;
};
$scope.speedms = function(newValue) {
if (angular.isDefined(newValue)) {
$scope.speed = newValue*3.6;
}
return $scope.speed/3.6;
};
});
Thank you

Angular-UI typeahead show on certain character

I would like to use Angular-UI typeahead directive to do something like Twitter's Tweet composer: show it only when the user inputs a certain character, like # or {, and when a match is selected, append only the selected value, not replace the entire model.
Is it possible with the current Angular-UI implementation?
This is what I achieved using a custom directive:
http://plnkr.co/edit/9eEq6fOZgVWlhBqUXpV5?p=preview
All you need to do is:
<input type="text" ng-model="model"
typeahead-on="{" typeahead-append="}"
typeahead="suggestion for suggestion in ['second', 'minute', 'hour', 'day','week', 'month', 'year']" />
I don't think this is available out the box but the typeahead API allows custom get and format functions to be specified.
Here is a Plunker loosely based on the async example in the angular-ui documentation. My version only kicks in when an # symbol is present in the input value. A custom search is then carried out using the substring after the # sign.
HTML:
<input type="text" ng-model="selected" typeahead="address for address in getLocation($viewValue)" typeahead-input-formatter="formatResult($model)" class="form-control">
Controller:
var prefix = "";
$scope.getLocation = function(val) {
var pos = val.indexOf("#");
if(pos <=0 || pos == val.length-1) {
return [];
}
prefix = val.substr(0,pos); //cache the prefix
var search = val.substr(pos+1); //get the search string
//filter the results
return $filter('filter')(states, search)
};
$scope.formatResult = function(model) {
if(!model) {
return prefix;
}
return prefix + "#"+model;
}
UPDATE
Updated plunk which allows multiple tokens. You can use whatever token matching scheme you want here. This is just an example:
http://plnkr.co/edit/RjSZ2wgI1POtNfbQ6tvy?p=preview

AngularJS: SetValidity within an ng-repeat

I want to check that an input value is valid, (i.e. if someone is spending at least 25 cents on a candy from a list of candies), and if not, tag that input as $invalid, so the form doesn't get submitted.
<form novalidate name="candyForm">
<div ng-repeat="candy in candies">
<input name="candyCost" type="text" ng-model="candy.cost" required>
<div class="error" ng-show='checkEnoughMoney()'>You only have 1 dollar to spend</div>
<div class="error" id="candyError{{$index}}">You must spend at least 25c per candy</div>
</div>
</form>
The checkEnoughMoney() is:
$scope.checkEnoughMoney = function() {
var total = 0;
for(var i = 0; i < $scope.candies.length; i++) {
var amount = parseFloat($scope.sweets[i].cost) || 0;
total = total + amount;
if((parseFloat(amount) >= 25) && (parseFloat(amount) <= 100)) {
$("#candyError" + i).hide();
}
else {
$("#candyError" + i).show();
}
}
if(total > 100) {
$scope.candyForm.candyCost.$setValidity('candyForm.candyCost',false);
return true;
}
else {
$scope.candyForm.candyCost.$setValidity('candyForm.candyCost',true);
return false;
}
};
Setting the $scope.candyForm.candyCost to true or false works here as it affects all the instances if candies, which is great, as it needs to. But how do I make the single instance invalid if the person has entered less than 25 cents or more than 100 cents?
As you can see, I have cheated to make the 25>=cost>=100 messages show when they should, but the form is still submitting, as the input is not being set to invalid. If I try $scope.candies[index].setValidity("cost", false), it throws an error, as the parent scope can't access the scope inside the ng-repeat. Is there any way to get around this?
Here's an example of handling forms in Angular that should take care of your problem.
http://docs.angularjs.org/cookbook/advancedform
I prefer to setup a controller fn to handle requests and pass those through a service.

Resources