diff --git a/horizon/static/framework/util/validators/validate-unique.js b/horizon/static/framework/util/validators/validate-unique.js new file mode 100644 index 0000000000..1b0030a699 --- /dev/null +++ b/horizon/static/framework/util/validators/validate-unique.js @@ -0,0 +1,90 @@ +/* + * Copyright 2016 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function () { + 'use strict'; + + /** + * @ngdoc directive + * @name horizon.framework.util.validators:validateUnique + * @element ng-model + * @description + * The `validateUnique` directive provides validation for form input elements to ensure + * values are unique. + * + * The form input value can be validated against an array of values or a custom validator + * function can be provided. If an array of values is specified, the validator returns true + * if the value is not found in the array. If a function is specified, the validator returns + * the value of the function which should evaluate to true or false. + * + * @restrict A + * + * @example + * ``` + * + * ``` + * + * @example + * ``` + * + * + * ctrl.validateUniqueName = function(value) { + * return !existingItems.some(function(item) { + * return item.leaf && item.leaf.name === value; + * }); + * }; + * ``` + */ + + angular + .module('horizon.framework.util.validators') + .directive('validateUnique', validateUnique); + + function validateUnique() { + var directive = { + require: 'ngModel', + restrict: 'A', + link: link + }; + + return directive; + + ////////// + + function link(scope, element, attrs, ctrl) { + ctrl.$parsers.push(validate); + ctrl.$formatters.push(validate); + + attrs.$observe('validateUnique', function () { + validate(ctrl.$modelValue); + }); + + function validate(value) { + var param = scope.$eval(attrs.validateUnique); + var unique = true; + if (angular.isArray(param) && param.length > 0) { + unique = param.indexOf(value) < 0; + } else if (angular.isFunction(param)) { + unique = param(value); + } + ctrl.$setValidity('unique', unique); + return value; + } + + } + } +})(); diff --git a/horizon/static/framework/util/validators/validators.spec.js b/horizon/static/framework/util/validators/validators.spec.js index 5256accf0f..b08fcded24 100644 --- a/horizon/static/framework/util/validators/validators.spec.js +++ b/horizon/static/framework/util/validators/validators.spec.js @@ -91,5 +91,99 @@ }); }); + describe('validateUnique directive', function () { + + var scope, port, name, protocol; + var items = [{ id: '1', protocol: 'HTTP' }, + { id: '2', protocol: 'HTTPS' }, + { id: '3', protocol: 'TCP' }]; + var markup = + '
' + + '' + + '' + + '' + + '
'; + + function protocolIsUnique(value) { + return !items.some(function(item) { + return item.protocol === value; + }); + } + + beforeEach(inject(function ($injector) { + var compile = $injector.get('$compile'); + scope = $injector.get('$rootScope').$new(); + + // generate dom from markup + var element = compile(markup)(scope); + port = element.children('#port'); + name = element.children('#name'); + protocol = element.children('#protocol'); + + // set initial data + scope.ports = [80, 443]; + scope.names = ['name1', 'name2']; + scope.protocolIsUnique = protocolIsUnique; + scope.$apply(); + })); + + it('should be initially empty', function () { + expect(port.val()).toEqual(''); + expect(name.val()).toEqual(''); + expect(protocol.val()).toEqual(''); + expect(port.hasClass('ng-valid')).toBe(true); + expect(name.hasClass('ng-valid')).toBe(true); + expect(protocol.hasClass('ng-valid')).toBe(true); + }); + + it('should be invalid if values are not unique', function () { + scope.port = 80; + scope.name = 'name1'; + scope.protocol = 'TCP'; + scope.$apply(); + expect(port.hasClass('ng-valid')).toBe(false); + expect(name.hasClass('ng-valid')).toBe(false); + expect(protocol.hasClass('ng-valid')).toBe(false); + }); + + it('should be valid if values are unique', function () { + scope.port = 81; + scope.name = 'name3'; + scope.protocol = 'TERMINATED_HTTPS'; + scope.$apply(); + expect(port.hasClass('ng-valid')).toBe(true); + expect(name.hasClass('ng-valid')).toBe(true); + expect(protocol.hasClass('ng-valid')).toBe(true); + }); + + it('should be valid if param is undefined', function () { + delete scope.ports; + scope.port = 80; + scope.$apply(); + expect(port.hasClass('ng-valid')).toBe(true); + }); + + it('should be valid if param is a string', function () { + scope.ports = '80'; + scope.port = 80; + scope.$apply(); + expect(port.hasClass('ng-valid')).toBe(true); + }); + + it('should be valid if param is a number', function () { + scope.ports = 80; + scope.port = 80; + scope.$apply(); + expect(port.hasClass('ng-valid')).toBe(true); + }); + + it('should be valid if param is an object', function () { + scope.ports = { port: 80 }; + scope.port = 80; + scope.$apply(); + expect(port.hasClass('ng-valid')).toBe(true); + }); + }); + }); })(); diff --git a/horizon/static/framework/widgets/metadata/tree/metadata-tree-unique.directive.js b/horizon/static/framework/widgets/metadata/tree/metadata-tree-unique.directive.js deleted file mode 100644 index 6415831bd9..0000000000 --- a/horizon/static/framework/widgets/metadata/tree/metadata-tree-unique.directive.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2015, Intel Corp. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -(function () { - 'use strict'; - - angular - .module('horizon.framework.widgets.metadata.tree') - .directive('metadataTreeUnique', metadataTreeUnique); - - /** - * @ngdoc directive - * @name metadataTreeUnique - * @restrict A - * - * @description - * The `metadataTreeUnique` helper directive provides validation - * for field which value should be unique new Item - */ - function metadataTreeUnique() { - var directive = { - restrict: 'A', - require: ['^metadataTree', 'ngModel'], - link: link - }; - - return directive; - - function link(scope, elm, attrs, controllers) { - var metadataTree = controllers[0]; - var ngModel = controllers[1]; - ngModel.$validators.unique = function (modelValue, viewValue) { - return !metadataTree.tree.flatTree.some(function (item) { - return item.leaf && item.leaf.name === viewValue; - }); - }; - } - } - -})(); diff --git a/horizon/static/framework/widgets/metadata/tree/metadata-tree.controller.js b/horizon/static/framework/widgets/metadata/tree/metadata-tree.controller.js index 5baef0da97..1ab017f032 100644 --- a/horizon/static/framework/widgets/metadata/tree/metadata-tree.controller.js +++ b/horizon/static/framework/widgets/metadata/tree/metadata-tree.controller.js @@ -36,6 +36,7 @@ ctrl.availableFilter = availableFilter; ctrl.quickFilter = quickFilter; + ctrl.checkNameUnique = checkNameUnique; ctrl.text = angular.extend({}, defaults.text, ctrl.text); if (!ctrl.tree) { ctrl.tree = new metadataTreeService.Tree(ctrl.available, ctrl.existing); @@ -71,6 +72,16 @@ } return item.label.indexOf(text) > -1 || item.leaf.name.indexOf(text) > -1; } + + /** + * Function used by the validateUnique directive to validate that a custom key name + * is unique. + */ + function checkNameUnique(name) { + return !ctrl.tree.flatTree.some(function (item) { + return item.leaf && item.leaf.name === name; + }); + } } })(); diff --git a/horizon/static/framework/widgets/metadata/tree/metadata-tree.html b/horizon/static/framework/widgets/metadata/tree/metadata-tree.html index ffe060c4f9..d36413eb97 100644 --- a/horizon/static/framework/widgets/metadata/tree/metadata-tree.html +++ b/horizon/static/framework/widgets/metadata/tree/metadata-tree.html @@ -32,7 +32,7 @@ + validate-unique="ctrl.checkNameUnique"/>