javascript 使用 ng-repeat 对 Angularjs 表进行排序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12744788/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Angularjs table sort with ng-repeat
提问by alchemication
I have an HTML table and want to sort my records ($scope.records
in ctrl) by clicking on table headers ($scope.headers
in ctrl),
我有一个 HTML 表格,想$scope.records
通过单击表格标题($scope.headers
在 ctrl 中)对我的记录(在 ctrl 中)进行排序,
Can anyone explain why does that work:
谁能解释为什么这样做:
<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>
And that doesn't:
而这不会:
<th ng-repeat="header in headers">
<a ng-click="sortColumn=headers[$index];reverse=!reverse">{{ headers[$index] }}</a>
</th>
Here is the code for the records:
这是记录的代码:
<tr ng-repeat="arr in records | orderBy:sortColumn:reverse">
<td ng-repeat="val in arr" ng-bind-html-unsafe="arr[headers[$index]]</td>
</tr>
I have 58 columns in my table so would be much better to loop through the table headers...
我的表中有 58 列,因此循环遍历表标题会好得多...
回答by Gloopy
As David suggested this is likely scope related. Since ngRepeatcreates a new scope your ngClick
is setting the sortColumn
and reverse
in its own child scope for each column header.
正如大卫所说,这可能与范围有关。由于ngRepeat创建一个新的范围你ngClick
是设置sortColumn
并reverse
在其自己的子范围为每列标题。
One way around this to ensure you are modifying the values in the same scope would be to create a function on the scope and call that in your ngClick passing in the index:
解决此问题以确保您在同一范围内修改值的一种方法是在范围内创建一个函数并在您的 ngClick 中调用该函数并传入索引:
$scope.toggleSort = function(index) {
if($scope.sortColumn === $scope.headers[index]){
$scope.reverse = !$scope.reverse;
}
$scope.sortColumn = $scope.headers[index];
}
with this as your markup:
以此作为您的标记:
<th ng-repeat="header in headers">
<a ng-click="toggleSort($index)">{{ headers[$index] }}</a>
</th>
Here is a fiddlewith an example.
Another option would be to bind to a non-primitive type like this (the child scopes will be accessing the same object):
另一种选择是绑定到这样的非原始类型(子作用域将访问同一个对象):
$scope.columnSort = { sortColumn: 'col1', reverse: false };
with this as your markup:
以此作为您的标记:
<th ng-repeat="header in headers">
<a ng-click="columnSort.sortColumn=headers[$index];columnSort.reverse=!columnSort.reverse">{{ headers[$index] }}</a>
</th>
Here is a fiddlewith an example.
回答by Mark Rajcok
Extending Gloopy's answer, yet another option is to modify the parent's properties in the ng-repeat for the primitive types:
扩展 Gloopy 的答案,另一种选择是在原始类型的 ng-repeat 中修改父级的属性:
<a ng-click="$parent.sortColumn=headers[$index];$parent.reverse=!$parent.reverse">{{ headers[$index] }}
Note however that $parent is not a documented property of scope, so this is somewhat of a hack, so use at your own risk.
但是请注意, $parent 不是scope的记录属性,所以这有点像黑客,所以使用风险自负。
I wish AngularJS had a better way of dealing with these "inner scopes" that are created by ng-repeat, ng-switch, etc. because quite often we need to modify parent scope properties that are primitives.
我希望 AngularJS 有更好的方法来处理这些由 ng-repeat、ng-switch 等创建的“内部作用域”,因为我们经常需要修改原始作用域的父作用域属性。
See also Gloopy's insightful comment about scope inheritance as it relates to primitives and non-primitives here.
另请参阅 Gloopy 关于范围继承的深刻评论,因为它与此处的原语和非原语有关。
回答by C.D.
I don't know what sort of data is in your records, so for my sample I just used an array of JSON values. I have tried several different sorting plugins for my Javascript with Angular and nothing worked. In the long run I have discovered you don't necessarily need those extras.
我不知道您的记录中有什么样的数据,因此对于我的示例,我只使用了一组 JSON 值。我已经用 Angular 为我的 Javascript 尝试了几种不同的排序插件,但没有任何效果。从长远来看,我发现你不一定需要这些额外的东西。
Since AngularJS is good at processing javascript data-structures for displaying in HTML, you can just rearrange the javascript-arrays in memory, and AngularJS picks up on the changes. This example allows clicking the headers of the table, which will trigger a sorting based on that columns data type. If it is already sorted on that column, it will reverse-sort the column.The type detection is done through the presented isNumeric() function, and one two-tiny tweaks:
由于 AngularJS 擅长处理 javascript 数据结构以显示在 HTML 中,因此您只需重新排列内存中的 javascript 数组,AngularJS 就会接受更改。此示例允许单击表的标题,这将触发基于该列数据类型的排序。如果它已经在该列上排序,它将对该列进行反向排序。类型检测是通过提供的 isNumeric() 函数和两个微小的调整完成的:
- Added checks if inputting on the '#' symbol as a header and sorts as a number in the toggleSort method. This can easily be removed by users if preferred.
- When toggleSort attempts to sort alphabetically, if it catches a TypeError it then switches to sorting on numbers.
- 添加了检查是否在“#”符号上输入作为标题并在 toggleSort 方法中按数字排序。如果愿意,用户可以轻松地将其删除。
- 当 toggleSort 尝试按字母顺序排序时,如果它捕获 TypeError 则切换到对数字进行排序。
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/[email protected]/dist/jquery.min.js"></script>
<script data-require="[email protected]" 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>
I have put together a working Plunker exampleto demonstrate. Just click on the headers and they will sort the array in memory, where AngularJS will pick up on the changes and refresh that portion of the DOM.
我已经整理了一个有效的Plunker 示例来演示。只需单击标题,它们就会对内存中的数组进行排序,AngularJS 将在其中获取更改并刷新 DOM 的该部分。