From a4f7c49f50392d3f30ee97edeb4b821bf9acb261 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 6 Jan 2015 10:55:38 +1100 Subject: [PATCH] Merge branch 'Alain-es-Fix_for_U4-5875' into dev-v7 - And updates quite a lot: changes directive name to valPropertyValidator and changes how it works to be ultra flexible for custom validation of property data. Updates umbProperty directive to expose an API for other directives to utilize (like the new set setPropertyError method). Updates valServer to use the better way to access the current property object from the umbProperty directive API. Updates both valServer and valPropertyMsg to only perform a watch when necessary when there has been server side errors, this will speed up client side performance quite a bit. Now the tags, color picker and upload property editors performs correct client side and server side validation for the required validation flag. --- .../directives/umbproperty.directive.js | 12 +- .../validation/valpropertymsg.directive.js | 129 +++++++++++------- .../valpropertyvalidator.directive.js | 67 +++++++++ .../validation/valserver.directive.js | 73 ++++++---- .../colorpicker/colorpicker.controller.js | 12 ++ .../colorpicker/colorpicker.html | 2 + .../fileupload/fileupload.controller.js | 20 +++ .../fileupload/fileupload.html | 5 +- .../propertyeditors/tags/tags.controller.js | 65 ++++----- .../src/views/propertyeditors/tags/tags.html | 19 ++- 10 files changed, 286 insertions(+), 118 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbproperty.directive.js index cd84a3b2ca..d84bb8e24d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbproperty.directive.js @@ -13,8 +13,18 @@ angular.module("umbraco.directives") restrict: 'E', replace: true, templateUrl: 'views/directives/umb-property.html', - link: function (scope, element, attrs, ctrl) { + //Define a controller for this directive to expose APIs to other directives + controller: function ($scope, $timeout) { + + var self = this; + + //set the API properties/methods + + self.property = $scope.property; + self.setPropertyError = function(errorMsg) { + $scope.property.propertyErrorMessage = errorMsg; + }; } }; }); \ No newline at end of file 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 ea4b1b82c8..d37f4c5271 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 @@ -9,22 +9,86 @@ * and when an error is detected for this property we'll show the error message. * In order for this directive to work, the valStatusChanged directive must be placed on the containing form. **/ -function valPropertyMsg(serverValidationManager) { +function valPropertyMsg(serverValidationManager) { + return { scope: { - property: "=property" + property: "=" }, require: "^form", //require that this directive is contained within an ngForm replace: true, //replace the element with the template restrict: "E", //restrict to element - template: "
{{errorMsg}}
", - + template: "
{{errorMsg}}
", + /** Our directive requries a reference to a form controller which gets passed in to this parameter */ link: function (scope, element, attrs, formCtrl) { + var watcher = null; + + // Gets the error message to display + function getErrorMsg() { + //this can be null if no property was assigned + if (scope.property) { + //first try to get the error msg from the server collection + var err = serverValidationManager.getPropertyError(scope.property.alias, ""); + //if there's an error message use it + if (err && err.errorMsg) { + return err.errorMsg; + } + else { + return scope.property.propertyErrorMessage ? scope.property.propertyErrorMessage : "Property has errors"; + } + + } + return "Property has errors"; + } + + // We need to subscribe to any changes to our model (based on user input) + // This is required because when we have a server error we actually invalidate + // the form which means it cannot be resubmitted. + // So once a field is changed that has a server error assigned to it + // we need to re-validate it for the server side validator so the user can resubmit + // the form. Of course normal client-side validators will continue to execute. + function startWatch() { + //if there's not already a watch + if (!watcher) { + watcher = scope.$watch("property.value", function (newValue, oldValue) { + + if (!newValue || angular.equals(newValue, oldValue)) { + return; + } + + var errCount = 0; + for (var e in formCtrl.$error) { + if (angular.isArray(formCtrl.$error[e])) { + errCount++; + } + } + + //we are explicitly checking for valServer errors here, since we shouldn't auto clear + // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg + // is the only one, then we'll clear. + + if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { + scope.errorMsg = ""; + formCtrl.$setValidity('valPropertyMsg', true); + stopWatch(); + } + }, true); + } + } + + //clear the watch when the property validator is valid again + function stopWatch() { + if (watcher) { + watcher(); + watcher = null; + } + } + //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; @@ -33,7 +97,7 @@ function valPropertyMsg(serverValidationManager) { scope.errorMsg = ""; //listen for form error changes - scope.$on("valStatusChanged", function(evt, args) { + scope.$on("valStatusChanged", function (evt, args) { if (args.form.$invalid) { //first we need to check if the valPropertyMsg validity is invalid @@ -47,12 +111,7 @@ function valPropertyMsg(serverValidationManager) { hasError = true; //update the validation message if we don't already have one assigned. if (showValidation && scope.errorMsg === "") { - var err; - //this can be null if no property was assigned - if (scope.property) { - err = serverValidationManager.getPropertyError(scope.property.alias, ""); - } - scope.errorMsg = err ? err.errorMsg : "Property has errors"; + scope.errorMsg = getErrorMsg(); } } else { @@ -70,15 +129,11 @@ function valPropertyMsg(serverValidationManager) { scope.$on("formSubmitting", function (ev, args) { showValidation = true; if (hasError && scope.errorMsg === "") { - var err; - //this can be null if no property was assigned - if (scope.property) { - err = serverValidationManager.getPropertyError(scope.property.alias, ""); - } - scope.errorMsg = err ? err.errorMsg : "Property has errors"; + scope.errorMsg = getErrorMsg(); } else if (!hasError) { scope.errorMsg = ""; + stopWatch(); } }); @@ -86,37 +141,10 @@ function valPropertyMsg(serverValidationManager) { scope.$on("formSubmitted", function (ev, args) { showValidation = false; scope.errorMsg = ""; - formCtrl.$setValidity('valPropertyMsg', true); + formCtrl.$setValidity('valPropertyMsg', true); + stopWatch(); }); - //We need to subscribe to any changes to our model (based on user input) - // This is required because when we have a server error we actually invalidate - // the form which means it cannot be resubmitted. - // So once a field is changed that has a server error assigned to it - // we need to re-validate it for the server side validator so the user can resubmit - // the form. Of course normal client-side validators will continue to execute. - scope.$watch("property.value", function(newValue) { - //we are explicitly checking for valServer errors here, since we shouldn't auto clear - // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg - // is the only one, then we'll clear. - - if (!newValue) { - return; - } - - var errCount = 0; - for (var e in formCtrl.$error) { - if (angular.isArray(formCtrl.$error[e])) { - errCount++; - } - } - - if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { - scope.errorMsg = ""; - formCtrl.$setValidity('valPropertyMsg', true); - } - }, true); - //listen for server validation changes // NOTE: we pass in "" in order to listen for all validation changes to the content property, not for // validation changes to fields in the property this is because some server side validators may not @@ -124,27 +152,30 @@ function valPropertyMsg(serverValidationManager) { // It's important to note that we need to subscribe to server validation changes here because we always must // indicate that a content property is invalid at the property level since developers may not actually implement // the correct field validation in their property editors. - + if (scope.property) { //this can be null if no property was assigned - serverValidationManager.subscribe(scope.property.alias, "", function(isValid, propertyErrors, allErrors) { + serverValidationManager.subscribe(scope.property.alias, "", function (isValid, propertyErrors, allErrors) { hasError = !isValid; if (hasError) { //set the error message to the server message scope.errorMsg = propertyErrors[0].errorMsg; //flag that the current validator is invalid formCtrl.$setValidity('valPropertyMsg', false); + startWatch(); } else { scope.errorMsg = ""; //flag that the current validator is valid formCtrl.$setValidity('valPropertyMsg', true); + stopWatch(); } }); //when the element is disposed we need to unsubscribe! // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain // but they are a different callback instance than the above. - element.bind('$destroy', function() { + element.bind('$destroy', function () { + stopWatch(); serverValidationManager.unsubscribe(scope.property.alias, ""); }); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js new file mode 100644 index 0000000000..1d04fd9b34 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertyvalidator.directive.js @@ -0,0 +1,67 @@ +/** +* @ngdoc directive +* @name umbraco.directives.directive:valPropertyValidator +* @restrict A +* @description Performs any custom property value validation checks on the client side. This allows property editors to be highly flexible when it comes to validation + on the client side. Typically if a property editor stores a primitive value (i.e. string) then the client side validation can easily be taken care of + with standard angular directives such as ng-required. However since some property editors store complex data such as JSON, a given property editor + might require custom validation. This directive can be used to validate an Umbraco property in any way that a developer would like by specifying a + callback method to perform the validation. The result of this method must return an object in the format of + {isValid: true, errorKey: 'required', errorMsg: 'Something went wrong' } + The error message returned will also be displayed for the property level validation message. + This directive should only be used when dealing with complex models, if custom validation needs to be performed with primitive values, use the simpler + angular validation directives instead since this will watch the entire model. +**/ + +function valPropertyValidator(serverValidationManager) { + return { + scope: { + valPropertyValidator: "=" + }, + + // The element must have ng-model attribute and be inside an umbProperty directive + require: ['ngModel', '^umbProperty'], + + restrict: "A", + + link: function (scope, element, attrs, ctrls) { + + var modelCtrl = ctrls[0]; + var propCtrl = ctrls[1]; + + // Check whether the scope has a valPropertyValidator method + if (!scope.valPropertyValidator || !angular.isFunction(scope.valPropertyValidator)) { + throw new Error('val-property-validator directive must specify a function to call'); + } + + var initResult = scope.valPropertyValidator(); + + // Validation method + var validate = function (viewValue) { + // Calls the validition method + var result = scope.valPropertyValidator(); + if (!result.errorKey || result.isValid === undefined || !result.errorMsg) { + throw "The result object from valPropertyValidator does not contain required properties: isValid, errorKey, errorMsg"; + } + if (result.isValid === true) { + // Tell the controller that the value is valid + modelCtrl.$setValidity(result.errorKey, true); + propCtrl.setPropertyError(null); + } + else { + // Tell the controller that the value is invalid + modelCtrl.$setValidity(result.errorKey, false); + propCtrl.setPropertyError(result.errorMsg); + } + }; + + // Formatters are invoked when the model is modified in the code. + modelCtrl.$formatters.push(validate); + + // Parsers are called as soon as the value in the form input is modified + modelCtrl.$parsers.push(validate); + + } + }; +} +angular.module('umbraco.directives.validation').directive("valPropertyValidator", valPropertyValidator); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js index fbc91dcb11..9d944aabcc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valserver.directive.js @@ -7,14 +7,49 @@ **/ function valServer(serverValidationManager) { return { - require: 'ngModel', + require: ['ngModel', '^umbProperty'], restrict: "A", - link: function (scope, element, attr, ctrl) { - - if (!scope.model || !scope.model.alias){ - throw "valServer can only be used in the scope of a content property object"; + link: function (scope, element, attr, ctrls) { + + var modelCtrl = ctrls[0]; + var umbPropCtrl = ctrls[1]; + + var watcher = null; + + //Need to watch the value model for it to change, previously we had subscribed to + //modelCtrl.$viewChangeListeners but this is not good enough if you have an editor that + // doesn't specifically have a 2 way ng binding. This is required because when we + // have a server error we actually invalidate the form which means it cannot be + // resubmitted. So once a field is changed that has a server error assigned to it + // we need to re-validate it for the server side validator so the user can resubmit + // the form. Of course normal client-side validators will continue to execute. + function startWatch() { + //if there's not already a watch + if (!watcher) { + watcher = scope.$watch(function () { + return modelCtrl.$modelValue; + }, function (newValue, oldValue) { + + if (!newValue || angular.equals(newValue, oldValue)) { + return; + } + + if (modelCtrl.$invalid) { + modelCtrl.$setValidity('valServer', true); + stopWatch(); + } + }, true); + } } - var currentProperty = scope.model; + + function stopWatch() { + if (watcher) { + watcher(); + watcher = null; + } + } + + var currentProperty = umbPropCtrl.property; //default to 'value' if nothing is set var fieldName = "value"; @@ -25,33 +60,20 @@ function valServer(serverValidationManager) { fieldName = attr.valServer; } } - - //Need to watch the value model for it to change, previously we had subscribed to - //ctrl.$viewChangeListeners but this is not good enough if you have an editor that - // doesn't specifically have a 2 way ng binding. This is required because when we - // have a server error we actually invalidate the form which means it cannot be - // resubmitted. So once a field is changed that has a server error assigned to it - // we need to re-validate it for the server side validator so the user can resubmit - // the form. Of course normal client-side validators will continue to execute. - scope.$watch(function() { - return ctrl.$modelValue; - }, function (newValue) { - if (ctrl.$invalid) { - ctrl.$setValidity('valServer', true); - } - }); //subscribe to the server validation changes serverValidationManager.subscribe(currentProperty.alias, fieldName, function (isValid, propertyErrors, allErrors) { if (!isValid) { - ctrl.$setValidity('valServer', false); + modelCtrl.$setValidity('valServer', false); //assign an error msg property to the current validator - ctrl.errorMsg = propertyErrors[0].errorMsg; + modelCtrl.errorMsg = propertyErrors[0].errorMsg; + startWatch(); } else { - ctrl.$setValidity('valServer', true); + modelCtrl.$setValidity('valServer', true); //reset the error message - ctrl.errorMsg = ""; + modelCtrl.errorMsg = ""; + stopWatch(); } }); @@ -59,6 +81,7 @@ function valServer(serverValidationManager) { // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain // but they are a different callback instance than the above. element.bind('$destroy', function () { + stopWatch(); serverValidationManager.unsubscribe(currentProperty.alias, fieldName); }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js index 89bc626d6a..f14492e88a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js @@ -2,11 +2,23 @@ function ColorPickerController($scope) { $scope.toggleItem = function (color) { if ($scope.model.value == color) { $scope.model.value = ""; + //this is required to re-validate + $scope.propertyForm.modelValue.$setViewValue($scope.model.value); } else { $scope.model.value = color; + //this is required to re-validate + $scope.propertyForm.modelValue.$setViewValue($scope.model.value); } }; + // Method required by the valPropertyValidator directive (returns true if the property editor has at least one color selected) + $scope.validateMandatory = function () { + return { + isValid: !$scope.model.validation.mandatory || ($scope.model.value != null && $scope.model.value != ""), + errorMsg: "Value cannot be empty", + errorKey: "required" + }; + } $scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html index d2bf2cf87c..a493fffdd8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html @@ -12,4 +12,6 @@ + + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js index 9106e2b334..37dea64c21 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js @@ -21,6 +21,11 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag fileManager.setFiles($scope.model.alias, []); //clear the current files $scope.files = []; + if ($scope.propertyForm.fileCount) { + //this is required to re-validate + $scope.propertyForm.fileCount.$setViewValue($scope.files.length); + } + } /** this method is used to initialize the data and to re-initialize it if the server value is changed */ @@ -71,6 +76,15 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag initialize(); + // Method required by the valPropertyValidator directive (returns true if the property editor has at least one file selected) + $scope.validateMandatory = function () { + return { + isValid: !$scope.model.validation.mandatory || ((($scope.persistedFiles != null && $scope.persistedFiles.length > 0) || ($scope.files != null && $scope.files.length > 0)) && !$scope.clearFiles), + errorMsg: "Value cannot be empty", + errorKey: "required" + }; + } + //listen for clear files changes to set our model to be sent up to the server $scope.$watch("clearFiles", function (isCleared) { if (isCleared == true) { @@ -80,6 +94,8 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag else { //reset to original value $scope.model.value = $scope.originalValue; + //this is required to re-validate + $scope.propertyForm.fileCount.$setViewValue($scope.files.length); } }); @@ -96,6 +112,10 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag $scope.files.push({ alias: $scope.model.alias, file: args.files[i] }); newVal += args.files[i].name + ","; } + + //this is required to re-validate + $scope.propertyForm.fileCount.$setViewValue($scope.files.length); + //set clear files to false, this will reset the model too $scope.clearFiles = false; //set the model value to be the concatenation of files selected. Please see the notes diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html index afcba5a493..e200b2726c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html @@ -25,4 +25,7 @@
  • {{file.file.name}}
  • - + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js index 4577430195..9c0d016189 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js @@ -12,31 +12,38 @@ angular.module("umbraco") $scope.isLoading = false; //load current value - $scope.currentTags = []; + if ($scope.model.value) { - if ($scope.model.config.storageType && $scope.model.config.storageType === "Json") { - //it's a json array already - $scope.currentTags = $scope.model.value; - } - else { + if (!$scope.model.config.storageType || $scope.model.config.storageType !== "Json") { //it is csv if (!$scope.model.value) { - $scope.currentTags = []; + $scope.model.value = []; } else { - $scope.currentTags = $scope.model.value.split(","); + $scope.model.value = $scope.model.value.split(","); } } } + else { + $scope.model.value = []; + } + + // Method required by the valPropertyValidator directive (returns true if the property editor has at least one tag selected) + $scope.validateMandatory = function () { + return { + isValid: !$scope.model.validation.mandatory || ($scope.model.value != null && $scope.model.value.length > 0), + errorMsg: "Value cannot be empty", + errorKey: "required" + }; + } //Helper method to add a tag on enter or on typeahead select function addTag(tagToAdd) { - if (tagToAdd.length > 0) { - if ($scope.currentTags.indexOf(tagToAdd) < 0) { - $scope.currentTags.push(tagToAdd); - //update the model value, this is required if there's a server validation error, it can - // only then be cleared if the model changes - $scope.model.value = $scope.currentTags; + if (tagToAdd != null && tagToAdd.length > 0) { + if ($scope.model.value.indexOf(tagToAdd) < 0) { + $scope.model.value.push(tagToAdd); + //this is required to re-validate + $scope.propertyForm.tagCount.$setViewValue($scope.model.value.length); } } } @@ -47,7 +54,6 @@ angular.module("umbraco") if ($element.find('.tags-' + $scope.model.alias).parent().find(".tt-dropdown-menu .tt-cursor").length === 0) { //this is required, otherwise the html form will attempt to submit. e.preventDefault(); - $scope.addTag(); } } @@ -66,36 +72,26 @@ angular.module("umbraco") $scope.removeTag = function (tag) { - var i = $scope.currentTags.indexOf(tag); + var i = $scope.model.value.indexOf(tag); if (i >= 0) { - $scope.currentTags.splice(i, 1); - //update the model value, this is required if there's a server validation error, it can - // only then be cleared if the model changes - $scope.model.value = $scope.currentTags; + $scope.model.value.splice(i, 1); + //this is required to re-validate + $scope.propertyForm.tagCount.$setViewValue($scope.model.value.length); } }; - //sync model on submit, always push up a json array - $scope.$on("formSubmitting", function (ev, args) { - $scope.model.value = $scope.currentTags; - }); - //vice versa $scope.model.onValueChanged = function (newVal, oldVal) { //update the display val again if it has changed from the server $scope.model.value = newVal; - if ($scope.model.config.storageType && $scope.model.config.storageType === "Json") { - //it's a json array already - $scope.currentTags = $scope.model.value; - } - else { + if (!$scope.model.config.storageType || $scope.model.config.storageType !== "Json") { //it is csv if (!$scope.model.value) { - $scope.currentTags = []; + $scope.model.value = []; } else { - $scope.currentTags = $scope.model.value.split(","); + $scope.model.value = $scope.model.value.split(","); } } }; @@ -110,14 +106,14 @@ angular.module("umbraco") }); // remove current tags from the list return $.grep(tagList, function (tag) { - return ($.inArray(tag.value, $scope.currentTags) === -1); + return ($.inArray(tag.value, $scope.model.value) === -1); }); } // helper method to remove current tags function removeCurrentTagsFromSuggestions(suggestions) { return $.grep(suggestions, function (suggestion) { - return ($.inArray(suggestion.value, $scope.currentTags) === -1); + return ($.inArray(suggestion.value, $scope.model.value) === -1); }); } @@ -158,7 +154,6 @@ angular.module("umbraco") // name = the data set name, we'll make this the tag group name name: $scope.model.config.group, displayKey: "value", - //source: tagsHound.ttAdapter(), source: function (query, cb) { tagsHound.get(query, function (suggestions) { cb(removeCurrentTagsFromSuggestions(suggestions)); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html index 1a0452e242..b1049a2309 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html @@ -1,21 +1,26 @@
    - -
    - Loading... + +
    + Loading...
    - + + + + + placeholder="@placeholders_enterTags" /> +
    +
    \ No newline at end of file