How can I limit rows showing in an ng-repeat? - angularjs

I have an ng-repeat that looks like this:
<div ng-repeat="subject in subjects">
<div>{{ subject.name }}</div>
</div>
Is there some way that I can limit the display of rows to rows where subject.id is less than 50 if roleId > 1 and otherwise show all rows?
$scope.roleId = 0 means no filter
$scope.roleId > 0 means filter to show only rows in $scope.subjects where subject.id is less than 50

You can accomplish this using ng-if or use a filter
<div ng-repeat="subject in subjects" ng-if="subject.id>50">
The part about the role id in question is not clear

Use Limit to Filter
<div ng-repeat="subject in subjects | limitTo:quantity">
<div>{{ subject.name }}</div>
</div>
then set the $scope.quantity in your controller based on your business logic.
Use a Custom Filter
to use a custom filter you just set a new $scope function in your controller.
$scope.yourFilter = function (items) {
// probably need some more logic but you get the idea
return items.id < 50;
};
thin in the filter it should look like this
<div ng-repeat="subject in subjects | filter:yourFilter(subjects)">
<div>{{ subject.name }}</div>
</div>

Add a filter
<div ng-repeat="subject in subjects | filter:filterFn">
<div>{{ subject.name }}</div>
</div>
And in your controller, have this function:
$scope.filterFn = function (item) {
if ($scope.roleId > 1) {
return (item.Id < 50) ? true : false;
}
else
return true;
};
If I understood your comments correctly.

Related

How to use ng-repeat to display according to starting letter of item values

I have an array with items names ranging from A-Z.
What I want to do is ng-repeat for each letter of the alphabet.
For A letters in Array:
<div ng-repeat="item in items | orderBy: 'item_name'">
{{item.item_name}}
</div>
For B letters in Array:
<div ng-repeat="item in items | orderBy: 'item_name'">
{{item.item_name}}
</div>
How can I achieve this?
You can achieve this with custom filter
<div ng-repeat="item in items | filter:criteriaMatch('z')">
{{item.name}}
</div>
<div ng-repeat="item in items | filter:criteriaMatch('A')">
{{item.name}}
</div>
and filter function like below
$scope.criteriaMatch = function(x) {
return function( item ) {
return item.name.startsWith(x);
};
};
here is Working fiddle
hope it helps you :)
You can use the groupBy filter using the angular.filter module:
<div ng-repeat="item in items | groupBy: 'item_name.substr(0,1)'">
{{item.item_name}}
</div>

Conditional Content with ng-repeat in AngularJS

I'm trying to build a list of people with dividers for each letter (similar to a phone's address book).
people = ['Angela','Annie','Bob','Chris'];
Result:
A
Angela
Annie
B
Bob
C
Chris
I want to do something similar to this pseudocode using Angular:
<div id="container" ng-init="lastCharacter = ''">
#foreach(person in people)
#if(firstCharacter(person.name) != lastCharacter)
<div class="divider-item">{{firstCharacter(person.name)}}</div>
{{lastCharacter = firstCharacter(person.name)}}
#endif
<div class="person-item">{{person.name}}</div>
#endforeach
</div>
What is the easiest way to achieve this? I couldn't come up with an elegant solution using ng-repeat.
You should create a custom filter and move the grouping logic inside that:
app.filter('groupByFirstLetter',function(){
return function(input){
var result = [];
for(var i = 0;i < input.length; i++){
var currentLetter = input[i][0]
var current = _.find(result, function(value){
return value.key == currentLetter;
});
if(!current){
current = {key: currentLetter, items: []}
result.push(current);
}
current.items.push(input[i]);
}
return result;
};
});
Then the view gets simple:
<div ng-repeat="personGroup in people | groupByFirstLetter">
<div class="divider-item">{{personGroup.key}}</div>
<div class="person-item" ng-repeat="person in personGroup.items">
{{person}}
</div>
</div>
Here's a little working example in plunker : http://plnkr.co/edit/W2qnTw0PVgcQWS6VbyM0?p=preview
It's working but it throws some exceptions, you will get the idea.
try (use sorted list)
<div id="container" ng-repeat="person in people">
<div class="divider-item"
ng-if="$index == 0 || ($index > 0 && firstCharacter(person.name) != firstCharacter(people[$index-1].name))">{{firstCharacter(person.name)}}</div>
<div class="person-item">{{person.name}}</div>
</div>
hope, you have firstCharacter function defined in scope. Or you can simpley use person.name.charAt(0).
edit: as this will contain id container for all person. so best would be using an inner div inside container and run ng-repeat on there
<div id="container" >
<div ng-repeat="person in people">
<div class="divider-item"
ng-if="$index == 0 || ($index > 0 && firstCharacter(person.name) != firstCharacter(people[$index-1].name))">{{firstCharacter(person.name)}}</div>
<div class="person-item">{{person.name}}</div>
</div>
</div>

using part of a list in angularJS ng-repeat

I'm doing an angularJS data-binding as follows:
<div class="timeSlotWrapper">
<div class="timeSlotItem" ng-repeat="t in timeSlots" time-slot-obj="t" id ="{{t.id}}"
ng-click="timeSlotClick(cardId, $index)">{{ t.signalingTimeSlot}}</div>
</div>
the collection timeslots contain some 60 items, 30 of each belonging to one category. Lets say typeId is 0 for 30, and 1 for the other 30. I want to use ng-repeat for the first 30 only. Is it possible to do within ng-repeat or do I have to create the collection according to my need in code behind?
<div class="timeSlotWrapper">
<div class="timeSlotItem" ng-repeat="t in timeSlots | filter:{typeId:0}" time-slot-obj="t" id ="{{t.id}}"
ng-click="timeSlotClick(cardId, $index)">{{ t.signalingTimeSlot}}</div>
</div>
You can make use of angular filter. Example for same
myApp.filter('filterList', function () {
return function(id) {
if(id==1)
return id;
}
});
And in your html markup
<div class="timeSlotItem" ng-repeat="t in timeSlots | filterList:t.id" time-slot-obj="t" id ="{{t.id}}"
ng-click="timeSlotClick(cardId, $index)">{{ t.signalingTimeSlot}}</div>
UPDATE:
If 1 need not be hardcoded then a $scope object can be used in the filter:
myApp.filter('filterList', function () {
return function($scope) {
$scope.Objs.forEach(function(Obj){
if(id==$scope.Obj.id) {
return id;
}
});
}
});
and in html markup pass this object
<div class="timeSlotItem" ng-repeat="t in timeSlots | filterList:this" time-slot-obj="t" id ="{{t.id}}"
ng-click="timeSlotClick(cardId, $index)">{{ t.signalingTimeSlot}}</div>
Documentation on Angular Filters

AngularJS - How to access to the next item from a ng-repeat in the controller

I'd like to access to the parameters of the next item on screen when clicking on a button.
I use a ng-repeat in my html file:
<li ng-repeat="item in items | filter:query" ng-show="isSelected($index)">
<img src="xxx.jpg" />
</li>
And the index in my Controller with a loop:
$scope.itemNext = function () {
$scope._Index = ($scope._Index < $scope.nbItems - 1) ? ++$scope._Index : 0;
$scope.functionToCallWithNextItem(nextItem.param1);
};
A simple $scope.items[$scope._Index].param1 instead of nextItem.param1 wouldn't work as the data is filtered so $index+1 from $scope.items isn't necessarily the good one.
Any idea ?
You can assign your filtered data to a variable:
<li ng-repeat="item in (filteredItems = (items | filter:query))">
Then use $index + 1 to get the next item:
<a ng-click="itemNext(filteredItems[$index + 1])">
Demo: http://plnkr.co/edit/OdL5rIxtTEHnQCC3g4LS?p=preview
It's simpy that
<div ng-repeat="item in items">
current: {{item.value}}
next: {{ items[$index + 1].value}}
previous: {{ items[$index - 1].value}}
</div>

Filter results 6 through 10 of 100 with ng-repeat in AngularJS

I see the limitTo filter in the docs, which allows me to limit the first 5, or last 5 results, but I want to set where my limit starts so I can show the second set of 5 results.
Is there a built in filter for that?
Since Angular 1.4.0, the limitTo filter takes an optional begin argument:
<div ng-repeat="item in items | limitTo:5:5">{{item}}</div>
In older versions, writing a custom filter is fairly straightforward. Here's a naïve implementation based on Array#slice (note you pass the first and last index, instead of a count):
app.filter('slice', function() {
return function(arr, start, end) {
return (arr || []).slice(start, end);
};
});
<div ng-repeat="item in items | slice:6:10">{{item}}</div>
Working jsFiddle: http://jsfiddle.net/BinaryMuse/vQUsS/
Alternatively, you can simply steal the entire Angular 1.4.0 implementation of limitTo:
function limitToFilter() {
return function(input, limit, begin) {
if (Math.abs(Number(limit)) === Infinity) {
limit = Number(limit);
} else {
limit = toInt(limit);
}
if (isNaN(limit)) return input;
if (isNumber(input)) input = input.toString();
if (!isArray(input) && !isString(input)) return input;
begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
if (limit >= 0) {
return input.slice(begin, begin + limit);
} else {
if (begin === 0) {
return input.slice(limit, input.length);
} else {
return input.slice(Math.max(0, begin + limit), begin);
}
}
};
}
AngularJS provides that functionality already out of the box. If you carefully read the limitTo documentation it allows you to specify a negative value for the limit. That means N elements at the end so if you want to process 5 results after an offset of 5 you need to do the following:
<div ng-repeat="item in items | limitTo: 10 | limitTo: -5">{{item}}</div>
I started playing around with customer filters but then found out you can just call slice inside of the ng-repeat expression:
<div ng-repeat="item in items.slice(6, 10)">{{item}}</div>
As bluescreen said, it can be done using only the limitTo filter, although dealing with the last page problem noticed by Harry Oosterveen needs some extra work.
I.e. using ui-bootstrap pagination directive properties:
ng-model = page // current page
items-per-page = rpp // records per page
total-items = count // total number of records
The expression should be:
<div ng-repeat="item in items | limitTo: rpp * page | limitTo: rpp * page < count ? -rpp : rpp - (rpp * page - count)">{{item}}</div>
Here's a functional example on how to filter a list with offset and limit:
<!doctype html>
<html lang="en" ng-app="phonecatApp">
<head>
<script src="lib/angular/angular.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
Offset: <input type="text" ng-model="offset" value="0" /><br />
Limit: <input type="text" ng-model="limit" value="10" /><br />
<p>offset limitTo: {{offset - phones.length}}</p>
<p>Partial list:</p>
<ul>
<li ng-repeat="phone in phones | limitTo: offset - phones.length | limitTo: limit">
{{$index}} - {{phone.name}} - {{phone.snippet}}
</li>
</ul>
<hr>
<p>Whole list:</p>
<ul>
<li ng-repeat="phone in phones">
{{$index}} - {{phone.name}} - {{phone.snippet}}
</li>
</ul>
</body>
</html>
The example is based on the second step of AngularJS tutorial.
Note that the filter for the offset has to be put as the first filter if you want to cover the generic case where the limit is uknnown.
from bluescreen answer :
<div ng-repeat="item in items | limitTo: 10 | limitTo: -5">{{item}}</div>
you might also find a scenario where you need to take N-th item till the last item, this is another way using limitTo:
<div ng-repeat="item in items | limitTo:-(items.length-5)>{{item}}</div>
negative sign will take as much as items.length from last, then minus 5 inside the bracket will skip the first 5 items
There's a tidier way to do it without having to invoke a filter and will behave more like a paginator:
$scope.page = 0;
$scope.items = [ "a", "b", "c", "d", "e", "f", "g" ];
$scope.itemsLimit = 5;
$scope.itemsPaginated = function () {
var currentPageIndex = $scope.page * $scope.itemsLimit;
return $scope.items.slice(
currentPageIndex,
currentPageIndex + $scope.itemsLimit);
};
And just stick that in your view:
<ul>
<li ng-repeat="item in itemsPaginated() | limitTo:itemsLimit">{{item}}</li>
</ul>
Then you can just increment/decrement $scope.page:
$scope.page++;
You can also create your own reusable filter startFrom like so:
myApp.filter('startFrom', function () {
return function (input, start) {
start = +start;
return input.slice(start);
}
});
and then use it in your ng-repeat like so:
ng-repeat="item in items | startFrom: 5 | limitTo:5
That way it is done "in a spirit of AngularJs filters", testable, can have some "extra" logic if needed, etc.
#bluescreen's ans and #Brandon's ans are not equivalent.
for example:
var pageSize = 3;
var page = 2;
var items = [1, 2, 3, 4];
--
item in items | limitTo: page*pageSize | limitTo: -1*pageSize
produce [2, 3, 4]
item in items | slice: (page-1)*pageSize : page*pageSize
produce [4]
For Ionic based projects find the solution below:
mainpage.html:
<ion-view view-title="My Music">
<ion-content>
<ion-list>
<ion-item ng-repeat="track in tracks | limitTo: limit | limitTo: -10 ">
{{track.title}}
</ion-item>
</ion-list>
<div class="list"></div>
<div align="center">
<button class="button button-small button-positive" ng-disabled="currentPage == 0" ng-click="decrementLimit()">
Back
</button>
<button class="button button-small button-energized">
{{currentPage+1}}/{{numberOfPages()}}
</button>
<button class="button button-small button-positive" ng-disabled="currentPage >= data_length/pageSize - 1" ng-click="incrementLimit()">
Next
</button>
</div>
</ion-content>
</ion-view>
controller.js:
var myApp = angular.module('musicApp.controllers', []);
myApp.controller('AppCtrl', function($scope, $http) {
console.log('AppCtrl called');
// $scope.tracks = MusicService.query();
$http.get('api/to/be/called').then(function(result){
$scope.tracks = result.data['results'];
$scope.data_length = $scope.tracks.length;
console.log(result)
// console.log($sco pe.tracks.length)
});
var currentPage = 0;
$scope.pageSize = 10;
$scope.numberOfPages = function(){
return Math.ceil($scope.tracks.length/$scope.pageSize);
}
var limitStep = 10;
$scope.limit = limitStep;
$scope.currentPage = currentPage;
$scope.incrementLimit = function(){
$scope.limit += limitStep;
$scope.currentPage += 1
};
$scope.decrementLimit = function(){
$scope.limit -= limitStep;
$scope.currentPage -= 1
}
});

Resources