I'm using Valdr on my project and I need to validate that a date input "startDate" is before another date input "endDate".
<input id="startDate" name="startDate" type="text" ng-model="project.startDate"/>
<input id="endDate" name="endDate" type="text" ng-model="project.endDate"/>
I know that, without Valdr, this problem can be solved using a custom directive, as shown here: Directive for comparing two dates
I found a little unclear how to create a custom validator on Valdr that uses the values of other fields.
The answer is short but dissatisfactory: valdr does currently not support this. There is an open feature request on GitHub, though.
Until the feature gets implemented in valdr, you can use your own validator directive and kind of make it talk to valdr. The directive can require a 'form' and can get the names of the date models you want to compare. Then you do your logic to compare the two values and set the validity of the appropriate 'ngModelController'. Since you need to provide an error when setting the validity to that model, the error name will be your connection with valdr.
After that, you just only need to map the error in the 'valdrMessage' service:
.run(function (valdrMessage) {
valdrMessage.angularMessagesEnabled = true;
valdrMessage.addMessages({
'date': 'Invalid date!'
});
});
Valdr will show the message bellow the invalid field as usual.
Actually you can solve this through a custom validator, which can get another field and compare the values with each other. The code below is using the valdr-bean-validation for serverside generation of valodation.json.
If you want to use it without this, just look into the JS code and add the validator in your validation.json manually.
Java Annotation (serverside declaration of the valdr validator):
package validation;
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR,
ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
public #interface DateFormat {
String message();
Class[] groups() default { };
String beforeFieldName();
}
Java Bean (usage of the annotation, this class has to be used in the generation of validation.json):
package pojo;
import validation.DateFormat;
public class RegistrationPojo implements BasePojo {
#NotNull(message = "message.date1.required")
private Date date1;
#NotNull(message = "message.date2.required")
#DateFormat(message = "message.date2.date", beforeFieldName = "date1")
private Date date2;
}
JS (implementation of the custom validator and registering it in valdr):
module.factory('validation.DateFormat', [
function () {
return {
name: 'validation.DateFormat',
validate: function (value, constraint) {
var minOk = true;
var maxOk = true;
var format = false; // constraint.pattern is mandatory
//do not validate for required here, if date is null, date will return true (valid)
console.log("my date validator called");
console.log(" beforeFieldName: " + constraint.beforeFieldName);
var field = document.querySelector('[name="' + constraint.beforeFieldName + '"]');
console.log("field value: " + (field ? field.value : "null"));
return (!field || value > field.value);
}
};
}]);
module.config([
"valdrProvider",
function(valdrProvider) {
valdrProvider.addValidator('validation.DateFormat');
}]);
You could go with this solution:
Have a bool value calculated upon changes in any of the date fields - a value indicating if the validation rule is met
Create a simple custom validator to check if that value is true or not
Register your validator and add a constraint for that calculated value
Place a hidden input for that calculated value anywhere you like your validation message to appear
Related
I think someone must have run into this situation before. Basically I have a big "form" which is composed of multiple smaller "forms" inside. (In fact, they are not real forms, just sets of inputs that are grouped together to collect info for models).
This form is for a checkout page, which contains:
shipping address
shipping method
billing address
billing method
other additional info such as discounts code input, gift wrapping etc.
I would like to update the user filled info to the server as soon as they complete each part (for example, when they complete shipping address). However, I want to make it work seamlessly without the need for the users to click some kind of "update" button after filling each partial part. I wonder if there is some way to go around this?
You'll want to $watch the fields in question and act upon them (say save to db) when they are filled in. The issue you will run into is how to determine when a user has filled fields in. Things like onblur etc don't work very well in practice. I would recommend using what is called a debounce function which is basically a function that allows the user to pause for X amount of time without our code going "ok done! now let's.. ohh wait still typing..."
Here's an example that I use on my own cart - I want to automatically get shipping quotes once I have an address so I watch these fields, allow some pausing with my debounce function then call my server for quotes.
Here's some controller code:
// Debounce function to wait until user is done typing
function debounce(fn, delay) {
var timer = null;
return function() {
var context = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
};
}
// Apply debounce to our shipping rate fetch method
var fetch = debounce(function() {
$scope.fetching = true;
cartService.updateShipping($scope.shipping, function(data) {
$scope.fetching = false;
$scope.quotes = data;
});
}, 1000);
// Watch the shipping fields - when enough done and user is done typing then get quote
$scope.$watch('shipping', function(newVal, oldVal) {
// I use this to play around with what fields I actually want before I do something
var fields = ['street', 'region', 'name', 'postal', 'country', 'city'];
var valid = true;
fields.forEach(function(field) {
if (!$scope.form[field].$valid) {
valid = false;
}
});
if (valid) fetch();
}, true);
My form fields are setup like this:
<input type="text" name="street ng-model="shipping.street" required>
<input type="text" name="name" ng-model="shipping.name" required>
Notice how I make them part of a "shipping" object - that allows me to watch the shipping fields independently of others such as billing.
Note that the above is for the extreme cases such as shipping fields. For simple things such as subscribing to a newsletter if they check a box then you don't need to use the above and can simply do an ng-click="spamMe();" call in your checkbox. That function (spamMe) would be in your controller and can then call your server etc...
var spamMe = function() {
// Grab the email field that might be at top - ideally check if it's filled in but you get the idea
var email = $scope.email;
$http.post('/api/spam', ....);
}
I'd apply a $scope.$watch on each of those variables to trigger a function that checks to see if all the fields for a given section are filled out, and if so, then submit it to the server as an ajax request.
Here's my attempt at writing this:
var shippingFields = ['address', 'city', 'state', 'zip'] // etc
function submitFieldsWhenComplete(section, fields) {
fieldValues = fields.forEach(function (field) {
return $scope[section][field]
});
if (fieldValues.every()) {
// We've got all the values, submit to the server
$http.post({
url: "/your/ajax/endpoint",
data: $scope.shipping
})
}
}
shippingFields.forEach(function(field) {
$scope.$watch(function() {
return $scope['shipping'][field]
}, function(val) {
submitFieldsWhenComplete('shipping', shippingFields);
});
});
I've a probllem with email textfield on which I want to perform multi validations. In detail:
1. Classic format email validation
2. Unique email check
Can I override email VType? or I have to create a custom VType? How can I perform two validation with two different error messages in a single VType?
Thanks
Regards
You can override the default validation using the validator attribute. For example, if you wish to enforce the standard rules and some other rules (e.g. defined by isSomeOtherRules() that returns a boolean), set the following attribute:
validator: function(value) {
return Ext.form.VTypes.email(value) && isSomeOtherRules(value);
}
Expanding on Andrew's post; we can return validation messages (shown below) to get the same look and feel of vtype error alert:
validator: function(value) {
if (!Ext.form.VTypes.cfpValidatePdf(value)) {
return 'File must be pdf';
} else if (!Ext.form.VTypes.cfpValidateFileNameSize(value)) {
return 'The maximum length of the filename is 64';
} else {
return true;
}
}
I'm new to NancyFX and trying to simply bind a posted form to my model.
In the module when trying to access the posted values I run following statement:
string email = this.Context.Request.Form["Email"];
Debug.WriteLine(email);
Result is:
"Nancy.DynamicDictionaryValue" instead of posted value
Can anybody tell me what newbie mistake I'm doing:
The form looks like:
<form method="post" action="account">
<input type="text" id="Email" />
<input type="password" id="Password" />
<input type="submit" value="Create" />
</form>
the routing in Module contructor:
Post["/"] = parameters => CreateAccount(parameters);
The dynamic dictionary returns a dynamic value, if you cast it to a string (implicitly or explicitly) you'll get what you want, or just use the build in model binder https://github.com/NancyFx/Nancy/wiki/Model-binding
Just adding to the correct answer above in the hope it is useful to nancy-newbies like me.
Because the Nancy Form and Query are of type dynamic you can access the values using the name of the form or query-string param (see terms and max in the example code). I use a simple base class just to make the syntax terser throughout the rest of my modules.
Note: The ExpandoObject Model in the base class is there so I can just throw values at my view-model and not have to worry about cluttering things up with strongly typed data-transfer classes (this also helps prevent exposing any secret domain instance data).
public class SearchModule : _BaseModule
{
public SearchModule(ISearchService searchService)
{
Get["/search"] = _ =>
{
if (!Query.terms.HasValue) return HttpStatusCode.BadRequest;
var terms = (string) Query.terms;
var max = (Query.max.HasValue) ? (int) Query.max : 3;
Model.SearchResults = searchService.GetResults(max, terms);
...
};
}
}
public class _BaseModule : NancyModule
{
protected dynamic Model = new ExpandoObject();
public dynamic Query { get { return Request.Query; } }
public dynamic Form { get { return Request.Form; } }
}
I need to validate a form with a bunch of inputs in it. And, if an input is invalid, indicate visually in the form that a particular attribute is invalid. For this I need to validate each form element individually.
I have one model & one view representing the entire form. Now when I update an attribute:
this.model.set('name', this.$name.val())
the validate method on the model will be called.
But, in that method I am validating all the attributes, so when setting the attribute above, all others are also validated, and if any one is invalid, an error is returned. This means that even if my 'name' attribute is valid, I get errors for others.
So, how do I validate just one attribute?
I think that it is not possible to just validate one attribute via the validate() method. One solution is to not use the validate method, and instead validate every attribute on 'change' event. But then this would make a lot of change handlers. Is it the correct approach? What else can I do?
I also think that this points to a bigger issue in backbone:
Whenever you use model.set() to set an attribute on the model, your validation method is run and all attributes are validated. This seems counterintuitive as you just want that single attribute to be validated.
Validate is used to keep your model in a valid state, it won't let you set an invalid value unless you pass a silent:true option.
You could either set all your attributes in one go:
var M=Backbone.Model.extend({
defaults:{
name:"",
count:0
},
validate: function(attrs) {
var invalid=[];
if (attrs.name==="") invalid.push("name");
if (attrs.count===0) invalid.push("count");
if (invalid.length>0) return invalid;
}
});
var obj=new M();
obj.on("error",function(model,err) {
console.log(err);
});
obj.set({
name:"name",
count:1
});
or validate them one by one before setting them
var M=Backbone.Model.extend({
defaults:{
name:"",
count:0
},
validate: function(attrs) {
var invalid=[];
if ( (_.has(attrs,"name"))&&(attrs.name==="") )
invalid.push("name");
if ( (_.has(attrs,"count"))&&(attrs.count===0) )
invalid.push("count");
if (invalid.length>0) return invalid;
}
});
var obj=new M();
obj.on("error",function(model,err) {
console.log(err);
});
if (!obj.validate({name:"name"}))
obj.set({name:"name"},{silent:true});
I recently created a small Backbone.js plugin, Backbone.validateAll, that will allow you to validate only the Model attributes that are currently being saved/set by passing a validateAll option.
https://github.com/gfranko/Backbone.validateAll
That is not the issue of Backbone, it doesn't force you to write validation in some way. There is no point in validation of all attributes persisted in the model, cause normally your model doesn't contain invalid attributes, cause set() doesn't change the model if validation fails, unless you pass silent option, but that is another story. However if you choose this way, validation just always pass for not changed attributes because of the point mentioned above.
You may freely choose another way: validate only attributes that are to be set (passed as an argument to validate()).
You can also overload your model's set function with your own custom function to pass silent: true to avoid triggering validation.
set: function (key, value, options) {
options || (options = {});
options = _.extend(options, { silent: true });
return Backbone.Model.prototype.set.call(this, key, value, options);
}
This basically passes {silent:true} in options and calls the Backbone.Model set function with {silent: true}.
In this way, you won't have to pass {silent: true} as options everywhere, where you call
this.model.set('propertyName',val, {silent:true})
For validations you can also use the Backbone.Validation plugin
https://github.com/thedersen/backbone.validation
I had to make a modification to the backbone.validation.js file, but it made this task much easier for me. I added the snippet below to the validate function.
validate: function(attrs, setOptions){
var model = this,
opt = _.extend({}, options, setOptions);
if(!attrs){
return model.validate.call(model, _.extend(getValidatedAttrs(model), model.toJSON()));
}
///////////BEGIN NEW CODE SNIPPET/////////////
if (typeof attrs === 'string') {
var attrHolder = attrs;
attrs = [];
attrs[attrHolder] = model.get(attrHolder);
}
///////////END NEW CODE SNIPPET///////////////
var result = validateObject(view, model, model.validation, attrs, opt);
model._isValid = result.isValid;
_.defer(function() {
model.trigger('validated', model._isValid, model, result.invalidAttrs);
model.trigger('validated:' + (model._isValid ? 'valid' : 'invalid'), model, result.invalidAttrs);
});
if (!opt.forceUpdate && result.errorMessages.length > 0) {
return result.errorMessages;
}
}
I could then call validation on a single attribute like so
this.model.set(attributeName, attributeValue, { silent: true });
this.model.validate(attributeName);
Is there any way to test a vType on a value, without it being in a form?
Eg I have a custom vtype implemented to do ajax validation, however I would also like to run it against the email vtype, so I was hoping to run something inside my custom vtype along the lines of
validate('abc#de.ce','email');
You could use functions instead of properties, then you could just call 'em:
Ext.apply(Ext.form.VTypes, {
// Validates an ajax thingy
ajax: function(v) {
// validate against email VType
return Ext.form.VTypes.email(v);
},
// Override the default Ext function, to allow oddly-placed hyphens
email: function(v) {
var regex = /^[-\w][-+\.\w]*#[-\w\.]+\.[A-Za-z]{2,4}$/;
return regex.test(v);
}
}
Ext.form.VTypes.ajax('abc#de.ce');