Skip to content

Commit

Permalink
enhance(ValidationParser): handle validate props that subpropeties of…
Browse files Browse the repository at this point in the history
… an object

fixes aurelia#283
  • Loading branch information
ericIMT committed Sep 20, 2016
1 parent f6a2c0c commit 2b1b4da
Show file tree
Hide file tree
Showing 47 changed files with 571 additions and 259 deletions.
2 changes: 1 addition & 1 deletion dist/amd/implementation/standard-validator.d.ts
Expand Up @@ -7,7 +7,7 @@ import { ValidationMessageProvider } from './validation-messages';
* Responsible for validating objects and properties.
*/
export declare class StandardValidator extends Validator {
static inject: (typeof ValidationMessageProvider | typeof ViewResources)[];
static inject: (typeof ViewResources | typeof ValidationMessageProvider)[];
private messageProvider;
private lookupFunctions;
private getDisplayName;
Expand Down
7 changes: 7 additions & 0 deletions dist/amd/implementation/standard-validator.js
Expand Up @@ -65,6 +65,13 @@ define(["require", "exports", 'aurelia-templating', '../validator', '../validati
}
// validate.
var value = rule.property.name === null ? object : object[rule.property.name];
if (rule.property.name && rule.property.name.indexOf('.') !== -1) {
//if the rule name has a '.', we have a sub property.
//object is the object containing the field. get the last propertyy in the chain
//to get the field name. Use thi to get the correct value.
var parts = rule.property.name.split('.');
value = object[parts[parts.length - 1]];
}
var promiseOrBoolean = rule.condition(value, object);
if (promiseOrBoolean instanceof Promise) {
promises.push(promiseOrBoolean.then(function (isValid) {
Expand Down
29 changes: 18 additions & 11 deletions dist/amd/implementation/validation-parser.js
Expand Up @@ -35,26 +35,33 @@ define(["require", "exports", 'aurelia-binding', 'aurelia-templating', './util',
return expression;
};
ValidationParser.prototype.getAccessorExpression = function (fn) {
var classic = /^function\s*\([$_\w\d]+\)\s*\{\s*(?:"use strict";)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/;
var arrow = /^[$_\w\d]+\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/;
var classic = /^function\s*\([$_\w\d]+\)\s*\{\s*(?:"use strict";)?\s*.*return\s+[$_\w\d]+((\.[$_\w\d]+)+)\s*;?\s*\}$/;
var arrow = /^\(?[$_\w\d]+\)?\s*=>\s*(?:\{?.*return\s+)?[$_\w\d]+((\.[$_\w\d]+)+);?\s*\}?$/;
var match = classic.exec(fn) || arrow.exec(fn);
if (match === null) {
throw new Error("Unable to parse accessor function:\n" + fn);
}
return this.parser.parse(match[1]);
var name = match[1][0] == "." ? match[1].substr(1) : match[1];
return this.parser.parse(name);
};
ValidationParser.prototype.parseProperty = function (property) {
var accessor;
if (util_1.isString(property)) {
accessor = this.parser.parse(property);
return { name: property, displayName: null };
}
else {
accessor = this.getAccessorExpression(property.toString());
}
if (accessor instanceof aurelia_binding_1.AccessScope
|| accessor instanceof aurelia_binding_1.AccessMember && accessor.object instanceof aurelia_binding_1.AccessScope) {
var accessor = this.getAccessorExpression(property.toString());
var isSubProp = accessor instanceof aurelia_binding_1.AccessMember && accessor.object instanceof aurelia_binding_1.AccessScope;
if (accessor instanceof aurelia_binding_1.AccessScope || isSubProp) {
var propName = accessor.name;
if (isSubProp) {
//iterate up the chain until we are in the 1st sub-object of the root object.
var ao = accessor.object;
while (ao) {
propName = ao.name + '.' + propName;
ao = ao.object;
}
}
return {
name: accessor.name,
name: propName,
displayName: null
};
}
Expand Down
2 changes: 1 addition & 1 deletion dist/amd/implementation/validation-rules.js
Expand Up @@ -240,7 +240,7 @@ define(["require", "exports", './util', './rules', './validation-messages'], fun
* null, undefined and empty-string values are considered valid.
*/
FluentRules.prototype.email = function () {
return this.matches(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/)
return this.matches(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i)
.withMessageKey('email');
};
/**
Expand Down
1 change: 1 addition & 0 deletions dist/amd/property-info.d.ts
Expand Up @@ -7,4 +7,5 @@ import { Expression } from 'aurelia-binding';
export declare function getPropertyInfo(expression: Expression, source: any): {
object: any;
propertyName: string;
ruleSrc: null;
};
12 changes: 11 additions & 1 deletion dist/amd/property-info.js
Expand Up @@ -25,13 +25,23 @@ define(["require", "exports", 'aurelia-binding'], function (require, exports, au
}
var object;
var propertyName;
var ruleSrc = null;
if (expression instanceof aurelia_binding_1.AccessScope) {
object = source.bindingContext;
propertyName = expression.name;
}
else if (expression instanceof aurelia_binding_1.AccessMember) {
object = getObject(originalExpression, expression.object, source);
propertyName = expression.name;
if (expression.object) {
//build the path to the property from the object root.
var exp = expression.object;
while (exp.object) {
propertyName = exp.name + '.' + propertyName;
exp = exp.object;
}
ruleSrc = getObject(originalExpression, exp, source);
}
}
else if (expression instanceof aurelia_binding_1.AccessKeyed) {
object = getObject(originalExpression, expression.object, source);
Expand All @@ -40,7 +50,7 @@ define(["require", "exports", 'aurelia-binding'], function (require, exports, au
else {
throw new Error("Expression '" + originalExpression + "' is not compatible with the validate binding-behavior.");
}
return { object: object, propertyName: propertyName };
return { object: object, propertyName: propertyName, ruleSrc: ruleSrc };
}
exports.getPropertyInfo = getPropertyInfo;
});
1 change: 1 addition & 0 deletions dist/amd/validation-controller.d.ts
Expand Up @@ -41,6 +41,7 @@ export declare class ValidationController {
*/
validateTrigger: string;
private finishValidating;
isValid: boolean;
constructor(validator: Validator);
/**
* Adds an object to the set of objects that should be validated when validate is called.
Expand Down
43 changes: 33 additions & 10 deletions dist/amd/validation-controller.js
@@ -1,4 +1,4 @@
define(["require", "exports", './validator', './validate-trigger', './property-info', './validation-error'], function (require, exports, validator_1, validate_trigger_1, property_info_1, validation_error_1) {
define(["require", "exports", './validator', './validate-trigger', './property-info', './validation-error', './implementation/rules'], function (require, exports, validator_1, validate_trigger_1, property_info_1, validation_error_1, rules_1) {
"use strict";
/**
* Orchestrates validation.
Expand Down Expand Up @@ -30,6 +30,7 @@ define(["require", "exports", './validator', './validate-trigger', './property-i
this.validateTrigger = validate_trigger_1.validateTrigger.blur;
// Promise that resolves when validation has completed.
this.finishValidating = Promise.resolve();
this.isValid = false;
}
/**
* Adds an object to the set of objects that should be validated when validate is called.
Expand Down Expand Up @@ -112,7 +113,7 @@ define(["require", "exports", './validator', './validate-trigger', './property-i
*/
ValidationController.prototype.getInstructionPredicate = function (instruction) {
if (instruction) {
var object_1 = instruction.object, propertyName_1 = instruction.propertyName, rules_1 = instruction.rules;
var object_1 = instruction.object, propertyName_1 = instruction.propertyName, rules_2 = instruction.rules;
var predicate_1;
if (instruction.propertyName) {
predicate_1 = function (x) { return x.object === object_1 && x.propertyName === propertyName_1; };
Expand All @@ -121,8 +122,8 @@ define(["require", "exports", './validator', './validate-trigger', './property-i
predicate_1 = function (x) { return x.object === object_1; };
}
// todo: move to Validator interface:
if (rules_1 && rules_1.indexOf) {
return function (x) { return predicate_1(x) && rules_1.indexOf(x.rule) !== -1; };
if (rules_2 && rules_2.indexOf) {
return function (x) { return predicate_1(x) && rules_2.indexOf(x.rule) !== -1; };
}
return predicate_1;
}
Expand All @@ -139,17 +140,20 @@ define(["require", "exports", './validator', './validate-trigger', './property-i
// Get a function that will process the validation instruction.
var execute;
if (instruction) {
var object_2 = instruction.object, propertyName_2 = instruction.propertyName, rules_2 = instruction.rules;
var object_2 = instruction.object, propertyName_2 = instruction.propertyName, rules_3 = instruction.rules;
// if rules were not specified, check the object map.
rules_2 = rules_2 || this.objects.get(object_2);
rules_3 = rules_3 || this.objects.get(object_2);
if (!rules_3) {
rules_3 = rules_1.Rules.get(ruleSrc);
}
// property specified?
if (instruction.propertyName === undefined) {
// validate the specified object.
execute = function () { return _this.validator.validateObject(object_2, rules_2); };
execute = function () { return _this.validator.validateObject(object_2, rules_3); };
}
else {
// validate the specified property.
execute = function () { return _this.validator.validateProperty(object_2, propertyName_2, rules_2); };
execute = function () { return _this.validator.validateProperty(object_2, propertyName_2, rules_3); };
}
}
else {
Expand All @@ -160,12 +164,27 @@ define(["require", "exports", './validator', './validate-trigger', './property-i
var _b = _a[_i], object = _b[0], rules = _b[1];
promises.push(_this.validator.validateObject(object, rules));
}
console.log(_this.bindings);
for (var _c = 0, _d = Array.from(_this.bindings); _c < _d.length; _c++) {
var _e = _d[_c], binding = _e[0], rules = _e[1].rules;
var _f = property_info_1.getPropertyInfo(binding.sourceExpression, binding.source), object = _f.object, propertyName = _f.propertyName;
if (_this.objects.has(object)) {
continue;
}
if (propertyName.indexOf(".") !== -1) {
//rules = Rules.get(object);
console.log("----->", "Property", propertyName);
console.log(binding);
var parentProp = "";
var ittr = binding.sourceExpression.expression;
while (ittr.object) {
ittr = ittr.object;
parentProp = ittr.name;
}
console.log("----->", "parentProp", parentProp);
rules = rules_1.Rules.get(binding._observer0._callable0._observer0.obj[parentProp]);
console.log("----->", "rules", rules);
}
promises.push(_this.validator.validateProperty(object, propertyName, rules));
}
return Promise.all(promises).then(function (errorSets) { return errorSets.reduce(function (a, b) { return a.concat(b); }, []); });
Expand Down Expand Up @@ -241,7 +260,7 @@ define(["require", "exports", './validator', './validate-trigger', './property-i
this_1.errors.splice(this_1.errors.indexOf(oldError), 1);
}
else {
// there is a corresponding new error...
// there is a corresponding new error...
var newError = newErrors.splice(newErrorIndex, 1)[0];
// get the elements that are associated with the new error.
var elements_1 = this_1.getAssociatedElements(newError);
Expand Down Expand Up @@ -279,9 +298,13 @@ define(["require", "exports", './validator', './validate-trigger', './property-i
if (!binding.isBound) {
return;
}
var _a = property_info_1.getPropertyInfo(binding.sourceExpression, binding.source), object = _a.object, propertyName = _a.propertyName;
var _a = property_info_1.getPropertyInfo(binding.sourceExpression, binding.source), object = _a.object, propertyName = _a.propertyName, ruleSrc = _a.ruleSrc;
var registeredBinding = this.bindings.get(binding);
var rules = registeredBinding ? registeredBinding.rules : undefined;
if (!rules && ruleSrc) {
//if we got ruleSrc back we need to get the rules for the subprop which are located in the root of the model
rules = rules_1.Rules.get(ruleSrc);
}
this.validate({ object: object, propertyName: propertyName, rules: rules });
};
/**
Expand Down
2 changes: 1 addition & 1 deletion dist/commonjs/implementation/standard-validator.d.ts
Expand Up @@ -7,7 +7,7 @@ import { ValidationMessageProvider } from './validation-messages';
* Responsible for validating objects and properties.
*/
export declare class StandardValidator extends Validator {
static inject: (typeof ValidationMessageProvider | typeof ViewResources)[];
static inject: (typeof ViewResources | typeof ValidationMessageProvider)[];
private messageProvider;
private lookupFunctions;
private getDisplayName;
Expand Down
7 changes: 7 additions & 0 deletions dist/commonjs/implementation/standard-validator.js
Expand Up @@ -69,6 +69,13 @@ var StandardValidator = (function (_super) {
}
// validate.
var value = rule.property.name === null ? object : object[rule.property.name];
if (rule.property.name && rule.property.name.indexOf('.') !== -1) {
//if the rule name has a '.', we have a sub property.
//object is the object containing the field. get the last propertyy in the chain
//to get the field name. Use thi to get the correct value.
var parts = rule.property.name.split('.');
value = object[parts[parts.length - 1]];
}
var promiseOrBoolean = rule.condition(value, object);
if (promiseOrBoolean instanceof Promise) {
promises.push(promiseOrBoolean.then(function (isValid) {
Expand Down
29 changes: 18 additions & 11 deletions dist/commonjs/implementation/validation-parser.js
Expand Up @@ -38,26 +38,33 @@ var ValidationParser = (function () {
return expression;
};
ValidationParser.prototype.getAccessorExpression = function (fn) {
var classic = /^function\s*\([$_\w\d]+\)\s*\{\s*(?:"use strict";)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/;
var arrow = /^[$_\w\d]+\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/;
var classic = /^function\s*\([$_\w\d]+\)\s*\{\s*(?:"use strict";)?\s*.*return\s+[$_\w\d]+((\.[$_\w\d]+)+)\s*;?\s*\}$/;
var arrow = /^\(?[$_\w\d]+\)?\s*=>\s*(?:\{?.*return\s+)?[$_\w\d]+((\.[$_\w\d]+)+);?\s*\}?$/;
var match = classic.exec(fn) || arrow.exec(fn);
if (match === null) {
throw new Error("Unable to parse accessor function:\n" + fn);
}
return this.parser.parse(match[1]);
var name = match[1][0] == "." ? match[1].substr(1) : match[1];
return this.parser.parse(name);
};
ValidationParser.prototype.parseProperty = function (property) {
var accessor;
if (util_1.isString(property)) {
accessor = this.parser.parse(property);
return { name: property, displayName: null };
}
else {
accessor = this.getAccessorExpression(property.toString());
}
if (accessor instanceof aurelia_binding_1.AccessScope
|| accessor instanceof aurelia_binding_1.AccessMember && accessor.object instanceof aurelia_binding_1.AccessScope) {
var accessor = this.getAccessorExpression(property.toString());
var isSubProp = accessor instanceof aurelia_binding_1.AccessMember && accessor.object instanceof aurelia_binding_1.AccessScope;
if (accessor instanceof aurelia_binding_1.AccessScope || isSubProp) {
var propName = accessor.name;
if (isSubProp) {
//iterate up the chain until we are in the 1st sub-object of the root object.
var ao = accessor.object;
while (ao) {
propName = ao.name + '.' + propName;
ao = ao.object;
}
}
return {
name: accessor.name,
name: propName,
displayName: null
};
}
Expand Down

0 comments on commit 2b1b4da

Please sign in to comment.