diff --git a/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js b/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js index 378eb79dc7..a5392d16ee 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/servervalidationmgr.service.js @@ -17,7 +17,7 @@ function serverValidationManager($timeout) { // - segment // - callback (function) // - id (unique identifier, auto-generated, used internally for unsubscribing the callback) - // - options (used for complex properties, can contain options.matchPrefix options.matchSuffix if either are set to true the callback will fire for any item with this propertyAlias prefix or suffix) + // - options (used for complex properties, can contain options.matchType which can be either "suffix" or "prefix" or "contains") var callbacks = []; // The array of error message objects, each object 'key' is: @@ -30,8 +30,7 @@ function serverValidationManager($timeout) { var items = []; var defaultMatchOptions = { - matchPrefix: false, - matchSuffix: false + matchType: null } /** calls the callback specified with the errors specified, used internally */ @@ -99,19 +98,25 @@ function serverValidationManager($timeout) { //find all errors for this property return _.filter(items, function (item) { - var matchProp = options.matchPrefix - ? (item.propertyAlias === propertyAlias || (item.propertyAlias && item.propertyAlias.startsWith(propertyAlias + '/'))) - : options.matchSuffix - ? (item.propertyAlias === propertyAlias || (item.propertyAlias && item.propertyAlias.endsWith('/' + propertyAlias))) - : item.propertyAlias === propertyAlias; + if (!item.propertyAlias) { + return false; + } - var ignoreField = options.matchPrefix || options.matchSuffix; + var matchProp = item.propertyAlias === propertyAlias + ? true + : options.matchType === "prefix" + ? item.propertyAlias.startsWith(propertyAlias + '/') + : options.matchType === "suffix" + ? item.propertyAlias.endsWith('/' + propertyAlias) + : options.matchType === "contains" + ? item.propertyAlias.includes('/' + propertyAlias + '/') + : false; return matchProp && item.culture === culture && item.segment === segment // ignore field matching if match options are used - && (ignoreField || (item.fieldName === fieldName || (fieldName === undefined || fieldName === ""))); + && (options.matchType || (item.fieldName === fieldName || (fieldName === undefined || fieldName === ""))); }); } @@ -234,20 +239,22 @@ function serverValidationManager($timeout) { cb.options = defaultMatchOptions; } - var matchProp = cb.options.matchPrefix - ? (cb.propertyAlias === propertyAlias || propertyAlias.startsWith(cb.propertyAlias + '/')) - : cb.options.matchSuffix - ? (cb.propertyAlias === propertyAlias || propertyAlias.endsWith(cb.propertyAlias + '/')) - : cb.propertyAlias === propertyAlias; - - var ignoreField = cb.options.matchPrefix || cb.options.matchSuffix; + var matchProp = cb.propertyAlias === propertyAlias + ? true + : cb.options.matchType === "prefix" + ? propertyAlias.startsWith(cb.propertyAlias + '/') + : cb.options.matchType === "suffix" + ? propertyAlias.endsWith('/' + cb.propertyAlias) + : cb.options.matchType === "contains" + ? propertyAlias.includes('/' + cb.propertyAlias + '/') + : false; //returns any callback that have been registered directly against the field and for only the property return matchProp && cb.culture === culture && cb.segment === segment - // if the callback is configured to patch prefix then we ignore the field value - && (ignoreField || (cb.fieldName === fieldName || (cb.fieldName === undefined || cb.fieldName === ""))); + // ignore field matching if match options are used + && (cb.options.matchType || (cb.fieldName === fieldName || (cb.fieldName === undefined || cb.fieldName === ""))); }); return found; } diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js index c5c6177444..ab2a5cdd69 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/server-validation-manager.spec.js @@ -639,13 +639,13 @@ if (propertyErrors.length > 0) { callbackA.push(propertyErrors); } - }, null, { matchPrefix: true }); + }, null, { matchType: "prefix" }); serverValidationManager.subscribe("myProperty/34E3A26C-103D-4A05-AB9D-7E14032309C3/addresses", null, null, function (isValid, propertyErrors, allErrors) { if (propertyErrors.length > 0) { callbackB.push(propertyErrors); } - }, null, { matchPrefix: true }); + }, null, { matchType: "prefix" }); //act // will match A: @@ -682,6 +682,97 @@ }); + it('can subscribe to a property validation path suffix', function () { + var callbackA = []; + var callbackB = []; + + //arrange + serverValidationManager.subscribe("myProperty", null, null, function (isValid, propertyErrors, allErrors) { + if (propertyErrors.length > 0) { + callbackA.push(propertyErrors); + } + }, null, { matchType: "suffix" }); + + serverValidationManager.subscribe("city", null, null, function (isValid, propertyErrors, allErrors) { + if (propertyErrors.length > 0) { + callbackB.push(propertyErrors); + } + }, null, { matchType: "suffix" }); + + //act + // will match A: + serverValidationManager.addPropertyError("myProperty", null, null, "property error", null); + serverValidationManager.addPropertyError("myProperty", null, "value1", "value error", null); + // will match B + serverValidationManager.addPropertyError("myProperty/34E3A26C-103D-4A05-AB9D-7E14032309C3/addresses/FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1/city", null, null, "property error", null); + serverValidationManager.addPropertyError("myProperty/34E3A26C-103D-4A05-AB9D-7E14032309C3/addresses/FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1/city", null, "value1", "value error", null); + // won't match: + serverValidationManager.addPropertyError("myProperty", "en-US", null, "property error", null); + serverValidationManager.addPropertyError("myProperty/34E3A26C-103D-4A05-AB9D-7E14032309C3/addresses/FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1/city", "en-US", null, "property error", null); + serverValidationManager.addPropertyError("otherProperty", null, null, "property error", null); + serverValidationManager.addPropertyError("otherProperty", null, "value1", "value error", null); + + //assert + + // both will be called each time addPropertyError is called + expect(callbackA.length).toEqual(8); + expect(callbackB.length).toEqual(6); // B - will only be called 6 times with errors because the first 2 calls to addPropertyError haven't added errors for B yet + expect(callbackA[callbackA.length - 1].length).toEqual(2); // 2 errors for A + expect(callbackB[callbackB.length - 1].length).toEqual(2); // 2 errors for B + + // clear the data and notify + callbackA = []; + callbackB = []; + + serverValidationManager.notify(); + $timeout.flush(); + + expect(callbackA.length).toEqual(1); + expect(callbackB.length).toEqual(1); + expect(callbackA[0].length).toEqual(2); // 2 errors for A + expect(callbackB[0].length).toEqual(2); // 2 errors for B + + }); + + it('can subscribe to a property validation path contains', function () { + var callbackA = []; + + //arrange + serverValidationManager.subscribe("addresses", null, null, function (isValid, propertyErrors, allErrors) { + if (propertyErrors.length > 0) { + callbackA.push(propertyErrors); + } + }, null, { matchType: "contains" }); + + //act + // will match A: + serverValidationManager.addPropertyError("addresses", null, null, "property error", null); + serverValidationManager.addPropertyError("addresses", null, "value1", "value error", null); + serverValidationManager.addPropertyError("myProperty/34E3A26C-103D-4A05-AB9D-7E14032309C3/addresses/FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1/city", null, null, "property error", null); + serverValidationManager.addPropertyError("myProperty/34E3A26C-103D-4A05-AB9D-7E14032309C3/addresses/FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1/city", null, "value1", "value error", null); + // won't match: + serverValidationManager.addPropertyError("addresses", "en-US", null, "property error", null); + serverValidationManager.addPropertyError("addresses/34E3A26C-103D-4A05-AB9D-7E14032309C3/addresses/FBEAEE8F-4BC9-43EE-8B81-FCA8978850F1/city", "en-US", null, "property error", null); + serverValidationManager.addPropertyError("otherProperty", null, null, "property error", null); + serverValidationManager.addPropertyError("otherProperty", null, "value1", "value error", null); + + //assert + + // both will be called each time addPropertyError is called + expect(callbackA.length).toEqual(8); + expect(callbackA[callbackA.length - 1].length).toEqual(4); // 4 errors for A + + // clear the data and notify + callbackA = []; + + serverValidationManager.notify(); + $timeout.flush(); + + expect(callbackA.length).toEqual(1); + expect(callbackA[0].length).toEqual(4); // 4 errors for A + + }); + // TODO: Finish testing the rest! });