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:
parent
9c9a298aa8
commit
4ead23c007
@ -29,9 +29,9 @@
|
||||
'horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
||||
'horizon.dashboard.admin.ironic.form-field.service',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.property-collection.service',
|
||||
'ctrl',
|
||||
'node'
|
||||
];
|
||||
'node'];
|
||||
|
||||
/**
|
||||
* @description Utility class used to manage local-link-connection
|
||||
@ -141,13 +141,9 @@
|
||||
validDatapathIdPattern,
|
||||
formFieldService,
|
||||
ironic,
|
||||
propertyCollectionService,
|
||||
ctrl,
|
||||
node) {
|
||||
ctrl.port = {
|
||||
extra: {},
|
||||
node_uuid: node.uuid
|
||||
};
|
||||
|
||||
ctrl.address = new formFieldService.FormField({
|
||||
id: "macAddress",
|
||||
title: gettext("MAC address"),
|
||||
@ -186,7 +182,8 @@
|
||||
var field = ctrl.portgroup_uuid;
|
||||
|
||||
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);
|
||||
|
||||
@ -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
|
||||
*
|
||||
@ -203,26 +207,5 @@
|
||||
ctrl.cancel = function() {
|
||||
$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>
|
||||
</form>
|
||||
|
||||
<form id="AddExtraForm" name="AddExtraForm" style="margin-bottom:10px;">
|
||||
<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>
|
||||
<property-collection-editor collection="ctrl.extra"></property-collection-editor>
|
||||
</div>
|
||||
<!--modal footer-->
|
||||
<div class="modal-footer ng-scope">
|
||||
@ -79,7 +37,7 @@
|
||||
<button type="submit"
|
||||
ng-disabled="CreatePortForm.$invalid ||
|
||||
LocalLinkConnectionForm.$invalid ||
|
||||
ExtraForm.$invalid"
|
||||
!ctrl.extra.complete()"
|
||||
ng-click="ctrl.submit()"
|
||||
class="btn btn-primary">
|
||||
{$ ::ctrl.submitButtonTitle $}
|
||||
|
@ -54,9 +54,11 @@
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.createPort = function() {
|
||||
var port = angular.copy(ctrl.port);
|
||||
|
||||
port.address = ctrl.address.value;
|
||||
var port = {
|
||||
extra: ctrl.extra.properties,
|
||||
node_uuid: node.id,
|
||||
address: ctrl.address.value
|
||||
};
|
||||
|
||||
var attr = ctrl.localLinkConnection.toPortAttr();
|
||||
if (attr) {
|
||||
|
@ -89,7 +89,7 @@
|
||||
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
|
||||
@ -108,7 +108,7 @@
|
||||
patcher.buildPatch(port.local_link_connection,
|
||||
ctrl.localLinkConnection.toPortAttr(),
|
||||
"/local_link_connection");
|
||||
patcher.buildPatch(port.extra, ctrl.port.extra, "/extra");
|
||||
patcher.buildPatch(port.extra, ctrl.extra.properties, "/extra");
|
||||
patcher.buildPatch(port.portgroup_uuid,
|
||||
ctrl.portgroup_uuid.value,
|
||||
"/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);
|
||||
});
|
||||
});
|
||||
})();
|
Loading…
Reference in New Issue
Block a user