使用ng-repeat对Angularjs表进行排序

时间:2012-10-05 10:57:59

标签: javascript angularjs sorting html-table

我有一个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列,所以循环表标题会好得多......

3 个答案:

答案 0 :(得分:11)

正如大卫所说,这可能与范围有关。由于ngRepeat创建了一个新范围,因此ngClick会在每个列标题的子范围内设置sortColumnreverse

确保修改同一范围内的值的一种方法是在范围上创建一个函数,并在索引中调用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] }}

Here is a fiddle

但请注意,$ parent不是scope的文档属性,因此这有点像黑客,所以使用风险自负。

我希望AngularJS有更好的方法来处理由ng-repeat,ng-switch等创建的这些“内部范围”,因为我们经常需要修改作为原语的父范围属性。

另见Gloopy对范围继承的深刻见解,因为它与基元和非基元here有关。

答案 2 :(得分:0)

我不知道您的记录中包含哪种数据,因此对于我的示例,我只使用了JSON值数组。我已经使用Angular为Javascript尝试了几种不同的排序插件,但没有任何效果。从长远来看,我发现您不一定需要这些额外功能。

由于AngularJS擅长处理JavaScript数据结构以HTML格式显示,因此您只需重新排列内存中的javascript数组,AngularJS就会掌握这些更改。 此示例允许单击表的标题,这将触发基于该列数据类型的排序。如果已在该列上对其进行了排序,则它将对该列进行反向排序。类型检测是通过提供的isNumeric()函数和一个两个小小的调整来完成的:

  1. 已添加检查是否以“#”符号作为标题输入,并在toggleSort方法中将其排序为数字。如果需要,用户可以轻松删除。
  2. 当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/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的该部分。