diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js index 047d67ffef..e26f622d2e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js @@ -94,6 +94,7 @@ Use this directive to render an umbraco button. The directive can be used to gen } }); + //TODO: This doesn't seem necessary? UmbButtonController.$inject = ['$timeout']; function UmbButtonController($timeout) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js b/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js index b811183251..158c9202eb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/filemanager.service.js @@ -43,9 +43,9 @@ function fileManager() { metaData = args.metaData; } - //this will clear the files for the current property and then add the new ones for the current property + //this will clear the files for the current property/culture and then add the new ones for the current property fileCollection = _.reject(fileCollection, function (item) { - return item.alias === args.propertyAlias; + return item.alias === args.propertyAlias && (!args.culture || args.culture === item.culture); }); for (var i = 0; i < args.files.length; i++) { //save the file object to the files collection 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 1c2ef37e0f..0e64e92bd7 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 @@ -7,33 +7,61 @@ * @function * * @description - * The controller for the file upload property editor. It is important to note that the $scope.model.value - * doesn't necessarily depict what is saved for this property editor. $scope.model.value can be empty when we - * are submitting files because in that case, we are adding files to the fileManager which is what gets peristed - * on the server. However, when we are clearing files, we are setting $scope.model.value to "{clearFiles: true}" - * to indicate on the server that we are removing files for this property. We will keep the $scope.model.value to - * be the name of the file selected (if it is a newly selected file) or keep it to be it's original value, this allows - * for the editors to check if the value has changed and to re-bind the property if that is true. + * The controller for the file upload property editor. * */ function fileUploadController($scope, $element, $compile, imageHelper, fileManager, umbRequestHelper, mediaHelper, angularHelper) { + + /* It is important to note that the $scope.model.value + * doesn't necessarily depict what is saved for this property editor. $scope.model.value can be empty when we + * are submitting files because in that case, we are adding files to the fileManager which is what gets peristed + * on the server. However, when we are clearing files, we are setting $scope.model.value to "{clearFiles: true}" + * to indicate on the server that we are removing files for this property. We will keep the $scope.model.value to + * be the name of the file selected (if it is a newly selected file) or keep it to be it's original value, this allows + * for the editors to check if the value has changed and to re-bind the property if that is true. + * */ + + var vm = this; + + vm.$onInit = onInit; + vm.$onChanges = onChanges; + vm.$postLink = postLink; + vm.validateMandatory = validateMandatory; /** Clears the file collections when content is saving (if we need to clear) or after saved */ function clearFiles() { //clear the files collection (we don't want to upload any!) fileManager.setFiles({ - propertyAlias: $scope.model.alias, + propertyAlias: vm.propertyAlias, + culture: vm.culture, files: [] }); //clear the current files - $scope.files = []; + vm.files = []; - if ($scope.fileUploadForm) { - if ($scope.fileUploadForm.fileCount) { - angularHelper.revalidateNgModel($scope, $scope.fileUploadForm.fileCount); + if (vm.fileUploadForm) { + if (vm.fileUploadForm.fileCount) { + angularHelper.revalidateNgModel($scope, vm.fileUploadForm.fileCount); } } + } + /** Called when the component initializes */ + function onInit() { + + //check if there's a tabbed-content controller available and get the culture + vm.culture = (vm.tabbedContentCtrl && vm.tabbedContentCtrl.content.language) ? vm.tabbedContentCtrl.content.language.culture : null; + + $scope.$watch(function () { + return vm.clearFiles; + }, clearFilesWatch); + + $scope.$on("filesSelected", onFilesSelected); + } + + /** Called when the component has linked all elements, this is when the form controller is available */ + function postLink() { + initialize(); } /** this method is used to initialize the data and to re-initialize it if the server value is changed */ @@ -48,28 +76,28 @@ //this is used in order to tell the umb-single-file-upload directive to //rebuild the html input control (and thus clearing the selected file) since //that is the only way to manipulate the html for the file input control. - $scope.rebuildInput = { + vm.rebuildInput = { index: index }; //clear the current files - $scope.files = []; + vm.files = []; //store the original value so we can restore it if the user clears and then cancels clearing. - $scope.originalValue = $scope.model.value; + vm.originalValue = vm.modelValue; //create the property to show the list of files currently saved - if ($scope.model.value != "" && $scope.model.value != undefined) { + if (vm.modelValue != "" && vm.modelValue != undefined) { - var images = $scope.model.value.split(","); + var images = vm.modelValue.split(","); - $scope.persistedFiles = _.map(images, function (item) { + vm.persistedFiles = _.map(images, function (item) { return { file: item, isImage: imageHelper.detectIfImageByExtension(item) }; }); } else { - $scope.persistedFiles = []; + vm.persistedFiles = []; } - _.each($scope.persistedFiles, function (file) { + _.each(vm.persistedFiles, function (file) { var thumbnailUrl = umbRequestHelper.getApiUrl( "imagesApiBaseUrl", @@ -82,115 +110,120 @@ file.extension = extension.toLowerCase(); }); - $scope.clearFiles = false; + vm.clearFiles = false; } - initialize(); - - // Method required by the valPropertyValidator directive (returns true if the property editor has at least one file selected) - $scope.validateMandatory = function () { + /** Method required by the valPropertyValidator directive (returns true if the property editor has at least one file selected) */ + function validateMandatory() { return { - isValid: !$scope.model.validation.mandatory || ((($scope.persistedFiles != null && $scope.persistedFiles.length > 0) || ($scope.files != null && $scope.files.length > 0)) && !$scope.clearFiles), + isValid: !vm.validation.mandatory || (((vm.persistedFiles != null && vm.persistedFiles.length > 0) || (vm.files != null && vm.files.length > 0)) && !vm.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 (newVal, oldVal) { - if (newVal == true) { - $scope.model.value = { clearFiles: true }; + /** + * listen for clear files changes to set our model to be sent up to the server + * @param {any} newVal + * @param {any} oldVal + */ + function clearFilesWatch(newVal, oldVal) { + //TODO: Call a callback method instead of setting the value directly inside this component + + if (newVal === true) { + vm.modelValue = { clearFiles: true }; clearFiles(); } else if (newVal !== oldVal) { //reset to original value - $scope.model.value = $scope.originalValue; + vm.modelValue = vm.originalValue; //this is required to re-validate - if ($scope.fileUploadForm) { - angularHelper.revalidateNgModel($scope, $scope.fileUploadForm.fileCount); + if (vm.fileUploadForm) { + angularHelper.revalidateNgModel($scope, vm.fileUploadForm.fileCount); } } - }); + } + + /** + * Watch for model changes + * @param {any} changes + */ + function onChanges(changes) { + + if (changes.modelValue && !changes.modelValue.isFirstChange() + && changes.modelValue.currentValue !== null && changes.modelValue.currentValue !== undefined + && changes.modelValue.currentValue !== changes.modelValue.previousValue) { - //listen for when the model value has changed - $scope.$watch("model.value", function (newVal, oldVal) { - //cannot just check for !newVal because it might be an empty string which we - //want to look for. - if (newVal !== null && newVal !== undefined && newVal !== oldVal) { // here we need to check if the value change needs to trigger an update in the UI. // if the value is only changed in the controller and not in the server values, we do not // want to trigger an update yet. // we can however no longer rely on checking values in the controller vs. values from the server // to determine whether to update or not, since you could potentially be uploading a file with // the exact same name - in that case we need to reinitialize to show the newly uploaded file. - if (newVal.clearFiles !== true && !newVal.selectedFiles) { - initialize($scope.rebuildInput.index + 1); + if (changes.modelValue.currentValue.clearFiles !== true && !changes.modelValue.currentValue.selectedFiles) { + initialize(vm.rebuildInput.index + 1); } } - }); + } + + /** + * listen for when a file is selected + * @param {any} event + * @param {any} args + */ + function onFilesSelected(event, args) { + //set the files collection + fileManager.setFiles({ + propertyAlias: vm.propertyAlias, + files: args.files, + culture: vm.culture + }); + //clear the current files + vm.files = []; + var newVal = ""; + for (var i = 0; i < args.files.length; i++) { + //save the file object to the files collection + vm.files.push({ alias: vm.propertyAlias, file: args.files[i] }); + newVal += args.files[i].name + ","; + } + + //this is required to re-validate + angularHelper.revalidateNgModel($scope, vm.fileUploadForm.fileCount); + + //set clear files to false, this will reset the model too + vm.clearFiles = false; + + //TODO: Call a callback method instead of setting the value directly inside this component + + //set the model value to be the concatenation of files selected. Please see the notes + // in the description of this controller, it states that this value isn't actually used for persistence, + // but we need to set it so that the editor and the server can detect that it's been changed, and it is used for validation. + vm.modelValue = { selectedFiles: newVal.trimEnd(",") }; + + //need to explicity setDirty here as file upload field can't track dirty & we can't use the fileCount (hidden field/model) + vm.fileUploadForm.$setDirty(); + } + }; - /** - * Directive used to capture the tabbed-content directive to get the current variant culture - * @param {any} fileManager - * @param {any} angularHelper - */ - function umbFileUploadEditorDirective(fileManager, angularHelper) { - var directive = { - require: '?^^tabbedContent', - restrict: 'E', - replace: true, - templateUrl: 'views/propertyeditors/fileupload/fileupload.directive.html', - controller: 'Umbraco.PropertyEditors.FileUploadController', - scope: { - model: "=", - }, - link: function (scope, element, attrs, ctrl) { - //listen for when a file is selected - scope.$on("filesSelected", function (event, args) { - - //check if there's a tabbed-content controller available and get the culture - var culture = (ctrl && ctrl.content.language) ? ctrl.content.language.culture : null; - - //set the files collection - fileManager.setFiles({ - propertyAlias: scope.model.alias, - files: args.files, - culture: culture - }); - //clear the current files - scope.files = []; - var newVal = ""; - for (var i = 0; i < args.files.length; i++) { - //save the file object to the scope's files collection - scope.files.push({ alias: scope.model.alias, file: args.files[i] }); - newVal += args.files[i].name + ","; - } - - //this is required to re-validate - angularHelper.revalidateNgModel(scope, scope.fileUploadForm.fileCount); - - //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 - // in the description of this controller, it states that this value isn't actually used for persistence, - // but we need to set it so that the editor and the server can detect that it's been changed, and it is used for validation. - scope.model.value = { selectedFiles: newVal.trimEnd(",") }; - - //need to explicity setDirty here as file upload field can't track dirty & we can't use the fileCount (hidden field/model) - scope.fileUploadForm.$setDirty(); - }); - - } - }; - - return directive; - } + var umbFileUploadEditorComponent = { + templateUrl: 'views/propertyeditors/fileupload/umbfileuploadeditor.component.html', + require: { + tabbedContentCtrl: '?^^tabbedContent' + }, + bindings: { + modelValue: "=", + propertyAlias: "@", + validation: "<" + }, + controllerAs: 'vm', + controller: fileUploadController + }; angular.module("umbraco") - .controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController) - .directive('umbFileUploadEditor', umbFileUploadEditorDirective) + //.controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController) + .component('umbFileUploadEditor', umbFileUploadEditorComponent) .run(function (mediaHelper, umbRequestHelper, assetsService) { if (mediaHelper && mediaHelper.registerFileResolver) { 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 086f7e20d3..b56016f11f 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 @@ -1,3 +1,6 @@