Add validateUnique form input validator
This adds the directive used for validating the uniqueness of a form input value. It also updates the metadata tree widget to use it. Closes-Bug: 1556255 Change-Id: Icc6ce0ef5212801fa1531196fb0705760e9c3b6e
This commit is contained in:
parent
16660b07da
commit
21c019611e
90
horizon/static/framework/util/validators/validate-unique.js
Normal file
90
horizon/static/framework/util/validators/validate-unique.js
Normal file
@ -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
|
||||||
|
* ```
|
||||||
|
* <input type="number" ng-model="value"
|
||||||
|
* validate-unique="[80,443]">
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* <input type="string" ng-model="value"
|
||||||
|
* validate-unique="ctrl.validateUniqueName">
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
@ -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 =
|
||||||
|
'<form>' +
|
||||||
|
'<input type="number" id="port" ng-model="port" validate-unique="ports">' +
|
||||||
|
'<input id="name" ng-model="name" validate-unique="names">' +
|
||||||
|
'<input id="protocol" ng-model="protocol" validate-unique="protocolIsUnique">' +
|
||||||
|
'</form>';
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
@ -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;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
ctrl.availableFilter = availableFilter;
|
ctrl.availableFilter = availableFilter;
|
||||||
ctrl.quickFilter = quickFilter;
|
ctrl.quickFilter = quickFilter;
|
||||||
|
ctrl.checkNameUnique = checkNameUnique;
|
||||||
ctrl.text = angular.extend({}, defaults.text, ctrl.text);
|
ctrl.text = angular.extend({}, defaults.text, ctrl.text);
|
||||||
if (!ctrl.tree) {
|
if (!ctrl.tree) {
|
||||||
ctrl.tree = new metadataTreeService.Tree(ctrl.available, ctrl.existing);
|
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;
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<span class="input-group-addon" ng-bind="::ctrl.text.custom"></span>
|
<span class="input-group-addon" ng-bind="::ctrl.text.custom"></span>
|
||||||
<input class="form-control" type="text" name="customItem"
|
<input class="form-control" type="text" name="customItem"
|
||||||
ng-model="ctrl.customItem"
|
ng-model="ctrl.customItem"
|
||||||
metadata-tree-unique/>
|
validate-unique="ctrl.checkNameUnique"/>
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button type='button' class="btn btn-primary"
|
<button type='button' class="btn btn-primary"
|
||||||
ng-click="ctrl.tree.addCustom(ctrl.customItem); ctrl.customItem=''"
|
ng-click="ctrl.tree.addCustom(ctrl.customItem); ctrl.customItem=''"
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
* |--------------------------------------------------------------------------------------------|
|
* |--------------------------------------------------------------------------------------------|
|
||||||
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTree `metadataTree`} |
|
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTree `metadataTree`} |
|
||||||
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTreeItem `metadataTreeItem`} |
|
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTreeItem `metadataTreeItem`} |
|
||||||
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTreeUnique `metadataTreeUnique`} |
|
|
||||||
* | {@link horizon.framework.widgets.metadata.tree.controller:MetadataTreeController `MetadataTreeController`} |
|
* | {@link horizon.framework.widgets.metadata.tree.controller:MetadataTreeController `MetadataTreeController`} |
|
||||||
* | {@link horizon.framework.widgets.metadata.tree.controller:MetadataTreeItemController `MetadataTreeItemController`} |
|
* | {@link horizon.framework.widgets.metadata.tree.controller:MetadataTreeItemController `MetadataTreeItemController`} |
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user