我有一个HTML表格,想要通过点击表格标题(ctrl中的$scope.records
)对我的记录(ctrl中的$scope.headers
)进行排序,
任何人都可以解释为什么会这样做:
<th>
<a ng-click="sortColumn=headers[0];reverse=!reverse">{{ headers[0] }}</a>
</th>
<th>
<a ng-click="sortColumn=headers[1];reverse=!reverse">{{ headers[1] }}</a>
</th>
那不是:
<th ng-repeat="header in headers">
<a ng-click="sortColumn=headers[$index];reverse=!reverse">{{ headers[$index] }}</a>
</th>
以下是记录的代码:
<tr ng-repeat="arr in records | orderBy:sortColumn:reverse">
<td ng-repeat="val in arr" ng-bind-html-unsafe="arr[headers[$index]]</td>
</tr>
我的表中有58列,所以循环表标题会好得多......
答案 0 :(得分:11)
正如大卫所说,这可能与范围有关。由于ngRepeat创建了一个新范围,因此ngClick
会在每个列标题的子范围内设置sortColumn
和reverse
。
确保修改同一范围内的值的一种方法是在范围上创建一个函数,并在索引中调用ngClick中的函数:
$scope.toggleSort = function(index) {
if($scope.sortColumn === $scope.headers[index]){
$scope.reverse = !$scope.reverse;
}
$scope.sortColumn = $scope.headers[index];
}
以此作为您的标记:
<th ng-repeat="header in headers">
<a ng-click="toggleSort($index)">{{ headers[$index] }}</a>
</th>
Here is a fiddle举了一个例子。
另一个选择是绑定到这样的非基本类型(子范围将访问同一个对象):
$scope.columnSort = { sortColumn: 'col1', reverse: false };
以此作为您的标记:
<th ng-repeat="header in headers">
<a ng-click="columnSort.sortColumn=headers[$index];columnSort.reverse=!columnSort.reverse">{{ headers[$index] }}</a>
</th>
Here is a fiddle举了一个例子。
答案 1 :(得分:3)
扩展Gloopy的答案,另一个选择是修改基元类型的ng-repeat中的父属性:
<a ng-click="$parent.sortColumn=headers[$index];$parent.reverse=!$parent.reverse">{{ headers[$index] }}
但请注意,$ parent不是scope的文档属性,因此这有点像黑客,所以使用风险自负。
我希望AngularJS有更好的方法来处理由ng-repeat,ng-switch等创建的这些“内部范围”,因为我们经常需要修改作为原语的父范围属性。
另见Gloopy对范围继承的深刻见解,因为它与基元和非基元here有关。
答案 2 :(得分:0)
我不知道您的记录中包含哪种数据,因此对于我的示例,我只使用了JSON值数组。我已经使用Angular为Javascript尝试了几种不同的排序插件,但没有任何效果。从长远来看,我发现您不一定需要这些额外功能。
由于AngularJS擅长处理JavaScript数据结构以HTML格式显示,因此您只需重新排列内存中的javascript数组,AngularJS就会掌握这些更改。 此示例允许单击表的标题,这将触发基于该列数据类型的排序。如果已在该列上对其进行了排序,则它将对该列进行反向排序。类型检测是通过提供的isNumeric()函数和一个两个小小的调整来完成的:
var app = angular.module("app", []);
app.controller("MainController", function($scope) {
$scope.samplePositions = [
{"#": "1", "Unique ID": "100130", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 1", "Status": "Available"},
{"#": "2", "Unique ID": "100131", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 2", "Status": "Available"},
{"#": "3", "Unique ID": "100132", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 3", "Status": "Available"},
{"#": "4", "Unique ID": "100133", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 4", "Status": "Available"},
{"#": "5", "Unique ID": "100134", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 5", "Status": "Checked Out"},
{"#": "6", "Unique ID": "100135", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 6", "Status": "Checked Out"},
{"#": "7", "Unique ID": "100136", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 7", "Status": "Checked Out"},
{"#": "8", "Unique ID": "100137", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 8", "Status": "Checked Out"},
{"#": "9", "Unique ID": "100138", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 1", "Status": "Available"},
{"#": "10", "Unique ID": "100139", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 1", "Status": "Available"},
{"#": "11", "Unique ID": "100140", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 2", "Status": "Available"},
{"#": "12", "Unique ID": "100141", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 3", "Status": "Lost"},
{"#": "13", "Unique ID": "100142", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 4", "Status": "Lost"},
{"#": "14", "Unique ID": "100143", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 3 - Box 1 - Position 1", "Status": "Available"},
{"#": "15", "Unique ID": "100144", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 3 - Box 1 - Position 2", "Status": "Available"},
{"#": "16", "Unique ID": "100145", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 1", "Status": "Checked Out"},
{"#": "17", "Unique ID": "100146", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 2", "Status": "Available"},
{"#": "18", "Unique ID": "100147", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 3", "Status": "Available"},
{"#": "19", "Unique ID": "100148", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 4", "Status": "Checked Out"},
{"#": "20", "Unique ID": "100149", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 5 - Box 1 - Position 1", "Status": "Available"}
]
// Dynamically get the entry headers to use with displaying the nested data via header-key lookups
// Assumes all lines contain same key-text data
$scope.samplePositionsHeaderKeys = []; // Contains only the key-data, not the values
for (var key in $scope.samplePositions[0]) {
if ($scope.samplePositions[0].hasOwnProperty(key)) {
$scope.samplePositionsHeaderKeys.push(key);
}
}
/**
* Determine if the input value is a number or not.
* @param n The input value to be checked for numeric status.
* @returns true if parameter is numeric, or false otherwise.
*
* This method uses the following evaluations to determine if input is a numeric:
*
* (5); // true
* ('123'); // true
* ('123abc'); // false
* ('q345'); // false
* (null); // false
* (""); // false
* ([]); // false
* (' '); // false
* (true); // false
* (false); // false
* (undefined); // false
* (new String('')); // false
*
* @author C.D. (modified by)
* @original https://stackoverflow.com/a/1421988/10930451
*
*/
function isNumeric(n) {
if (!isNaN(parseFloat(n)) && !isNaN(n - 0) && n !== null && n !== "") {
return true;
}
return false;
}
/**
* Column Sort Method (generic). Sort based on target column header or reverse sort if already selected on that.
* @param dataSource The array of JSON data to be sorted
* @param headers The array of JSON object-keys (table column headers) to be referenced
* @param index The target JSON object-key to sort the table columns based upon
*
* @author C.D.
*/
$scope.lastSortIndex = 0;
$scope.toggleSort = function (dataSource, headers, index) {
if ($scope.lastSortIndex === index) {
dataSource.reverse();
}
else {
var key = headers[index];
if (key === "#" || isNumeric(dataSource[key])) { // Compare as numeric or on '#' sign
dataSource.sort((a, b) => parseFloat(a[key]) - parseFloat(b[key]));
}
else // Compare as Strings
{
try { // Attempt to sort as Strings
dataSource.sort((a, b) => a[key].localeCompare(b[key]));
} catch (error) {
if (error.name === 'TypeError') { // Catch type error, actually sort as Numeric
dataSource.sort((a, b) => parseFloat(a[key]) - parseFloat(b[key]));
}
}
}
$scope.lastSortIndex = index;
}
}
});
<html ng-app="app">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>AngularJS - Hello World</title>
<script data-require="jquery@*" data-semver="3.1.1" src="https://cdn.jsdelivr.net/npm/jquery@3.1.1/dist/jquery.min.js"></script>
<script data-require="angular.js@1.3.13" data-semver="1.3.13" src="https://code.angularjs.org/1.3.13/angular.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="MainController">
<div class="container">
<table class="table table-hover table-sm">
<thead>
<tr>
<th ng-repeat="header in samplePositionsHeaderKeys">
<a ng-click="toggleSort(samplePositions, samplePositionsHeaderKeys, $index)">{{ header }}</a>
</th>
</tr>
</thead>
<tbody>
<!-- Data is nested, so double-repeat to extract and display -->
<tr ng-repeat="row in samplePositions" >
<td ng-repeat="key in samplePositionsHeaderKeys">
{{row[key]}}
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
我整理了一个Plunker example来进行演示。只需单击标题,它们就会对内存中的数组进行排序,AngularJS将在那里获取更改并刷新DOM的该部分。