Add property-collection-editor directive
Refactor the current code to create a reusable component for editing the contents of property collections. The new component has replaced code in the existing port create/edit views, and will do the same for the node views. Change-Id: Iea81609450acc6a72ab3cbe6be070f9b845aaa9b
This commit is contained in:
@@ -29,9 +29,9 @@
|
|||||||
'horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
'horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
||||||
'horizon.dashboard.admin.ironic.form-field.service',
|
'horizon.dashboard.admin.ironic.form-field.service',
|
||||||
'horizon.app.core.openstack-service-api.ironic',
|
'horizon.app.core.openstack-service-api.ironic',
|
||||||
|
'horizon.dashboard.admin.ironic.property-collection.service',
|
||||||
'ctrl',
|
'ctrl',
|
||||||
'node'
|
'node'];
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Utility class used to manage local-link-connection
|
* @description Utility class used to manage local-link-connection
|
||||||
@@ -141,13 +141,9 @@
|
|||||||
validDatapathIdPattern,
|
validDatapathIdPattern,
|
||||||
formFieldService,
|
formFieldService,
|
||||||
ironic,
|
ironic,
|
||||||
|
propertyCollectionService,
|
||||||
ctrl,
|
ctrl,
|
||||||
node) {
|
node) {
|
||||||
ctrl.port = {
|
|
||||||
extra: {},
|
|
||||||
node_uuid: node.uuid
|
|
||||||
};
|
|
||||||
|
|
||||||
ctrl.address = new formFieldService.FormField({
|
ctrl.address = new formFieldService.FormField({
|
||||||
id: "macAddress",
|
id: "macAddress",
|
||||||
title: gettext("MAC address"),
|
title: gettext("MAC address"),
|
||||||
@@ -186,7 +182,8 @@
|
|||||||
var field = ctrl.portgroup_uuid;
|
var field = ctrl.portgroup_uuid;
|
||||||
|
|
||||||
if (portgroups.length > 0) {
|
if (portgroups.length > 0) {
|
||||||
field.portgroups.push({uuid: null, name: gettext("Select a portgroup")});
|
field.portgroups.push({uuid: null,
|
||||||
|
name: gettext("Select a portgroup")});
|
||||||
}
|
}
|
||||||
field.portgroups = field.portgroups.concat(portgroups);
|
field.portgroups = field.portgroups.concat(portgroups);
|
||||||
|
|
||||||
@@ -195,6 +192,13 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ctrl.extra = new propertyCollectionService.PropertyCollection({
|
||||||
|
id: 'extra',
|
||||||
|
title: gettext('Extras'),
|
||||||
|
addPropertyLabel: gettext('Add Extra'),
|
||||||
|
placeholder: gettext('Property Name')
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel the modal
|
* Cancel the modal
|
||||||
*
|
*
|
||||||
@@ -203,26 +207,5 @@
|
|||||||
ctrl.cancel = function() {
|
ctrl.cancel = function() {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$uibModalInstance.dismiss('cancel');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a port metadata property
|
|
||||||
*
|
|
||||||
* @param {string} propertyName - Name of the property
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
ctrl.deleteExtra = function(propertyName) {
|
|
||||||
delete ctrl.port.extra[propertyName];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the specified port metadata property already exists
|
|
||||||
*
|
|
||||||
* @param {string} propertyName - Name of the metadata property
|
|
||||||
* @return {boolean} True if the property already exists,
|
|
||||||
* otherwise false
|
|
||||||
*/
|
|
||||||
ctrl.checkExtraUnique = function(propertyName) {
|
|
||||||
return !(propertyName in ctrl.port.extra);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@@ -26,49 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form id="AddExtraForm" name="AddExtraForm" style="margin-bottom:10px;">
|
<property-collection-editor collection="ctrl.extra"></property-collection-editor>
|
||||||
<label for="extras" class="control-label" translate>Extras</label>
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<span class="input-group-addon"
|
|
||||||
style="width:25%;text-align:right">
|
|
||||||
Add Extra:</span>
|
|
||||||
<input class="form-control"
|
|
||||||
type="text"
|
|
||||||
ng-model="extraName"
|
|
||||||
validate-unique="ctrl.checkExtraUnique"
|
|
||||||
placeholder="{$ ::'Property Name' | translate $}"/>
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-primary"
|
|
||||||
type="button"
|
|
||||||
ng-disabled="!extraName || AddExtraForm.$invalid"
|
|
||||||
ng-click="ctrl.port.extra[extraName] = null;
|
|
||||||
extraName = null">
|
|
||||||
<span class="fa fa-plus"> </span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<form class="form-horizontal" id="ExtraForm" name="ExtraForm">
|
|
||||||
<div class="input-group input-group-sm"
|
|
||||||
ng-repeat="(propertyName, propertyValue) in ctrl.port.extra">
|
|
||||||
<span class="input-group-addon"
|
|
||||||
style="width:25%;text-align:right">
|
|
||||||
{$ propertyName $}
|
|
||||||
</span>
|
|
||||||
<input class="form-control"
|
|
||||||
type="text"
|
|
||||||
name="{$ propertyName $}"
|
|
||||||
ng-model="ctrl.port.extra[propertyName]"
|
|
||||||
ng-required="true"/>
|
|
||||||
<div class="input-group-btn">
|
|
||||||
<a class="btn btn-default"
|
|
||||||
ng-click="ctrl.deleteExtra(propertyName)">
|
|
||||||
<span class="fa fa-minus"> </span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<!--modal footer-->
|
<!--modal footer-->
|
||||||
<div class="modal-footer ng-scope">
|
<div class="modal-footer ng-scope">
|
||||||
@@ -79,7 +37,7 @@
|
|||||||
<button type="submit"
|
<button type="submit"
|
||||||
ng-disabled="CreatePortForm.$invalid ||
|
ng-disabled="CreatePortForm.$invalid ||
|
||||||
LocalLinkConnectionForm.$invalid ||
|
LocalLinkConnectionForm.$invalid ||
|
||||||
ExtraForm.$invalid"
|
!ctrl.extra.complete()"
|
||||||
ng-click="ctrl.submit()"
|
ng-click="ctrl.submit()"
|
||||||
class="btn btn-primary">
|
class="btn btn-primary">
|
||||||
{$ ::ctrl.submitButtonTitle $}
|
{$ ::ctrl.submitButtonTitle $}
|
||||||
|
@@ -54,9 +54,11 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
ctrl.createPort = function() {
|
ctrl.createPort = function() {
|
||||||
var port = angular.copy(ctrl.port);
|
var port = {
|
||||||
|
extra: ctrl.extra.properties,
|
||||||
port.address = ctrl.address.value;
|
node_uuid: node.id,
|
||||||
|
address: ctrl.address.value
|
||||||
|
};
|
||||||
|
|
||||||
var attr = ctrl.localLinkConnection.toPortAttr();
|
var attr = ctrl.localLinkConnection.toPortAttr();
|
||||||
if (attr) {
|
if (attr) {
|
||||||
|
@@ -89,7 +89,7 @@
|
|||||||
UNABLE_TO_UPDATE_CONNECTIVITY_ATTR_MSG);
|
UNABLE_TO_UPDATE_CONNECTIVITY_ATTR_MSG);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl.port.extra = angular.copy(port.extra);
|
ctrl.extra.properties = angular.copy(port.extra);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply updates to the port being edited
|
* Apply updates to the port being edited
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
patcher.buildPatch(port.local_link_connection,
|
patcher.buildPatch(port.local_link_connection,
|
||||||
ctrl.localLinkConnection.toPortAttr(),
|
ctrl.localLinkConnection.toPortAttr(),
|
||||||
"/local_link_connection");
|
"/local_link_connection");
|
||||||
patcher.buildPatch(port.extra, ctrl.port.extra, "/extra");
|
patcher.buildPatch(port.extra, ctrl.extra.properties, "/extra");
|
||||||
patcher.buildPatch(port.portgroup_uuid,
|
patcher.buildPatch(port.portgroup_uuid,
|
||||||
ctrl.portgroup_uuid.value,
|
ctrl.portgroup_uuid.value,
|
||||||
"/portgroup_uuid");
|
"/portgroup_uuid");
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Cray Inc.
|
||||||
|
*
|
||||||
|
* 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.dashboard.admin.ironic')
|
||||||
|
.directive('propertyCollectionEditor', PropertyCollectionEditor);
|
||||||
|
|
||||||
|
PropertyCollectionEditor.$inject = [
|
||||||
|
'horizon.dashboard.admin.ironic.basePath'
|
||||||
|
];
|
||||||
|
|
||||||
|
function PropertyCollectionEditor(basePath) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
collection: '='
|
||||||
|
},
|
||||||
|
templateUrl: basePath + '/property-collection-editor.html'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
@@ -0,0 +1,53 @@
|
|||||||
|
<div>
|
||||||
|
<!-- Add property to collection -->
|
||||||
|
<form id="add_{$ collection.id $}_form"
|
||||||
|
name="add_{$ collection.id $}_form"
|
||||||
|
style="margin-bottom:10px;">
|
||||||
|
<label for="add_{$ collection.id $}_input"
|
||||||
|
class="control-label">{$ collection.title $}</label>
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<span class="input-group-addon"
|
||||||
|
style="width:25%;text-align:right">
|
||||||
|
{$ collection.addPropertyLabel $}:</span>
|
||||||
|
<input id="add_{$ collection.id $}_input"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
ng-model="newPropertyName"
|
||||||
|
validate-unique="collection.checkPropertyUnique"
|
||||||
|
placeholder="{$ collection.placeholder $}"/>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
ng-disabled="!newPropertyName ||
|
||||||
|
add_{$ collection.id $}_form.$invalid"
|
||||||
|
ng-click="collection.addProperty(newPropertyName);
|
||||||
|
newPropertyName = null">
|
||||||
|
<span class="fa fa-plus"> </span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<!-- Property list -->
|
||||||
|
<form class="form-horizontal"
|
||||||
|
id="{$ collection.id $}_form"
|
||||||
|
name="{$ collection.id $}_form">
|
||||||
|
<div class="input-group input-group-sm"
|
||||||
|
ng-repeat="(propertyName, propertyValue) in collection.properties">
|
||||||
|
<span class="input-group-addon"
|
||||||
|
style="width:25%;text-align:right">
|
||||||
|
{$ propertyName $}
|
||||||
|
</span>
|
||||||
|
<input class="form-control"
|
||||||
|
type="text"
|
||||||
|
name="{$ propertyName $}"
|
||||||
|
ng-model="collection.properties[propertyName]"
|
||||||
|
ng-required="true"/>
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<a class="btn btn-default"
|
||||||
|
ng-click="collection.deleteProperty(propertyName)">
|
||||||
|
<span class="fa fa-minus"> </span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Cray Inc.
|
||||||
|
*
|
||||||
|
* 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.dashboard.admin.ironic')
|
||||||
|
.factory('horizon.dashboard.admin.ironic.property-collection.service',
|
||||||
|
propertyCollectionService);
|
||||||
|
|
||||||
|
function propertyCollectionService() {
|
||||||
|
var service = {
|
||||||
|
PropertyCollection: PropertyCollection
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Utility class for managing property collections.
|
||||||
|
* Used is association with the property-collection-editor directive.
|
||||||
|
*
|
||||||
|
* @param {object} args - Base properties are:
|
||||||
|
* id [string] - Unique id used to create DOM element ids, and
|
||||||
|
* internal variable names
|
||||||
|
* title [string] - Label used to identify the collection to the user
|
||||||
|
* addPropertyLabel [string] - Label used to prompt the user to add a new
|
||||||
|
* property
|
||||||
|
* placeholder [string] - Placeholder for text input field
|
||||||
|
* properties [object] - Dictionary of property values indexed by
|
||||||
|
* property name
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function PropertyCollection(args) {
|
||||||
|
var collection = this;
|
||||||
|
collection.id = undefined;
|
||||||
|
collection.title = undefined;
|
||||||
|
collection.addPropertyLabel = undefined;
|
||||||
|
collection.placeholder = undefined;
|
||||||
|
collection.properties = {};
|
||||||
|
|
||||||
|
angular.forEach(args, function(value, arg) {
|
||||||
|
collection[arg] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Test whether this collection contains a property.
|
||||||
|
*
|
||||||
|
* @param {string} propertyName - Property name.
|
||||||
|
* @return {boolean} True if the property already exists, false otherwise.
|
||||||
|
*/
|
||||||
|
this.checkPropertyUnique = function(propertyName) {
|
||||||
|
return !(propertyName in collection.properties);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Add a property to the collection.
|
||||||
|
*
|
||||||
|
* @param {string} propertyName - Property name.
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
this.addProperty = function(propertyName) {
|
||||||
|
this.properties[propertyName] = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Delete a specified property.
|
||||||
|
*
|
||||||
|
* @param {string} propertyName - Property name.
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
this.deleteProperty = function(propertyName) {
|
||||||
|
delete collection.properties[propertyName];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Test whether this collection is in a complete state.
|
||||||
|
* Complete is defined as all properties having a non-null value.
|
||||||
|
*
|
||||||
|
* @return {boolean} True if the collection is complete, false otherwise.
|
||||||
|
*/
|
||||||
|
this.complete = function() {
|
||||||
|
for (var propertyName in this.properties) {
|
||||||
|
if (this.properties.hasOwnProperty(propertyName) &&
|
||||||
|
this.properties[propertyName] === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
})();
|
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Cray Inc
|
||||||
|
*
|
||||||
|
* 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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Unit tests for the form-field service
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe(
|
||||||
|
'horizon.dashboard.admin.ironic.property-collection.service',
|
||||||
|
|
||||||
|
function() {
|
||||||
|
var propertyCollectionService;
|
||||||
|
|
||||||
|
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||||
|
|
||||||
|
beforeEach(inject(function($injector) {
|
||||||
|
propertyCollectionService =
|
||||||
|
$injector.get('horizon.dashboard.admin.ironic.property-collection.service');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('defines the form-field service', function() {
|
||||||
|
expect(propertyCollectionService).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('PropertyCollection - default construction', function() {
|
||||||
|
var collection = new propertyCollectionService.PropertyCollection({});
|
||||||
|
|
||||||
|
expect(collection.id).toBeUndefined();
|
||||||
|
expect(collection.title).toBeUndefined();
|
||||||
|
expect(collection.addPropertyLabel).toBeUndefined();
|
||||||
|
expect(collection.placeholder).toBeUndefined();
|
||||||
|
expect(collection.properties).toEqual({});
|
||||||
|
expect(collection.checkPropertyUnique).toBeDefined();
|
||||||
|
expect(collection.addProperty).toBeDefined();
|
||||||
|
expect(collection.deleteProperty).toBeDefined();
|
||||||
|
expect(collection.complete).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('PropertyCollection - local parameters', function() {
|
||||||
|
var args = {id: 'id',
|
||||||
|
title: 'title',
|
||||||
|
placeholder: 'placeholder',
|
||||||
|
properties: {'prop-1': 'prop1-val',
|
||||||
|
'prop-2': 'prop2-val'}
|
||||||
|
};
|
||||||
|
var collection = new propertyCollectionService.PropertyCollection(args);
|
||||||
|
for (var arg in args) {
|
||||||
|
if (args.hasOwnProperty(arg)) {
|
||||||
|
expect(collection[arg]).toBeDefined();
|
||||||
|
expect(collection[arg]).toEqual(args[arg]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checkPropertyUnique', function() {
|
||||||
|
var collection = new propertyCollectionService.PropertyCollection({});
|
||||||
|
expect(collection.checkPropertyUnique('foo')).toBe(true);
|
||||||
|
collection.addProperty('foo');
|
||||||
|
expect(collection.checkPropertyUnique('foo')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('addProperty', function() {
|
||||||
|
var collection = new propertyCollectionService.PropertyCollection({});
|
||||||
|
collection.addProperty('foo');
|
||||||
|
expect(collection.properties.foo).toBeDefined();
|
||||||
|
expect(collection.properties.foo).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deleteProperty', function() {
|
||||||
|
var collection = new propertyCollectionService.PropertyCollection({});
|
||||||
|
var original = angular.copy(collection);
|
||||||
|
collection.addProperty('foo');
|
||||||
|
collection.deleteProperty('foo');
|
||||||
|
expect(collection).toEqual(original);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('complete', function() {
|
||||||
|
var collection = new propertyCollectionService.PropertyCollection({});
|
||||||
|
expect(collection.complete()).toBe(true);
|
||||||
|
collection.addProperty('foo');
|
||||||
|
expect(collection.complete()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
Reference in New Issue
Block a user