From 7037f0626add940d022b65fa43751d8d22f9a41b Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Sep 2013 17:00:34 +1000 Subject: [PATCH] completes U4-2706 Change form watching in the validators over to use functions , test using other manifest property editors as parameter editors, fixes up validation in the dialog. --- .../Manifest/ManifestParserTests.cs | 38 +++++++++++++++++++ .../validation/valpropertymsg.directive.js | 26 ++++++------- .../directives/validation/valtab.directive.js | 14 +++---- .../validation/valtogglemsg.directive.js | 33 +++++++++++----- .../views/templates/insertmacro.controller.js | 15 +++++--- .../src/views/templates/insertmacro.html | 16 ++++---- .../App_Plugins/MyPackage/Package.manifest | 1 + .../PropertyEditors/Views/RegexEditor.html | 6 +-- .../Models/ContentEditing/MacroParameter.cs | 13 +++++++ .../Models/Mapping/MacroModelMapper.cs | 15 +++++++- .../Mapping/ParameterEditorViewResolver.cs | 20 ---------- src/Umbraco.Web/Umbraco.Web.csproj | 1 - 12 files changed, 125 insertions(+), 73 deletions(-) delete mode 100644 src/Umbraco.Web/Models/Mapping/ParameterEditorViewResolver.cs diff --git a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs index b26daff668..03898e74d5 100644 --- a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs +++ b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs @@ -110,6 +110,44 @@ namespace Umbraco.Tests.Manifest Assert.AreEqual("some default pre val", parser.ElementAt(1).DefaultPreValues["key1"]); } + [Test] + public void Property_Editors_Can_Be_Parameter_Editor() + { + + var a = JsonConvert.DeserializeObject(@"[ + { + alias: 'Test.Test1', + name: 'Test 1', + isParameterEditor: true, + defaultConfig: { key1: 'some default val' }, + editor: { + view: '~/App_Plugins/MyPackage/PropertyEditors/MyEditor.html', + valueType: 'int', + validation: { + required : true, + regex : '\\d*' + } + } + }, + { + alias: 'Test.Test2', + name: 'Test 2', + defaultConfig: { key1: 'some default pre val' }, + editor: { + view: '~/App_Plugins/MyPackage/PropertyEditors/CsvEditor.html' + } + } +]"); + var parser = ManifestParser.GetPropertyEditors(a); + + Assert.AreEqual(1, parser.Count(x => x.IsParameterEditor)); + + IParameterEditor parameterEditor = parser.First(); + Assert.AreEqual(1, parameterEditor.Configuration.Count); + Assert.IsTrue(parameterEditor.Configuration.ContainsKey("key1")); + Assert.AreEqual("some default val", parameterEditor.Configuration["key1"]); + } + [Test] public void Parse_Parameter_Editors() { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js index 55eb2341a0..75ca905c4a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js @@ -24,9 +24,6 @@ function valPropertyMsg(serverValidationManager) { */ link: function (scope, element, attrs, formCtrl) { - //assign the form control to our isolated scope so we can watch it's values - scope.formCtrl = formCtrl; - //if there's any remaining errors in the server validation service then we should show them. var showValidation = serverValidationManager.items.length > 0; var hasError = false; @@ -34,17 +31,18 @@ function valPropertyMsg(serverValidationManager) { //create properties on our custom scope so we can use it in our template scope.errorMsg = ""; - //listen for error changes - scope.$watch("formCtrl.$error", function () { + //listen for form error changes + scope.$watch(function() { + return formCtrl.$error; + }, function() { if (formCtrl.$invalid) { - + //first we need to check if the valPropertyMsg validity is invalid if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) { //since we already have an error we'll just return since this means we've already set the // hasError and errorMsg properties which occurs below in the serverValidationManager.subscribe return; - } - else if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) { + } else if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) { //check if it's one of the properties that is invalid in the current content property hasError = true; //update the validation message if we don't already have one assigned. @@ -56,13 +54,11 @@ function valPropertyMsg(serverValidationManager) { } scope.errorMsg = err ? err.errorMsg : "Property has errors"; } - } - else { + } else { hasError = false; scope.errorMsg = ""; } - } - else { + } else { hasError = false; scope.errorMsg = ""; } @@ -103,12 +99,12 @@ function valPropertyMsg(serverValidationManager) { // is the only one, then we'll clear. var errCount = 0; - for (var e in scope.formCtrl.$error) { + for (var e in formCtrl.$error) { errCount++; } - if ((errCount === 1 && scope.formCtrl.$error.valPropertyMsg !== undefined) || - (formCtrl.$invalid && scope.formCtrl.$error.valServer !== undefined)) { + if ((errCount === 1 && formCtrl.$error.valPropertyMsg !== undefined) || + (formCtrl.$invalid && formCtrl.$error.valServer !== undefined)) { scope.errorMsg = ""; formCtrl.$setValidity('valPropertyMsg', true); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js index 463a47397f..9740adfabc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtab.directive.js @@ -12,25 +12,23 @@ function valTab() { link: function (scope, element, attr, formCtrl) { var tabId = "tab" + scope.tab.id; - - //assign the form control to our isolated scope so we can watch it's values - scope.formCtrl = formCtrl; + scope.tabHasError = false; //watch the current form's validation for the current field name - scope.$watch("formCtrl.$valid", function () { + scope.$watch(function() { + return formCtrl.$valid; + }, function() { var tabContent = element.closest(".umb-panel").find("#" + tabId); if (formCtrl.$invalid) { //check if the validation messages are contained inside of this tabs if (tabContent.find(".ng-invalid").length > 0) { scope.tabHasError = true; - } - else { + } else { scope.tabHasError = false; } - } - else { + } else { scope.tabHasError = false; } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js index 65f441b061..e2db686605 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valtogglemsg.directive.js @@ -18,19 +18,28 @@ function valToggleMsg(serverValidationManager) { throw "valToggleMsg cannot find field " + attr.valMsgFor + " on form " + formCtrl.$name; } - //assign the form control to our isolated scope so we can watch it's values - scope.formCtrl = formCtrl; - //if there's any remaining errors in the server validation service then we should show them. var showValidation = serverValidationManager.items.length > 0; - + var hasCustomMsg = element.contents().length > 0; + //add a watch to the validator for the value (i.e. myForm.value.$error.required ) - scope.$watch("formCtrl." + attr.valMsgFor + ".$error." + attr.valToggleMsg, function () { - if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] && showValidation) { - element.show(); - } - else { - element.hide(); + scope.$watch(function () { + //sometimes if a dialog closes in the middle of digest we can get null references here + + return (formCtrl && formCtrl[attr.valMsgFor]) ? formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] : null; + }, function () { + //sometimes if a dialog closes in the middle of digest we can get null references here + if ((formCtrl && formCtrl[attr.valMsgFor])) { + if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] && showValidation) { + element.show(); + //display the error message if this element has no contents + if (!hasCustomMsg) { + element.html(formCtrl[attr.valMsgFor].errorMsg); + } + } + else { + element.hide(); + } } }); @@ -38,6 +47,10 @@ function valToggleMsg(serverValidationManager) { showValidation = true; if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg]) { element.show(); + //display the error message if this element has no contents + if (!hasCustomMsg) { + element.html(formCtrl[attr.valMsgFor].errorMsg); + } } else { element.hide(); diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.controller.js index 53985cdca6..ce54e173cc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.controller.js @@ -21,12 +21,10 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEdi $scope.submit({ selectedMacro: $scope.selectedMacro }); } else { $scope.wizardStep = "paramSelect"; - //update the view on each editor to be correct - _.each(data, function (item) { - item.view = umbPropEditorHelper.getViewPath(item.view); - //add the 'value' property for the model for editors to bind to - item.value = null; - }); + ////update the view on each editor to be correct + //_.each(data, function (item) { + // item.view = umbPropEditorHelper.getViewPath(item.view); + //}); $scope.macroParams = data; } }); @@ -64,6 +62,11 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEdi $scope.submitForm = function () { + if ($scope.wizardStep === "paramSelect") { + //we need to broadcast the saving event for the toggle validators to work + $scope.$broadcast("saving"); + } + //ensure the drop down is dirty so the styles validate $scope.insertMacroForm.$setDirty(true); if ($scope.insertMacroForm.$invalid) { diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.html b/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.html index 172045fd9b..3c446c68a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/insertmacro.html @@ -1,4 +1,4 @@ -
+
@@ -23,18 +23,16 @@
    -
  • - -
    - +
  • + +
    + +
    +
-

- {{$parent.macroParams | json}} -

-
diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest index f134877736..bdc8d9f211 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest @@ -3,6 +3,7 @@ { alias: "MyPackage.Regex", name: "Regex", + isParameterEditor: true, defaultConfig: { regex: "^\\d*$" }, diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html index ad86065019..e196cbe7c6 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html @@ -8,10 +8,10 @@ Required! - {{propertyForm.regex.errorMsg}} - {{propertyForm.regex.errorMsg}} + + \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs b/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs index 733fdbd0f6..0f5bf072e0 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs @@ -28,5 +28,18 @@ namespace Umbraco.Web.Models.ContentEditing [Required(AllowEmptyStrings = false)] public string View { get; set; } + /// + /// The configuration for this parameter editor + /// + [DataMember(Name = "config", IsRequired = true)] + [Required(AllowEmptyStrings = false)] + public IDictionary Configuration { get; set; } + + /// + /// Since we don't post this back this isn't currently really used on the server side + /// + [DataMember(Name = "value")] + public object Value { get; set; } + } } diff --git a/src/Umbraco.Web/Models/Mapping/MacroModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MacroModelMapper.cs index f29f3675a0..1af1a6ea8d 100644 --- a/src/Umbraco.Web/Models/Mapping/MacroModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MacroModelMapper.cs @@ -27,7 +27,20 @@ namespace Umbraco.Web.Models.Mapping .ConvertUsing(macro => macro.Properties.Select(Mapper.Map).ToList()); config.CreateMap() - .ForMember(parameter => parameter.View, expression => expression.ResolveUsing()); + .AfterMap((property, parameter) => + { + //map the view and the config + + var paramEditor = ParameterEditorResolver.Current.GetByAlias(property.EditorAlias); + if (paramEditor == null) + { + throw new InvalidOperationException("Could not resolve macro parameter editor: " + property.EditorAlias); + } + parameter.View = paramEditor.ValueEditor.View; + + //set the config + parameter.Configuration = paramEditor.Configuration; + }); } diff --git a/src/Umbraco.Web/Models/Mapping/ParameterEditorViewResolver.cs b/src/Umbraco.Web/Models/Mapping/ParameterEditorViewResolver.cs deleted file mode 100644 index 982f7aab5e..0000000000 --- a/src/Umbraco.Web/Models/Mapping/ParameterEditorViewResolver.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using AutoMapper; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; - -namespace Umbraco.Web.Models.Mapping -{ - internal class ParameterEditorViewResolver : ValueResolver - { - protected override string ResolveCore(IMacroProperty source) - { - var paramEditor = ParameterEditorResolver.Current.GetByAlias(source.EditorAlias); - if (paramEditor == null) - { - throw new InvalidOperationException("Could not resolve macro parameter editor: " + source.EditorAlias); - } - return paramEditor.ValueEditor.View; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 009cc9557d..ec8dec8fdf 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -307,7 +307,6 @@ -