Modify hz-cell to use hz-field
hz-cell doesn't use params, which causes lots of problems when trying to render changes to data...since it evaluates only once, reading what's assumed to be on scope. This patch adds 'table,' 'column,' and 'item' params so they may be better watched by the directive. hz-field does use params and thus is useful in many situations outside of hz-cell. This patch provides hz-field and lets hz-cell use it so all output logic is shared. hz-field also: a) accepts 'values' as a column configuration as we discussed in the last mid-cycle, so codes, etc. may be supplied without use of a filter; and b) accepts 'urlFunction' which translates the given item to a url which is linked on the outputted name (rather than having to write a template for a common link function). Follow-on patches will demonstrate use. Change-Id: I0835a90e61d0e708233da795964595f88616388c Partially-Implements: blueprint angular-registry
This commit is contained in:
parent
4242d2c29d
commit
146256f8c7
131
horizon/static/framework/widgets/property/hz-field.directive.js
Normal file
131
horizon/static/framework/widgets/property/hz-field.directive.js
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
*
|
||||
* 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.property')
|
||||
.directive('hzField', hzField);
|
||||
|
||||
hzField.$inject = ['$filter'];
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name hzField
|
||||
* @param config {Object} - The field definition object, described below
|
||||
* @param item {Object} - The object containing the property from config.id
|
||||
* @description
|
||||
* The `hzField` directive allows you to output an object's property using
|
||||
* formatting as provided by a field configuration.
|
||||
*
|
||||
* The config object describes a single field, and the config object's 'id'
|
||||
* property matches the name of a property in the 'item' parameter. For
|
||||
* example, if config.id is 'name' then there should be an item.name that
|
||||
* is evaluated for display using the logic described below.
|
||||
*
|
||||
* The field configuration may transform the data in the item's property
|
||||
* using either a set of single-argument filters or functions, specified by
|
||||
* the 'filters' property, or using the 'values' object in which the item
|
||||
* property is mapped via the keys to the values in the given object. Note
|
||||
* that a combination of 'filters' and 'values' may be used; in this case
|
||||
* the filters are evaluated first. This allows for translations that will
|
||||
* map to keys first (e.g. upper-casing a string with a filter so it matches
|
||||
* upper-case keys), and allows the values provided in the 'values' mapping
|
||||
* to be the final value produced. The 'urlFunction' option allows for a
|
||||
* a function to be given, where the item is the sole parameter and the result
|
||||
* should be a URL.
|
||||
*
|
||||
* @restrict E
|
||||
*
|
||||
* @scope
|
||||
* @example
|
||||
*
|
||||
* var config = {id: 'a', title: 'Header A', priority: 1};
|
||||
*
|
||||
* // Using urlFunction to create a link
|
||||
* var linked = {id: 'b', title: 'Header B', priority: 2, urlFunction: myUrlFunction},
|
||||
*
|
||||
* // Using defaultSort
|
||||
* var defaultSort = {id: 'c', title: 'Header C', priority: 1, sortDefault: true};
|
||||
*
|
||||
* // Using filters (can be combined with 'values')
|
||||
* var filtered = {id: 'd', title: 'Header D', priority: 2,
|
||||
* filters: [someFilterFunction, 'uppercase']};
|
||||
*
|
||||
* // Using value mappings
|
||||
* var mapped = {id: 'e', title: 'Header E', priority: 1,
|
||||
* values: {
|
||||
* 'a': 'apple',
|
||||
* 'j': 'jacks'
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* function myUrlFunction(item) {
|
||||
* return '/my/path/' + item.id;
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* <hz-field config="config" item="item"></hz-field>
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
function hzField($filter) {
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
config: "=",
|
||||
item: "="
|
||||
},
|
||||
link: link
|
||||
};
|
||||
return directive;
|
||||
|
||||
///////////////////
|
||||
|
||||
function link(scope, element) {
|
||||
var config = scope.config;
|
||||
var item = scope.item;
|
||||
var propValue = item[config.id];
|
||||
var output = propValue;
|
||||
if (config && config.filters) {
|
||||
for (var i = 0; i < config.filters.length; i++) {
|
||||
var filter = config.filters[i];
|
||||
// call horizon framework filter function if provided
|
||||
if (angular.isFunction(filter)) {
|
||||
output = filter(propValue);
|
||||
// call angular filters
|
||||
} else {
|
||||
output = $filter(filter)(propValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config && config.values) {
|
||||
// apply mapping values to the data if applicable
|
||||
output = config.values[output];
|
||||
}
|
||||
var url;
|
||||
if (config && config.urlFunction) {
|
||||
url = config.urlFunction(item);
|
||||
}
|
||||
if (url) {
|
||||
element.append(angular.element('<a>').attr('href', url).append(output));
|
||||
} else {
|
||||
element.append(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
28
horizon/static/framework/widgets/property/property.module.js
Normal file
28
horizon/static/framework/widgets/property/property.module.js
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
*
|
||||
* 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 overview
|
||||
* @name horizon.framework.widgets.property
|
||||
* @description
|
||||
* This module provides support for displaying properties of registered resource
|
||||
* types.
|
||||
*/
|
||||
angular.module('horizon.framework.widgets.property', []);
|
||||
|
||||
})();
|
@ -20,26 +20,29 @@
|
||||
.module('horizon.framework.widgets.table')
|
||||
.directive('hzCell', hzCell);
|
||||
|
||||
hzCell.$inject = ['$compile', '$filter'];
|
||||
hzCell.$inject = ['$compile'];
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name horizon.framework.widgets.table.directive:hzCell
|
||||
* @param table {Object} - The table/controller context
|
||||
* @param column {Object} - The column definition object, described below
|
||||
* @param item {Object} - The object containing the property from column.id
|
||||
* @description
|
||||
* The `hzCell` directive allows you to customize your cell content.
|
||||
* When specifying your table configuration object, you may pass in a
|
||||
* template per each column.
|
||||
*
|
||||
* See the documentation on hz-field for details on how to specify formatting
|
||||
* based on the column configuration.
|
||||
*
|
||||
* You should define a template when you want to format data or show more
|
||||
* complex content (e.g conditionally show different icons or a link).
|
||||
* You should reference the cell's 'item' attribute in the template if
|
||||
* you need access to the cell's data. The attributes 'column' and 'item'
|
||||
* should be defined outside of this directive. See example below.
|
||||
* you need access to the cell's data. See example below.
|
||||
*
|
||||
* It should ideally be used within the context of the `hz-dynamic-table` directive.
|
||||
* The params passed into `hz-dynamic-table` can be used in the custom template,
|
||||
* including the 'table' scope. 'table' can be referenced if you want to pass in an
|
||||
* outside scope.
|
||||
* 'table' can be referenced in a template if you want to pass in an outside scope.
|
||||
*
|
||||
* @restrict E
|
||||
*
|
||||
@ -55,27 +58,37 @@
|
||||
* {id: 'c', title: 'Header C', priority: 1, sortDefault: true},
|
||||
* {id: 'd', title: 'Header D', priority: 2,
|
||||
* template: '<span class="fa fa-bolt">{$ item.id $}</span>',
|
||||
* filters: [someFilterFunction, 'uppercase']}
|
||||
* filters: [someFilterFunction, 'uppercase']},
|
||||
* {id: 'e', title: 'Header E', priority: 1,
|
||||
* values: {
|
||||
* 'a': 'apple',
|
||||
* 'j': 'jacks'
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
* };
|
||||
*
|
||||
* ```
|
||||
* <tbody>
|
||||
* <tbody ng-controller="TableCtrl as table">
|
||||
* <tr ng-repeat="item in items track by $index">
|
||||
* <td ng-repeat="column in config.columns"
|
||||
* class="{$ column.classes $}">
|
||||
* <hz-cell></hz-cell>
|
||||
* <hz-cell table="table" column="column" item="item"></hz-cell>
|
||||
* </td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
function hzCell($compile, $filter) {
|
||||
function hzCell($compile) {
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
scope: false,
|
||||
scope: {
|
||||
table: '=',
|
||||
column: '=',
|
||||
item: '='
|
||||
},
|
||||
link: link
|
||||
};
|
||||
return directive;
|
||||
@ -84,26 +97,14 @@
|
||||
|
||||
function link(scope, element) {
|
||||
var column = scope.column;
|
||||
var item = scope.item;
|
||||
var html;
|
||||
// if template provided, render, and place into cell
|
||||
if (column && column.template) {
|
||||
// if template provided, render, and place into cell
|
||||
html = $compile(column.template)(scope);
|
||||
} else {
|
||||
// apply filters to cell data if applicable
|
||||
html = item[column.id];
|
||||
if (column && column.filters) {
|
||||
for (var i = 0; i < column.filters.length; i++) {
|
||||
var filter = column.filters[i];
|
||||
// call horizon framework filter function if provided
|
||||
if (angular.isFunction(filter)) {
|
||||
html = filter(item[column.id]);
|
||||
// call angular filters
|
||||
} else {
|
||||
html = $filter(filter)(item[column.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE: 'table' is not passed to hz-field as hz-field is intentionally
|
||||
// not cognizant of a 'table' context as hz-cell is.
|
||||
html = $compile('<hz-field config="column" item="item"></hz-field>')(scope);
|
||||
}
|
||||
element.append(html);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
<span class="rsp-alt-p2">
|
||||
<dl class="col-sm-2" ng-repeat="column in config.columns">
|
||||
<dt translate>{$ column.title $}</dt>
|
||||
<dd translate><hz-cell></hz-cell></dd>
|
||||
<dd translate><hz-cell table="table" column="column" item="item"></hz-cell></dd>
|
||||
</dl>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -63,7 +63,7 @@
|
||||
</td>
|
||||
<td ng-repeat="column in config.columns"
|
||||
class="rsp-p{$ column.priority $}">
|
||||
<hz-cell></hz-cell>
|
||||
<hz-cell table="table" column="column" item="item"></hz-cell>
|
||||
</td>
|
||||
<td ng-if="itemActions" class="actions_column">
|
||||
<!--
|
||||
|
@ -222,6 +222,54 @@
|
||||
expect($element.find('tbody tr:eq(1) td:eq(3)').text()).toContain('reptile-ish');
|
||||
expect($element.find('tbody tr:eq(2) td:eq(3)').text()).toContain('bird-ish');
|
||||
});
|
||||
|
||||
it('properly maps the cell content given a mapping', function() {
|
||||
|
||||
$scope.config = {
|
||||
selectAll: true,
|
||||
expand: false,
|
||||
trackId: 'id',
|
||||
columns: [
|
||||
{id: 'animal', title: 'Animal', priority: 1,
|
||||
values: {
|
||||
cat: "Catamount",
|
||||
snake: "Serpent",
|
||||
sparrow: "CAPTAIN Jack Sparrow"
|
||||
}
|
||||
},
|
||||
{id: 'type', title: 'Type', priority: 2},
|
||||
{id: 'diet', title: 'Diet', priority: 1, sortDefault: true},
|
||||
{id: 'domestic', title: 'Domestic', priority: 2}
|
||||
]
|
||||
};
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
expect($element.find('tbody tr:eq(0) td:eq(2)').text()).toContain('Catamount');
|
||||
expect($element.find('tbody tr:eq(1) td:eq(2)').text()).toContain('Serpent');
|
||||
expect($element.find('tbody tr:eq(2) td:eq(2)').text()).toContain('CAPTAIN Jack Sparrow');
|
||||
});
|
||||
|
||||
it('properly adds a link with urlFunction', function() {
|
||||
$scope.config = {
|
||||
selectAll: true,
|
||||
expand: false,
|
||||
trackId: 'id',
|
||||
columns: [
|
||||
{id: 'animal', title: 'Animal', priority: 1,
|
||||
urlFunction: myFunction
|
||||
},
|
||||
{id: 'type', title: 'Type', priority: 2},
|
||||
{id: 'diet', title: 'Diet', priority: 1, sortDefault: true},
|
||||
{id: 'domestic', title: 'Domestic', priority: 2}
|
||||
]
|
||||
};
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
expect($element.find('tbody tr:eq(0) td:eq(2) a').attr('href')).toBe('/here/cat');
|
||||
expect($element.find('tbody tr:eq(1) td:eq(2) a').attr('href')).toBe('/here/snake');
|
||||
expect($element.find('tbody tr:eq(2) td:eq(2) a').attr('href')).toBe('/here/sparrow');
|
||||
function myFunction(item) {
|
||||
return '/here/' + item.animal;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -23,6 +23,7 @@
|
||||
'horizon.framework.widgets.details',
|
||||
'horizon.framework.widgets.help-panel',
|
||||
'horizon.framework.widgets.wizard',
|
||||
'horizon.framework.widgets.property',
|
||||
'horizon.framework.widgets.table',
|
||||
'horizon.framework.widgets.modal',
|
||||
'horizon.framework.widgets.modal-wait-spinner',
|
||||
|
@ -122,8 +122,7 @@
|
||||
id: 'name',
|
||||
priority: 1,
|
||||
sortDefault: true,
|
||||
template: '<a ng-href="{$ \'project/ngdetails/OS::Glance::Image/\' + item.id $}">' +
|
||||
'{$ item.name $}</a>'
|
||||
urlFunction: urlFunction
|
||||
})
|
||||
.append({
|
||||
id: 'type',
|
||||
@ -228,6 +227,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function urlFunction(item) {
|
||||
return 'project/ngdetails/OS::Glance::Image/' + item.id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user