9

我有一个 HTML 表格,想$scope.records通过单击表格标题($scope.headers在 ctrl 中)对我的记录(在 ctrl 中)进行排序,

谁能解释为什么会这样:

<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 列,所以循环遍历表头会更好......

4

3 回答 3

12

As David suggested this is likely scope related. Since ngRepeat creates a new scope your ngClick is setting the sortColumn and reverse in its own child scope for each column header.

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:

$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 fiddle with 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 fiddle with an example.

于 2012-10-05T15:21:37.603 回答
3

扩展 Gloopy 的答案,另一种选择是修改原始类型的 ng-repeat 中的父级属性:

<a ng-click="$parent.sortColumn=headers[$index];$parent.reverse=!$parent.reverse">{{ headers[$index] }}

这是一个小提琴

但是请注意, $parent 不是scope的记录属性,所以这有点像 hack,所以使用风险自负。

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

另请参阅 Gloopy 关于范围继承的富有洞察力的评论,因为它与原语和非原语有关

于 2012-10-05T16:02:41.727 回答
0

我不知道您的记录中有什么类型的数据,所以对于我的示例,我只使用了一个 JSON 值数组。我用 Angular 为我的 Javascript 尝试了几个不同的排序插件,但没有任何效果。从长远来看,我发现你不一定需要那些额外的东西。

由于 AngularJS 擅长处理 javascript 数据结构以在 HTML 中显示,因此您只需重新排列内存中的 javascript-arrays,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 示例来演示。只需单击标题,它们就会在内存中对数组进行排序,AngularJS 将在其中获取更改并刷新 DOM 的该部分。

于 2019-10-04T01:23:44.143 回答