ng-repeat中的angular.js条件标记

时间:2022-08-23 23:33:05

I'm using angular.js and (for the sake of argument) bootstrap. Now I need to iterate on "things" and display them in ''rows'' :

我正在使用angular.js和(为了参数)bootstrap。现在我需要迭代“事物”并在“行”中显示它们:

<div class="row">
  <div class="span4">...</div>
  <div class="span4">...</div>
  <div class="span4">...</div>
</div>
<div class="row">
  etc...

Now, how can I close my .row div on every third thing with angular? I tried ui-if from angular-ui but even that doesn't make it.

现在,我怎么能用角度来关闭每三分之一的.row div?我试过ui-if来自angular-ui,但即便如此也没有。

If I were to use server-side rendering, I would do something like this (JSP syntax here, but does not matter) :

如果我要使用服务器端渲染,我会做这样的事情(JSP语法在这里,但无关紧要):

<div class="row>
  <c:forEach items="${things}" var="thing" varStatus="i">
    <div class="span4">
        ..
    </div>
  <%-- Here is the trick:--%>
  <c:if test="${i.index % 3 == 2}">
          </div><div class="row">
  </c:if>
  </c:forEach>
</div>

Note that I need to actually alter the DOM here, not just css-hiding elements. I tried with the repeat on the .row and .span4 divs, with no avail.

请注意,我需要在这里实际更改DOM,而不仅仅是css隐藏元素。我尝试重复使用.row和.span4 div,但没有用。

3 个解决方案

#1


19  

Edit Nov 12, 2013

It seems that not only did angular change a little in 1.2, but that there is an even better method. I've created two filters. I tried to combine them into one but got digest errors. Here are the two filters:

似乎1.2中的角度变化不仅有点变化,而且还有更好的方法。我创建了两个过滤器。我试图将它们合并为一个,但却出现了摘要错误。以下是两个过滤器:

.filter("mySecondFilter", function(){
    return function(input, row, numColumns){
        var returnArray = [];
        for(var x = row * numColumns; x < row * numColumns + numColumns; x++){
            if(x < input.length){
                returnArray.push(input[x]);                    
            }
            else{
                returnArray.push(""); //this is used for the empty cells
            }
        }
        return returnArray;   
    }
})
.filter("myFilter", function(){
    return function(input, numColumns){
        var filtered = [];
        for(var x = 0; x < input.length; x++){
            if(x % numColumns === 0){
                filtered.push(filtered.length);
            }
        }
        return filtered;
    }
});

And now the html will look like this:

现在html将如下所示:

<table border="1">
     <tr data-ng-repeat="rows in (objects | myFilter:numColumns)">
          <td data-ng-repeat="column in (objects | mySecondFilter:rows:numColumns)">{{ column.entry }}</td>
     </tr>  
</table>

jsFiddle: http://jsfiddle.net/W39Q2/

jsFiddle:http://jsfiddle.net/W39Q2/


Edit Sept 20, 2013

编辑2013年9月20日

While working with lots of data that needed dynamic columns I've come up with a better method.

在处理需要动态列的大量数据时,我想出了一个更好的方法。

HTML:

HTML:

<table border="1">
    <tr data-ng-repeat="object in (objects | myFilter:numColumns.length)">
        <td data-ng-repeat="column in numColumns">{{ objects[$parent.$index * numColumns.length + $index].entry }}</td>
    </tr>  
</table>

Javascript:

使用Javascript:

$scope.objects = [ ];
for(var x = 65; x < 91; x++){
    $scope.objects.push({
        entry: String.fromCharCode(x)
    });
}

$scope.numColumns = [];
$scope.numColumns.length = 3;

New Filter:

新过滤器:

.filter("myFilter", function(){
    return function(input, columns){
        var filtered = [];
        for(var x = 0; x < input.length; x+= columns){
             filtered.push(input[x]);   
        }
        return filtered;
    }
});

This allows it to be dynamic. To change the columns just change the numColumns.length. In the js fiddle you can see I've wired it up to a dropdown.

这使它成为动态的。要更改列,只需更改numColumns.length。在js小提琴中你可以看到我已将它连接到下拉列表。

jsFiddle: http://jsfiddle.net/j4MPK/

jsFiddle:http://jsfiddle.net/j4MPK/


Your html markup would look like this:

你的HTML标记看起来像这样:

<div data-ng-repeat="row in rows">
    <div data-ng-repeat="col in row.col">{{col}}</div>
</div>

And then you could make a variable in your controller like so:

然后你可以在控制器中创建一个变量,如下所示:

$scope.rows = [
    {col: [ 1,2,3,4 ]},
    {col: [ 5,6,7 ]},
    {col: [ 9,10,11,12 ]}
]; 

This way, you can have any number of columns you want.

这样,您可以拥有任意数量的列。

jsfiddle http://jsfiddle.net/rtCP3/39/

jsfiddle http://jsfiddle.net/rtCP3/39/


Edit I've modified the fiddle to now support having a flat array of objects:

编辑我已修改小提琴现在支持具有平面对象数组:

jsfiddle: http://jsfiddle.net/rtCP3/41/

jsfiddle:http://jsfiddle.net/rtCP3/41/

The html now looks like this:

html现在看起来像这样:

<div class="row" data-ng-repeat="row in rows">
    <div class="col" data-ng-repeat="col in cols">
        {{objects[$parent.$index * numColumns + $index].entry}}
    </div>
</div>  

And then in the controller i have:

然后在控制器中我有:

$scope.objects = [
    {entry: 'a'},
    {entry: 'b'},
    {entry: 'c'},
    {entry: 'd'},
    {entry: 'e'},
    {entry: 'f'},
    {entry: 'g'},
    {entry: 'h'}    
];

$scope.numColumns = 3;
$scope.rows = [];
$scope.rows.length = Math.ceil($scope.objects.length / $scope.numColumns);
$scope.cols = [];
$scope.cols.length = $scope.numColumns;

The $scope.numColumns variable is used to specify how many columns you want in each row.

$ scope.numColumns变量用于指定每行中需要的列数。


To handle dynamic array size changes, put a watch on the length of the array (not the whole array, that would be redundent)

要处理动态数组大小更改,请将监视放在数组的长度上(而不是整个数组,这将是redundent)

$scope.numColumns = 3;  
$scope.rows = [];    
$scope.cols = [];    
$scope.$watch("objects.length", function(){
    $scope.rows.length = Math.ceil($scope.objects.length / $scope.numColumns);
    $scope.cols.length = $scope.numColumns;        
});

jsfiddle: http://jsfiddle.net/rtCP3/45/

jsfiddle:http://jsfiddle.net/rtCP3/45/

#2


15  

Why not use something simple like this? http://jsfiddle.net/everdimension/ECCL7/3/

为什么不使用像这样简单的东西? http://jsfiddle.net/everdimension/ECCL7/3/

<div ng-controller="MyCtrl as ctr">
    <div class="row" ng-repeat="project in ctr.projects" ng-if="$index % 3 == 0">
         <h4 class="col-sm-4" ng-repeat="project in ctr.projects.slice($index, $index+3)">
            {{project}}
        </h4>
    </div>
</div>

#3


3  

I recommend a directive for a couple of reasons:

我推荐一个指令有几个原因:

  • it can be reused and parameterized in the HTML (i.e., "every 3rd thing" can be a directive attribute)
  • 它可以在HTML中重用和参数化(即“每三件事”可以是一个指令属性)
  • it does not require any controller code/$scope properties, and hence it does not require recalculation of controller $scope properties if the "things" array changes in size
  • 它不需要任何控制器代码/ $ scope属性,因此如果“things”数组的大小发生变化,则不需要重新计算controller $ scope属性

Here is a suggested element directive:

这是一个建议的元素指令:

<row-generator row-data=objects col-count=3></row-generator>

In the implementation I used code similar to your server-side example:

在实现中,我使用了类似于服务器端示例的代码:

myApp.directive('rowGenerator', function() {
    var rowTemplate = '<div class="row">',
        colTemplate = '<div class="span4">';
    return {
        restrict: 'E',
        // use '=' for colCount instead of '@' so that we don't
        // have to use attr.$observe() in the link function
        scope: { rowData: '=', colCount: '='},
        link: function(scope, element) {
            // To save CPU time, we'll just watch the overall
            // length of the rowData array for changes.  You
            // may need to watch more.
            scope.$watch('rowData.length', function(value) {
                var html = rowTemplate;
                for(var i=0; i < scope.rowData.length; i++) {
                    html += colTemplate + scope.rowData[i].key + '</div>';
                    if (i % scope.colCount == scope.colCount - 1) {
                        html += '</div>' + rowTemplate;
                    }
                }
                html += '</div>';
                // don't use replaceWith() as the $watch 
                // above will not work -- use html() instead
                element.html(html);
            })
        }
    }
});

Data:

数据:

$scope.things = [
    {key: 'one'},
    {key: 'two'},
    {key: 3},
    {key: 4},
    {key: 'five'},
    {key: 'six'},
    {key: 77},
    {key: 8}    
];

Fiddle

小提琴

To be efficient, the directive as shown only looks for changes to the length of the rowData (i.e., things) array. If you want to have the directive update the view if the value of one of the array elements changes, you'll need a more expensive $watch:

为了有效,所示的指令仅查找对rowData(即,thing)数组的长度的改变。如果你想让指令更新视图,如果其中一个数组元素的值发生变化,你需要一个更昂贵的$ watch:

scope.$watch('rowData', function(value){ ... }, true)

The true at the end does "shallow" dirty checking ("compares the object for equality rather than for reference) -- see docs.

最后的真实做“浅”脏检查(“比较对象的平等而不是参考) - 请参阅文档。

There's one thing I don't like about the directive -- it needs to know that rowData entries have a property with name key:

有一件事我不喜欢这个指令 - 它需要知道rowData条目有一个名为key的属性:

html += colTemplate + scope.rowData[i].key + '</div>';

#1


19  

Edit Nov 12, 2013

It seems that not only did angular change a little in 1.2, but that there is an even better method. I've created two filters. I tried to combine them into one but got digest errors. Here are the two filters:

似乎1.2中的角度变化不仅有点变化,而且还有更好的方法。我创建了两个过滤器。我试图将它们合并为一个,但却出现了摘要错误。以下是两个过滤器:

.filter("mySecondFilter", function(){
    return function(input, row, numColumns){
        var returnArray = [];
        for(var x = row * numColumns; x < row * numColumns + numColumns; x++){
            if(x < input.length){
                returnArray.push(input[x]);                    
            }
            else{
                returnArray.push(""); //this is used for the empty cells
            }
        }
        return returnArray;   
    }
})
.filter("myFilter", function(){
    return function(input, numColumns){
        var filtered = [];
        for(var x = 0; x < input.length; x++){
            if(x % numColumns === 0){
                filtered.push(filtered.length);
            }
        }
        return filtered;
    }
});

And now the html will look like this:

现在html将如下所示:

<table border="1">
     <tr data-ng-repeat="rows in (objects | myFilter:numColumns)">
          <td data-ng-repeat="column in (objects | mySecondFilter:rows:numColumns)">{{ column.entry }}</td>
     </tr>  
</table>

jsFiddle: http://jsfiddle.net/W39Q2/

jsFiddle:http://jsfiddle.net/W39Q2/


Edit Sept 20, 2013

编辑2013年9月20日

While working with lots of data that needed dynamic columns I've come up with a better method.

在处理需要动态列的大量数据时,我想出了一个更好的方法。

HTML:

HTML:

<table border="1">
    <tr data-ng-repeat="object in (objects | myFilter:numColumns.length)">
        <td data-ng-repeat="column in numColumns">{{ objects[$parent.$index * numColumns.length + $index].entry }}</td>
    </tr>  
</table>

Javascript:

使用Javascript:

$scope.objects = [ ];
for(var x = 65; x < 91; x++){
    $scope.objects.push({
        entry: String.fromCharCode(x)
    });
}

$scope.numColumns = [];
$scope.numColumns.length = 3;

New Filter:

新过滤器:

.filter("myFilter", function(){
    return function(input, columns){
        var filtered = [];
        for(var x = 0; x < input.length; x+= columns){
             filtered.push(input[x]);   
        }
        return filtered;
    }
});

This allows it to be dynamic. To change the columns just change the numColumns.length. In the js fiddle you can see I've wired it up to a dropdown.

这使它成为动态的。要更改列,只需更改numColumns.length。在js小提琴中你可以看到我已将它连接到下拉列表。

jsFiddle: http://jsfiddle.net/j4MPK/

jsFiddle:http://jsfiddle.net/j4MPK/


Your html markup would look like this:

你的HTML标记看起来像这样:

<div data-ng-repeat="row in rows">
    <div data-ng-repeat="col in row.col">{{col}}</div>
</div>

And then you could make a variable in your controller like so:

然后你可以在控制器中创建一个变量,如下所示:

$scope.rows = [
    {col: [ 1,2,3,4 ]},
    {col: [ 5,6,7 ]},
    {col: [ 9,10,11,12 ]}
]; 

This way, you can have any number of columns you want.

这样,您可以拥有任意数量的列。

jsfiddle http://jsfiddle.net/rtCP3/39/

jsfiddle http://jsfiddle.net/rtCP3/39/


Edit I've modified the fiddle to now support having a flat array of objects:

编辑我已修改小提琴现在支持具有平面对象数组:

jsfiddle: http://jsfiddle.net/rtCP3/41/

jsfiddle:http://jsfiddle.net/rtCP3/41/

The html now looks like this:

html现在看起来像这样:

<div class="row" data-ng-repeat="row in rows">
    <div class="col" data-ng-repeat="col in cols">
        {{objects[$parent.$index * numColumns + $index].entry}}
    </div>
</div>  

And then in the controller i have:

然后在控制器中我有:

$scope.objects = [
    {entry: 'a'},
    {entry: 'b'},
    {entry: 'c'},
    {entry: 'd'},
    {entry: 'e'},
    {entry: 'f'},
    {entry: 'g'},
    {entry: 'h'}    
];

$scope.numColumns = 3;
$scope.rows = [];
$scope.rows.length = Math.ceil($scope.objects.length / $scope.numColumns);
$scope.cols = [];
$scope.cols.length = $scope.numColumns;

The $scope.numColumns variable is used to specify how many columns you want in each row.

$ scope.numColumns变量用于指定每行中需要的列数。


To handle dynamic array size changes, put a watch on the length of the array (not the whole array, that would be redundent)

要处理动态数组大小更改,请将监视放在数组的长度上(而不是整个数组,这将是redundent)

$scope.numColumns = 3;  
$scope.rows = [];    
$scope.cols = [];    
$scope.$watch("objects.length", function(){
    $scope.rows.length = Math.ceil($scope.objects.length / $scope.numColumns);
    $scope.cols.length = $scope.numColumns;        
});

jsfiddle: http://jsfiddle.net/rtCP3/45/

jsfiddle:http://jsfiddle.net/rtCP3/45/

#2


15  

Why not use something simple like this? http://jsfiddle.net/everdimension/ECCL7/3/

为什么不使用像这样简单的东西? http://jsfiddle.net/everdimension/ECCL7/3/

<div ng-controller="MyCtrl as ctr">
    <div class="row" ng-repeat="project in ctr.projects" ng-if="$index % 3 == 0">
         <h4 class="col-sm-4" ng-repeat="project in ctr.projects.slice($index, $index+3)">
            {{project}}
        </h4>
    </div>
</div>

#3


3  

I recommend a directive for a couple of reasons:

我推荐一个指令有几个原因:

  • it can be reused and parameterized in the HTML (i.e., "every 3rd thing" can be a directive attribute)
  • 它可以在HTML中重用和参数化(即“每三件事”可以是一个指令属性)
  • it does not require any controller code/$scope properties, and hence it does not require recalculation of controller $scope properties if the "things" array changes in size
  • 它不需要任何控制器代码/ $ scope属性,因此如果“things”数组的大小发生变化,则不需要重新计算controller $ scope属性

Here is a suggested element directive:

这是一个建议的元素指令:

<row-generator row-data=objects col-count=3></row-generator>

In the implementation I used code similar to your server-side example:

在实现中,我使用了类似于服务器端示例的代码:

myApp.directive('rowGenerator', function() {
    var rowTemplate = '<div class="row">',
        colTemplate = '<div class="span4">';
    return {
        restrict: 'E',
        // use '=' for colCount instead of '@' so that we don't
        // have to use attr.$observe() in the link function
        scope: { rowData: '=', colCount: '='},
        link: function(scope, element) {
            // To save CPU time, we'll just watch the overall
            // length of the rowData array for changes.  You
            // may need to watch more.
            scope.$watch('rowData.length', function(value) {
                var html = rowTemplate;
                for(var i=0; i < scope.rowData.length; i++) {
                    html += colTemplate + scope.rowData[i].key + '</div>';
                    if (i % scope.colCount == scope.colCount - 1) {
                        html += '</div>' + rowTemplate;
                    }
                }
                html += '</div>';
                // don't use replaceWith() as the $watch 
                // above will not work -- use html() instead
                element.html(html);
            })
        }
    }
});

Data:

数据:

$scope.things = [
    {key: 'one'},
    {key: 'two'},
    {key: 3},
    {key: 4},
    {key: 'five'},
    {key: 'six'},
    {key: 77},
    {key: 8}    
];

Fiddle

小提琴

To be efficient, the directive as shown only looks for changes to the length of the rowData (i.e., things) array. If you want to have the directive update the view if the value of one of the array elements changes, you'll need a more expensive $watch:

为了有效,所示的指令仅查找对rowData(即,thing)数组的长度的改变。如果你想让指令更新视图,如果其中一个数组元素的值发生变化,你需要一个更昂贵的$ watch:

scope.$watch('rowData', function(value){ ... }, true)

The true at the end does "shallow" dirty checking ("compares the object for equality rather than for reference) -- see docs.

最后的真实做“浅”脏检查(“比较对象的平等而不是参考) - 请参阅文档。

There's one thing I don't like about the directive -- it needs to know that rowData entries have a property with name key:

有一件事我不喜欢这个指令 - 它需要知道rowData条目有一个名为key的属性:

html += colTemplate + scope.rowData[i].key + '</div>';