AC-369 Inventories->Hosts page now uses the /api/vi/inventories/N/trees to load the tree. Tree is loaded fully expanded.

This commit is contained in:
chouseknecht 2013-08-27 18:31:19 -04:00
parent fe03a789b6
commit 415fbc5362
5 changed files with 336 additions and 270 deletions

View File

@ -33,10 +33,11 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa
if (scope.loadBreadCrumbsRemove) {
scope.loadBreadCrumbsRemove();
}
scope.loadBreadCrumbsRemove = scope.$on('hostTabInit', function(e, inventory_name) {
scope.loadBreadCrumbsRemove = scope.$on('buildAllGroups', function(e, inventory_name) {
LoadBreadCrumbs({ path: '/inventories/' + id, title: inventory_name });
});
// Sets up the search tree and loads All Hosts for the inventory
LoadSearchTree({ scope: scope, inventory_id: scope['inventory_id'] });
// Add the selected flag to the hosts set.

View File

@ -6,7 +6,7 @@
* Routines that handle host add/edit/delete on the Inventory detail page.
*
*/
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'HostsHelper',
'InventoryHelper', 'RelatedSearchHelper','RelatedPaginateHelper',
@ -460,49 +460,194 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}])
.factory('LoadSearchTree', ['Rest', 'GetBasePath', 'ProcessErrors', '$compile',
function(Rest, GetBasePath, ProcessErrors, $compile) {
.factory('LoadSearchTree', ['Rest', 'GetBasePath', 'ProcessErrors', '$compile', '$rootScope', 'Wait',
function(Rest, GetBasePath, ProcessErrors, $compile, $rootScope, Wait) {
return function(params) {
var scope = params.scope;
var inventory_id = params.inventory_id;
var newTree = [];
scope.searchTree = [];
var html = '';
function buildHTML(tree_data) {
html += (tree_data.length > 0) ? "<ul>\n" : "";
for(var i=0; i < tree_data.length; i++) {
html += "<li id=\"search-node-1000\" data-state=\"opened\" data-hosts=\"" + tree_data[i].related.hosts + "\" " +
"data-description=\"" + tree_data[i].description + "\" " +
"data-failures=\"" + tree_data[i].has_active_failures + "\" " +
"data-groups=\"" + tree_data[i].related.groups + "\" " +
"data-name=\"" + tree_data[i].name + "\" " +
"data-group-id=\"" + tree_data[i].id + "\" " +
"><a href=\"\" class=\"expand\"><i class=\"icon-caret-down\"></i></a> " +
"<i class=\"field-badge icon-failures-" + tree_data[i].has_active_failures + "\"></i> " +
"<a href=\"\" class=\"activate\">" + tree_data[i].name + "</a> ";
if (tree_data[i].children.length > 0) {
buildHTML(tree_data[i].children);
}
else {
html += "</li>\n";
}
}
html += "</ul>\n";
}
function refresh(parent) {
var group, title;
if (parent.attr('data-group-id')) {
group = parent.attr('data-group-id');
title = parent.attr('data-name');
//title += (parent.attr('data-description') !== "") ? '<p>' + parent.attr('data-description') + '</p>' : '';
}
else {
group = null;
title = 'All Hosts'
}
// The following will trigger the host list to load. See Inventory.js controller.
scope.$emit('refreshHost', group, title);
}
function activate(e) {
/* Set the clicked node as active */
var elm = angular.element(e.target); //<a>
var parent = angular.element(e.target.parentNode); //<li>
$('.search-tree .active').removeClass('active');
elm.addClass('active');
refresh(parent);
}
function toggle(e) {
var id, parent, elm, icon;
if (e.target.tagName == 'I') {
id = e.target.parentNode.parentNode.attributes.id.value;
parent = angular.element(e.target.parentNode.parentNode); //<li>
elm = angular.element(e.target.parentNode); // <a>
}
else {
id = e.target.parentNode.attributes.id.value;
parent = angular.element(e.target.parentNode);
elm = angular.element(e.target);
}
var sibling = angular.element(parent.children()[2]); // <a>
var state = parent.attr('data-state');
var icon = angular.element(elm.children()[0]);
if (state == 'closed') {
// expand the elment
var childlists = parent.find('ul');
if (childlists && childlists.length > 0) {
// has childen
for (var i=0; i < childlists.length; i++) {
var listChild = angular.element(childlists[i]);
var listParent = angular.element(listChild.parent());
if (listParent.attr('id') == id) {
angular.element(childlists[i]).removeClass('hidden');
}
}
}
parent.attr('data-state','open');
icon.removeClass('icon-caret-right').addClass('icon-caret-down');
}
else {
// close the element
parent.attr('data-state','closed');
icon.removeClass('icon-caret-down').addClass('icon-caret-right');
var childlists = parent.find('ul');
if (childlists && childlists.length > 0) {
// has childen
for (var i=0; i < childlists.length; i++) {
angular.element(childlists[i]).addClass('hidden');
}
}
/* When the active node's parent is closed, activate the parent*/
if ($(parent).find('.active').length > 0) {
$(parent).find('.active').removeClass('active');
sibling.addClass('active');
refresh(parent);
}
}
}
// Responds to searchTreeReady, thrown from Hosts.js helper when the inventory tree
// is ready
if (scope.searchTreeReadyRemove) {
scope.searchTreeReadyRemove();
}
scope.searchTreeReadyRemove = scope.$on('searchTreeReady', function(e, html) {
var container = angular.element(document.getElementById('search-tree-container'));
container.empty();
var compiled = $compile(html)(scope);
container.append(compiled);
var links = container.find('a');
for (var i=0; i < links.length; i++) {
var link = angular.element(links[i]);
if (link.hasClass('expand')) {
link.unbind('click', toggle);
link.bind('click', toggle);
}
if (link.hasClass('activate')) {
link.unbind('click', activate);
link.bind('click', activate);
}
}
Wait('stop');
});
// After the inventory is loaded, build an array of all unique groups found therein.
// The lis is used in the group drop-down selector widget for each host.
if (scope.buildAllGroupsRemove) {
scope.buildAllGroupsRemove();
}
scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function(e, inventory_name) {
scope.inventory_groups = [];
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/groups/?order_by=name');
scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree) {
Rest.setUrl(inventory_tree);
Rest.get()
.success( function(data, status, headers, config) {
for (var i=0; i < data.results.length; i++) {
scope.inventory_groups.push({ name: data.results[i].name, id: data.results[i].id });
}
scope.$emit('hostTabInit', inventory_name);
buildHTML(data);
scope.$emit('searchTreeReady', html + "</li>\n</ul>\n");
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get groups for inventory: ' + inventory_id + '. GET returned: ' + status });
{ hdr: 'Error!', msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status });
});
});
if (scope.buildGroupListRemove) {
scope.buildGroupListRemove();
}
scope.buildGroupListRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree, groups_url) {
scope.inventory_groups = [];
Rest.setUrl(groups_url);
Rest.get()
.success( function(data, status, headers, config) {
for (var i=0; i < data.results.length; i++) {
scope.inventory_groups.push({
id: data.results[i].id,
description: data.results[i].description,
name: data.results[i].name });
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get groups for inventory: ' + inventory_id + '. GET returned: ' + status });
});
});
Wait('start');
// Load the inventory root node
Rest.setUrl (GetBasePath('inventory') + inventory_id + '/');
Rest.get()
.success( function(data, status, headers, config) {
scope.searchTree.push({
name: data.name,
description: data.description,
hosts: data.related.hosts,
failures: data.has_active_failures,
groups: data.related.root_groups,
children: []
});
scope.$emit('buildAllGroups', data.name);
html += "<div class=\"title\">Group Selector:</div>\n" +
"<ul class=\"tree-root\">\n" +
"<li id=\"search-node-1000\" data-state=\"opened\" data-hosts=\"" + data.related.hosts + "\" " +
"data-description=\"" + data.description + "\" " +
"data-failures=\"" + data.has_active_failures + "\" " +
"data-groups=\"" + data.related.groups + "\" " +
"data-name=\"" + data.name + "\" " +
"><a href=\"\" class=\"expand\"><i class=\"icon-caret-down\"></i></a> " +
"<i class=\"field-badge icon-failures-" + data.has_active_failures + "\"></i> " +
"<a href=\"\" class=\"activate active\">" + data.name + "</a>";
scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups);
scope.$emit('refreshHost', null, 'All Hosts');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,

View File

@ -687,6 +687,8 @@ select.field-mini-height {
}
.search-tree {
padding: 10px 3px 10px 3px;
.title {
color: @black;
@ -697,20 +699,24 @@ select.field-mini-height {
ul {
list-style-type: none;
padding-left: 13px;
padding-left: 16px;
}
ul:first-child {
.tree-root {
padding-left: 0;
}
.activate {
word-break: break-all;
}
.activate:hover {
background-color: #ddd;
}
.active {
font-weight: bold;
padding: 5px;
padding: 5px 0 5px 0;
}
.expand {
@ -730,10 +736,10 @@ select.field-mini-height {
}
}
.host-failure-filter {
padding: 10px;
}
.host-failure-filter {
padding: 10px;
}
.disabled {
color: @grey;

View File

@ -344,258 +344,171 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
$(elm).spinner(opts);
}
}
}])
}]);
/* This has become more than a simple directive. All the logic for building the group selector tree
on the Hosts tab is here. Probably needs to move into the Hosts helper and/or Inventory helper.
*/
.directive('awTree', ['Rest', 'ProcessErrors', 'Authorization', '$compile', '$rootScope', 'Wait',
function(Rest, ProcessErrors, Authorization, $compile, $rootScope, Wait) {
return {
//require: 'ngModel',
// .directive('awTree', ['Rest', 'ProcessErrors', 'Authorization', '$compile', '$rootScope', 'Wait',
// function(Rest, ProcessErrors, Authorization, $compile, $rootScope, Wait) {
// return {
// //require: 'ngModel',
replace: true,
// replace: true,
transclude: true,
// transclude: true,
scope: {
treeData: '=awTree'
},
// scope: {
// treeData: '=awTree'
// },
replace: true,
// replace: true,
template:
"<div class=\"search-tree well\" id=\"search-tree-container\">\n" +
"<ul>\n" +
"<li id=\"search-node-1000\" data-state=\"closed\" data-hosts=\"{{ treeData[0].hosts}}\" " +
"data-hosts=\"{{ treeData[0].hosts }}\" " +
"data-description=\"{{ treeData[0].description }}\" " +
"data-failures=\"{{ treeData[0].failures }}\" " +
"data-groups=\"{{ treeData[0].groups }}\" " +
"data-name=\"{{ treeData[0].name }}\">" +
"<a href=\"\" class=\"expand\"><i class=\"icon-caret-right\"></i></a> <a href=\"\" class=\"activate active\">{{ treeData[0].name }}</a></li>\n" +
"</li>\n"+
"</ul>\n" +
"</div>\n",
// template:
// "<div class=\"search-tree well\" id=\"search-tree-container\">\n" +
// "<ul>\n" +
// "<li id=\"search-node-1000\" data-state=\"closed\" data-hosts=\"{{ treeData[0].hosts}}\" " +
// "data-hosts=\"{{ treeData[0].hosts }}\" " +
// "data-description=\"{{ treeData[0].description }}\" " +
// "data-failures=\"{{ treeData[0].failures }}\" " +
// "data-groups=\"{{ treeData[0].groups }}\" " +
// "data-name=\"{{ treeData[0].name }}\">" +
// "<a href=\"\" class=\"expand\"><i class=\"icon-caret-right\"></i></a> <a href=\"\" class=\"activate active\">{{ treeData[0].name }}</a></li>\n" +
// "</li>\n"+
// "</ul>\n" +
// "</div>\n",
link: function(scope, elm , attrs) {
// link: function(scope, elm , attrs) {
var idx=1000;
// var idx=1000;
function refresh(parent) {
var group, title;
if (parent.attr('data-group-id')) {
group = parent.attr('data-group-id');
title = parent.attr('data-name');
//title += (parent.attr('data-description') !== "") ? '<p>' + parent.attr('data-description') + '</p>' : '';
}
else {
group = null;
title = 'All Hosts'
}
// The following will trigger the host list to load. See Inventory.js controller.
scope.$emit('refreshHost', group, title);
}
// function refresh(parent) {
// var group, title;
// if (parent.attr('data-group-id')) {
// group = parent.attr('data-group-id');
// title = parent.attr('data-name');
// //title += (parent.attr('data-description') !== "") ? '<p>' + parent.attr('data-description') + '</p>' : '';
// }
// else {
// group = null;
// title = 'All Hosts'
// }
// // The following will trigger the host list to load. See Inventory.js controller.
// scope.$emit('refreshHost', group, title);
// }
function activate(e) {
/* Set the clicked node as active */
var elm = angular.element(e.target); //<a>
var parent = angular.element(e.target.parentNode); //<li>
$('.search-tree .active').removeClass('active');
elm.addClass('active');
refresh(parent);
}
// function activate(e) {
// /* Set the clicked node as active */
// var elm = angular.element(e.target); //<a>
// var parent = angular.element(e.target.parentNode); //<li>
// $('.search-tree .active').removeClass('active');
// elm.addClass('active');
// refresh(parent);
// }
function toggle(e) {
// function toggle(e) {
var id, parent, elm, icon;
// var id, parent, elm, icon;
if (e.target.tagName == 'I') {
id = e.target.parentNode.parentNode.attributes.id.value;
parent = angular.element(e.target.parentNode.parentNode); //<li>
elm = angular.element(e.target.parentNode); // <a>
}
else {
id = e.target.parentNode.attributes.id.value;
parent = angular.element(e.target.parentNode);
elm = angular.element(e.target);
}
// if (e.target.tagName == 'I') {
// id = e.target.parentNode.parentNode.attributes.id.value;
// parent = angular.element(e.target.parentNode.parentNode); //<li>
// elm = angular.element(e.target.parentNode); // <a>
// }
// else {
// id = e.target.parentNode.attributes.id.value;
// parent = angular.element(e.target.parentNode);
// elm = angular.element(e.target);
// }
var sibling = angular.element(parent.children()[2]); // <a>
var state = parent.attr('data-state');
var icon = angular.element(elm.children()[0]);
// var sibling = angular.element(parent.children()[2]); // <a>
// var state = parent.attr('data-state');
// var icon = angular.element(elm.children()[0]);
// if (state == 'closed') {
// // expand the elment
// var childlists = parent.find('ul');
// if (childlists && childlists.length > 0) {
// // has childen
// for (var i=0; i < childlists.length; i++) {
// var listChild = angular.element(childlists[i]);
// var listParent = angular.element(listChild.parent());
// if (listParent.attr('id') == id) {
// angular.element(childlists[i]).removeClass('hidden');
// }
// }
// }
// parent.attr('data-state','open');
// icon.removeClass('icon-caret-right').addClass('icon-caret-down');
// }
// else {
// // close the element
// parent.attr('data-state','closed');
// icon.removeClass('icon-caret-down').addClass('icon-caret-right');
// var childlists = parent.find('ul');
// if (childlists && childlists.length > 0) {
// // has childen
// for (var i=0; i < childlists.length; i++) {
// angular.element(childlists[i]).addClass('hidden');
// }
// }
// /* When the active node's parent is closed, activate the parent*/
// if ($(parent).find('.active').length > 0) {
// $(parent).find('.active').removeClass('active');
// sibling.addClass('active');
// refresh(parent);
// }
// }
// }
/* Open/close the node and expand */
if (scope.childrenLoadedRemove) {
scope.childrenLoadedRemove();
}
scope.childrenLoadedRemove = scope.$on('childrenLoaded', function() {
childlists = parent.find('ul'); //look for children
if (childlists && childlists.length > 0) {
// bind toggle() to click event of each link in the group we clicked on
var links = parent.find('a');
for (var i=0; i < links.length; i++) {
var link = angular.element(links[i]);
if (link.hasClass('expand')) {
link.unbind('click', toggle);
link.bind('click', toggle);
}
if (link.hasClass('activate')) {
link.unbind('click', activate);
link.bind('click', activate);
}
}
toggle(e);
}
else {
icon.removeClass('icon-caret-right').addClass('icon-caret-down');
parent.attr('data-state','open');
//activate(e);
}
});
if (state == 'closed') {
// expand the elment
var childlists = parent.find('ul');
if (childlists && childlists.length > 0) {
// already has childen
for (var i=0; i < childlists.length; i++) {
var listChild = angular.element(childlists[i]);
var listParent = angular.element(listChild.parent());
if (listParent.attr('id') == id) {
angular.element(childlists[i]).removeClass('hidden');
}
// all the children should be in a closed state
var liList = listChild.find('li');
for (var j=0; j < liList.length; j++) {
var thisList = angular.element(liList[j]);
var anchor = angular.element(thisList.find('a')[0]);
var thisIcon = angular.element(anchor.children()[0]);
thisIcon.removeClass('icon-caret-down').addClass('icon-caret-right');
thisList.attr('data-state', 'closed');
}
}
parent.attr('data-state','open');
icon.removeClass('icon-caret-right').addClass('icon-caret-down');
}
else {
getChildren(elm, parent, sibling);
}
}
else {
// close the element
parent.attr('data-state','closed');
icon.removeClass('icon-caret-down').addClass('icon-caret-right');
var childlists = parent.find('ul');
if (childlists && childlists.length > 0) {
// has childen
for (var i=0; i < childlists.length; i++) {
angular.element(childlists[i]).addClass('hidden');
}
}
/* When the active node's parent is closed, activate the parent*/
if ($(parent).find('.active').length > 0) {
$(parent).find('.active').removeClass('active');
sibling.addClass('active');
refresh(parent);
}
}
}
function getChildren(elm, parent, sibling) {
var url = parent.attr('data-groups');
var html = '';
var token = Authorization.getToken();
url += ($rootScope.hostFailureFilter) ? '?has_active_failures=true&order_by=name' : '?order_by=name';
/* For reasons unknown calling Rest fails. It just dies with no errors
or any info */
$.ajax({
url: url,
headers: { 'Authorization': 'Token ' + token },
dataType: 'json',
success: function(data) {
// build html and append to parent of clicked link
for (var i=0; i < data.results.length; i++) {
idx++;
html += "<li ";
html += "id=\"search-tree-" + idx +"\" ";
html += "date-state=\"closed\" ";
html += "data-hosts=\"" + data.results[i].related.all_hosts + "\" ";
html += "data-description=\"" + data.results[i].description + "\" ";
html += "data-failures=\"" + data.results[i].has_active_failures + "\" ";
html += "data-groups=\"" + data.results[i].related.children + "\" ";
html += "data-name=\"" + data.results[i].name + "\" ";
html += "data-group-id=\"" + data.results[i].id + "\">";
html += "<a href=\"\" class=\"expand\"><i class=\"icon-caret-right\"></i></a> ";
html += "<i class=\"field-badge icon-failures-" + data.results[i].has_active_failures + "\"></i>";
html += " <a href=\"\" class=\"activate\">" + data.results[i].name + "</a> ";
html += "</li>\n";
}
html = (html !== '') ? "<ul>" + html + "</ul>\n" : "";
var compiled = $compile(html)(scope);
parent.append(compiled); //append the new list to the parent <li>
if (!scope.$$phase) {
scope.$digest();
}
scope.$emit('childrenLoaded');
},
error: function(data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get child groups for ' + elm.attr('name') +
'. GET returned: ' + status });
}
});
}
function initialize() {
var root = angular.element(document.getElementById('search-node-1000'));
var toggleElm = angular.element(root.find('a')[0]);
var activateElm = angular.element(root.find('a')[1])
toggleElm.bind('click', toggle);
activateElm.bind('click', activate);
}
// function initialize() {
// var root = angular.element(document.getElementById('search-node-1000'));
// var toggleElm = angular.element(root.find('a')[0]);
// var activateElm = angular.element(root.find('a')[1])
// toggleElm.bind('click', toggle);
// activateElm.bind('click', activate);
// }
// Responds to hostTabInit event, thrown from Hosts.js helper. Once the initial
// inventory node is loaded, force the root level groups to load and populate the
// host list with All Hosts.
if ($rootScope.hostTabInitRemove) {
$rootScope.hostTabInitRemove();
}
$rootScope.hostTabInitRemove = $rootScope.$on('hostTabInit', function(e) {
Wait('start');
var container = angular.element(document.getElementById('search-tree-container'));
container.empty();
var html = "<div class=\"title\">Group Selector:</div>\n" +
"<ul>\n" +
"<li id=\"search-node-1000\" data-state=\"closed\" data-hosts=\"{{ treeData[0].hosts}}\" " +
"data-hosts=\"{{ treeData[0].hosts }}\" " +
"data-description=\"{{ treeData[0].description }}\" " +
"data-failures=\"{{ treeData[0].failures }}\" " +
"data-groups=\"{{ treeData[0].groups }}\" " +
"data-name=\"{{ treeData[0].name }}\" " +
"><a href=\"\" class=\"expand\"><i class=\"icon-caret-right\"></i></a> " +
"<i class=\"field-badge \{\{ 'icon-failures-' + treeData[0].failures \}\}\"></i> " +
"<a href=\"\" class=\"activate active\">{{ treeData[0].name }}</a> " +
"</li>\n" +
"</ul>\n";
var compiled = $compile(html)(scope);
container.append(compiled);
initialize();
// Expand the root node and show All Hosts
setTimeout(function() {
$('#search-node-1000 .expand').click();
$('#search-node-1000 .activate').click();
Wait('stop');
}, 500);
});
// // Responds to searchTreeReady, thrown from Hosts.js helper when the inventory tree
// // is ready
// if ($rootScope.hostTabInitRemove) {
// $rootScope.hostTabInitRemove();
// }
// $rootScope.hostTabInitRemove = $rootScope.$on('searchTreeReady', function(e, html) {
// Wait('start');
// var container = angular.element(document.getElementById('search-tree-container'));
// container.empty();
// var compiled = $compile(html)(scope);
// container.append(compiled);
// var links = container.find('a');
// for (var i=0; i < links.length; i++) {
// var link = angular.element(links[i]);
// if (link.hasClass('expand')) {
// link.unbind('click', toggle);
// link.bind('click', toggle);
// }
// if (link.hasClass('activate')) {
// link.unbind('click', activate);
// link.bind('click', activate);
// }
// }
}
}
}]);
// Wait('stop');
// //initialize();
// // Expand the root node and show All Hosts
// //setTimeout(function() {
// // $('#search-node-1000 .expand').click();
// // $('#search-node-1000 .activate').click();
// // Wait('stop');
// // }, 500);
// });
// }
// }
// }]);

View File

@ -941,7 +941,8 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
// build the hosts page
html += "<div class=\"row\">\n";
html += "<div class=\"col-lg-3\" id=\"search-tree-target\">\n";
html += "<div aw-tree=\"searchTree\"></div>\n";
//html += "<div aw-tree=\"searchTree\"></div>\n";
html += "<div class=\"search-tree well\" id=\"search-tree-container\">\n</div>\n";
html += "</div>\n";
html += "<div class=\"col-lg-9\">\n";
html += "<div class=\"hosts-well well\">\n";
@ -978,8 +979,8 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
// "data-placement=\"top\"></th>\n";
for (var fld in form.fields) {
html += "<th class=\"list-header\" id=\"" + form.iterator + '-' + fld + "-header\" ";
html += (!form.fields[fld].nosort) ? "ng-click=\"sort('" + form.iterator + "', '" + fld + "')\"" : "";
html += "<th class=\"list-header\" id=\"" + fld + "-header\" ";
html += (!form.fields[fld].nosort) ? "ng-click=\"sort('"+ fld + "')\"" : "";
html += ">";
html += (form['fields'][fld].label && form['fields'][fld].type !== 'DropDown') ? form['fields'][fld].label : '';
if (form.fields[fld].nosort == undefined || form.fields[fld].nosort == false) {