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 19, 2016
1 parent f6a2c0c commit f466374
Show file tree
Hide file tree
Showing 37 changed files with 450 additions and 253 deletions.
2 changes: 1 addition & 1 deletion dist/amd/implementation/standard-validator.d.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
1 change: 1 addition & 0 deletions dist/amd/property-info.d.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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;
});
24 changes: 14 additions & 10 deletions dist/amd/validation-controller.js
Original file line number Diff line number Diff line change
@@ -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 @@ -112,7 +112,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 +121,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 +139,17 @@ 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);
// 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 Down Expand Up @@ -241,7 +241,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 +279,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
1 change: 1 addition & 0 deletions dist/commonjs/property-info.d.ts
Original file line number Diff line number Diff line change
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/commonjs/property-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,23 @@ function getPropertyInfo(expression, source) {
}
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,6 +50,6 @@ function getPropertyInfo(expression, source) {
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;
23 changes: 14 additions & 9 deletions dist/commonjs/validation-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var validator_1 = require('./validator');
var validate_trigger_1 = require('./validate-trigger');
var property_info_1 = require('./property-info');
var validation_error_1 = require('./validation-error');
var rules_1 = require('./implementation/rules');
/**
* Orchestrates validation.
* Manages a set of bindings, renderers and objects.
Expand Down Expand Up @@ -115,7 +116,7 @@ var ValidationController = (function () {
*/
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 @@ -124,8 +125,8 @@ var ValidationController = (function () {
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 @@ -142,17 +143,17 @@ var ValidationController = (function () {
// 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);
// 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 Down Expand Up @@ -244,7 +245,7 @@ var ValidationController = (function () {
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 @@ -282,9 +283,13 @@ var ValidationController = (function () {
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
Loading

0 comments on commit f466374

Please sign in to comment.