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.
This commit is contained in:
@@ -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;
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -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: "<div ng-show=\"errorMsg != ''\" class='alert alert-error property-error' >{{errorMsg}}</div>",
|
||||
|
||||
template: "<div ng-show=\"errorMsg != ''\" class='alert alert-error property-error' >{{errorMsg}}</div>",
|
||||
|
||||
/**
|
||||
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, "");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,4 +12,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<input type="hidden" name="modelValue" ng-model="model.value" val-property-validator="validateMandatory"/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,4 +25,7 @@
|
||||
<li ng-repeat="file in files">{{file.file.name}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="fileCount" ng-model="files.length" val-property-validator="validateMandatory"/>
|
||||
|
||||
</div>
|
||||
@@ -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));
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.TagsController" class="umb-editor umb-tags">
|
||||
|
||||
<div ng-if="isLoading">
|
||||
<localize key="loading">Loading</localize>...
|
||||
|
||||
<div ng-if="isLoading">
|
||||
<localize key="loading">Loading</localize>...
|
||||
</div>
|
||||
|
||||
<div ng-if="!isLoading">
|
||||
<span ng-repeat="tag in currentTags" ng-click="removeTag(tag)" class="label label-primary tag">
|
||||
|
||||
<input type="hidden" name="tagCount" ng-model="model.value.length" val-property-validator="validateMandatory" />
|
||||
|
||||
<span ng-repeat="tag in model.value" ng-click="$parent.removeTag(tag)" class="label label-primary tag">
|
||||
<span ng-bind-html="tag"></span>
|
||||
<i class="icon icon-delete"></i>
|
||||
</span>
|
||||
|
||||
<input type="text"
|
||||
class="typeahead tags-{{model.alias}}"
|
||||
ng-model="$parent.tagToAdd"
|
||||
ng-model="$parent.tagToAdd"
|
||||
ng-keydown="$parent.addTagOnEnter($event)"
|
||||
on-blur="$parent.addTag()"
|
||||
on-blur="$parent.addTag()"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_enterTags" />
|
||||
placeholder="@placeholders_enterTags" />
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
Reference in New Issue
Block a user