I've been working on sorting a list of items alphanumerically. I have found that using ng-repeat with orderBy tends to sort either numerically or alphabetically depending on the type of data it is working with. It does not however sort alphanumerically.
JavaScript Code:
function AppCtrl($scope) {
$scope.items = [
{'name':'School Item 1','address':'1920'},
{'name':'Work Item 2','address':'4192'},
{'name':'Ad Item 5','address':'2039'},
{'name':'Cool Item 45','address':'2090'},
{'name':'Cool Item 50','address':'1029'},
{'name':'Cool Item 100','address':'1829'},
{'name':'Cool Item 400','address':'1728'}
];
}
HTML Code:
<ul ng-controller="AppCtrl">
<li ng-repeat="item in items|orderBy:['name','address']">
{{item.name}} {{item.address}}
</li>
</ul>
Here is the fiddle http://jsfiddle.net/roverton/PuYLS/1/
Notice that when ordered the it will show 1 then 100, 45 then 400, 5 then 50 etc. I would like to order these alphanumerically. How would I do that in AngularJS?
One way to do this is to use a function to extract the number.
function nameNumberSorter(item) {
var numberPart = item.name.replace('NamedItem', '');
return parseInt(numberPart);
}
And then alter your filter a bit:
<div ng-app>
<ul ng-controller="AppCtrl">
<li ng-repeat="item in items|filter:deviceBindingFilter|orderBy:nameNumberSorter">
{{item.name}} - {{item.address}}
</li>
</ul>
</div>
Alternatively, you could make an extra field on your model for sorting.
function AppCtrl($scope) {
$scope.items = [
{'name':'NamedItem1','address':'1920', 'sortOrder': 1 },
{'name':'NamedItem2','address':'4192', 'sortOrder': 2 },
{'name':'NamedItem5','address':'2039', 'sortOrder': 5 },
{'name':'NamedItem45','address':'2090', 'sortOrder': 45 },
{'name':'NamedItem50','address':'1029', 'sortOrder': 50 },
{'name':'NamedItem100','address':'1829', 'sortOrder': 100 },
{'name':'NamedItem400','address':'1728', 'sortOrder': 400 }
];
}
And change your sort to look at this field.
<div ng-app>
<ul ng-controller="AppCtrl">
<li ng-repeat="item in items|filter:deviceBindingFilter|orderBy:'sortOrder'">
{{item.name}} - {{item.address}}
</li>
</ul>
</div>
Related
<div>
<h1>Birds</h1>
<ul>
<li ng-if="bird.type === 'bird'"
ng-repeat="bird in creatures">{{bird.name}}</li>
</ul>
</div>
I have a response from the server and I want to put it into the list. But when the list is empty I need to hide this div container. For example if bird.type === 'bird' is not in the array - I want to hide div. But I want to use bird after ng-repeat so I cant make ng-if="bird.type === 'bird'" on div. Can I check if li is empty, then hide the div?
plunkr example
AngularJS ng-repeat handle empty list case - It's not the same. I don't want to hide li if it empty, I want to hide parent where I have h1 - when li is empty.
You could do the following:
<div ng-if="hasBirds(creatures)">
<h1>Birds</h1>
<ul>
<li ng-if="bird.type === 'bird'"
ng-repeat="bird in creatures">{{bird.name}}</li>
</ul>
</div>
And then in controller/directive you can add the hasBirds function.
$scope.hasBirds = function(list){
return list.filter(function(item){return item.type === 'bird'}).length > 0;
}
This hasBirds function would get called often, but would allow you to hide/show the heading of the birds exist.
In your example I advise you to use a filter instead of using "ng-if", you should create a filter like:
angular.module('moduleName').filter(birdsFilter);
function birdsFilter(creature) {
return creature.type == 'bird';
}
With the filter you can rewrite your code like this:
<div ng-hide="birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in birds = (creatures | filter:birdsFilter)">{{bird.name}}</li>
</ul>
</div>
IMO, several of these answers will work. But none of them are ideally optimized. I would recommend filtering your data in your controller/postlink function.
$scope.animals = {
dogs: $scope.creates.filter(function(a){return a.type == 'dog'}),
cats: $scope.creates.filter(function(a){return a.type == 'cat'}),
birds: $scope.creates.filter(function(a){return a.type == 'bird'}),
fishes: $scope.creates.filter(function(a){return a.type == 'fish'})
};
This way you would only ever process the array of creatures one time, in one spot. The digest cycle would never have to re-eval the array to see if it needed to update the DOM. Here is what you markup with look like then:
<div ng-if="animals.birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in animals.birds">{{bird.name}}</li>
</ul>
</div>
You should filter the list based on the type, store the filtered items in a scope property then use that property to show or hide the div.
<div ng-show="birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in creatures | filter:birdType as birds">{{bird.name}} </li>
</ul>
</div>
Then implement the birdType filter function in your controller:
$scope.birdType = function(creature) {
return creature.type === 'bird';
};
Using ng-show="cats.length" to make div's disappear if length is zero.
Filter inline based on object property like cat in creatures | filter:{type: 'cat'} as cats as discussed in this SO post.
WORKING EXAMPLE:
var app = angular.module('App', []);
app.filter(birdsFilter);
function birdsFilter(creature) {
return creature.type == 'bird';
}
app.controller('Ctrl', function($scope) {
$scope.creatures = [
{
name : 'Cho-cho',
type : 'bird'
},
{
name : 'Floo-floo',
type : 'dog'
},
{
name : 'Pou-pou',
type : 'bird'
},
{
name : 'Oop-flup',
type : 'bird'
},
{
name : 'Chio-mio',
type : 'cat'
},
{
name : 'Floo-floo',
type : 'dog'
},
{
name : 'Loo-Li',
type : 'dog'
},
{
name : 'Pops-Mops',
type : 'bird'
},
{
name : 'Boo-Moo',
type : 'dog'
},
{
name : 'Iop-Pio',
type : 'dog'
},
{
name : 'Floop-cho',
type : 'bird'
}
]
});
<!DOCTYPE html>
<html ng-app="App">
<head>
<script data-require="angularjs#1.5.7" data-semver="1.5.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="Ctrl">
<div ng-show="birds.length">
<h1>Birds</h1>
<ul>
<li ng-repeat="bird in creatures | filter:{type: 'bird'} as birds">{{bird.name}} </li>
</ul>
</div>
<div ng-show="dogs.length">
<h1>Dogs</h1>
<ul>
<li ng-repeat="dog in creatures | filter:{type: 'dog'} as dogs">{{dog.name}} </li>
</ul>
</div>
<div ng-show="cats.length">
<h1>Cats</h1>
<ul>
<li ng-repeat="cat in creatures | filter:{type: 'cat'} as cats">{{cat.name}} </li>
</ul>
</div>
<div ng-show="fishes.length">
<h1>Fish</h1>
<ul>
<li ng-repeat="fish in creatures | filter:{type: 'fish'} as fishes">{{fish.name}} </li>
</ul>
</div>
</body>
</html>
I have a situation that is pretty similar to this answer to the question here:
AngularJS ng-include with nested hierarchy
I have some data in the format
$scope.data = {
text: "blah",
comments:
[
{
text: ["blahL11", "blahL12", "blahL13"],
comments: [
{
text: ["blahL111", "blahL112", "blahL113"]
},
{
text: ["blahR111", "blahR112", "blahR113"]
}
]
},
{
text: ["blahR11", "blahR12", "blahR13"]
}
]
};
And I am display it with a recursive ng-include like this:
<ul>
<li>{{data.text}}</li>
<li ng-repeat="item in data.comments" ng-include="'tree'"></li>
</ul>
<script type="text/ng-template" id="tree">
<ul>
<li ng-repeat="text in item.text">{{text}}</li>
<li ng-repeat="item in item.comments" ng-include="'tree'"></li>
</ul>
</script>
http://plnkr.co/edit/8swLos2V6QRz6ct6GDGb?p=info
However, I would like to somehow keep track of the depth of the recursion as well. So that instead of simply displaying:
-blah
-blahL11
-blahL12
-blahL13
-blahL111
It could display
-1. blah
-2. blahL11
-2. blahL12
-2. blahL13
-3. blahL111
Without me modifying the data structure (as the depth is only really for display?). Can I insert a variable into the ng-include, is there some sort of recursive $index I can use?
You can do this using ng-init. This will assign a value to the new scope when it's created, which you can do by refering to a value in the old scope and incrementing it.
plnkr demo
<li ng-repeat="item in item.comments" ng-include="'tree'" ng-init="depth = depth + 1"></li>
i am using mvc. I have model and i take data from model to view with this code:
<ul>
<li id="geri"><<</li>
#foreach (var item in Model.Skills)
{
<li id="#String.Format("{0}{1}", "skill", item.SkillId)">
#item.SkillName
</li>
}
<li id="ileri" style="margin-right: 0;">>></li>
</ul>
After first 4 items, they should be hidden (display:none). I searched angular and find ng-show attribute but cannot find how to use. Now my website looks like:
It should be one line and when i pressed next button, first item will hide and 5th item will show.
I hope i can explain myself, thanks
In Angular, try to use limitTo and offset filters.
Here's the Jsfiddle link.
AngularJS sample codes:
HTML:
<div ng-app="myApp">
<ul ng-controller="YourCtrl">
<li ng-click="previousSkills()"><<</li>
<li ng-repeat="skill in skills | offset: currentPage * 4 | limitTo: 4">
{{skill.SkillName}}
</li>
<li ng-click="nextSkills()">>></li>
</ul>
</div>
AngularJS Controller:
'use strict';
var app = angular.module('myApp', []);
app.controller('YourCtrl', ['$scope', function ($scope) {
$scope.currentPage = 0;
$scope.skills = [
{SkillName:'C#'},
{SkillName:'MVC'},
{SkillName:'Web Forms'},
{SkillName:'Web API'},
{SkillName:'SignalR'},
{SkillName:'EF'},
{SkillName:'Linq'},
{SkillName:'Github'},
{SkillName:'Html'},
{SkillName:'CSS'},
{SkillName:'SQL'},
{SkillName:'Angular'},
{SkillName:'Azure'}
];
$scope.previousSkills = function() {
$scope.currentPage = $scope.currentPage - 1;
};
$scope.nextSkills = function() {
$scope.currentPage = $scope.currentPage + 1;
};
}]);
app.filter('offset', function() {
return function(input, start) {
start = parseInt(start, 10);
return input.slice(start);
};
});
Hope it helps.
In Angular your HTML should be something like this to display only the first 4 items <li>, where items is your $scope.items:
<ul>
<li id="geri"><<</li>
<li ng-repeat="(key, item) in items" ng-show="key <= 3">{{item.SkillName}}</li>
<li id="ileri" style="margin-right: 0;">>></li>
</ul>
JSFiddle here
Say I have 4 item in ng-repeat. How can I exclude one item in orderBy?
<li ng-repeat="item in items | orderBy:'id':true">
$scope.items = [
{"name":"item1","id":1},
{"name":"item2","id":2},
{"name":"item3","id":3},
{"name":"item4","id":4}
];
How can I make, say, id:3 always appear as the first item?
plunker demo
You can create a function to alter the value you sort by (plunker):
$scope.itemSortValue = function(item) {
if (item.id == 3)
return -1;
return item.id;
}
Html:
<li ng-repeat="item in items | orderBy:itemSortValue">
{{item.name}}
</li>
Trying to sort based on price and rating (in the Object returned). I'd rather do an ng-click an an li than use a select menu. Is there a way this is possible? I've been looking all around and this is the closest I've come up with.
<ul class="restaurant-filter">
<li> <i class="icon-search"></i>Organize results by "Price" & "Rating"…
<ul ng-model="priceAndRating">
<li ng-click="price">Price</li>
<li ng-click="rating">Rating</li>
</ul>
</li>
</ul>
<ul class="available-restaurants">
<li ng-repeat="restaurant in availableRestaurants | orderBy:priceAndRating" ng-click="addRestaurantToEvent(restaurant.restaurantName)">
<h6>{{restaurant.restaurantName}}</h6>
<p>label one two three for five six</p>
<i class="icon-chevron-right"></i>
</li>
</ul>
availableRestaurants is my object.
You didn't give us the structure of your data, or your controller, so I just made a simple one that did what you wanted. Here is the fiddle.
Note: ng-click needs to call a function, or execute some code. You could write a function literal to set the property, but this is mixing controller logic into the view (think about what would happen if the sort needed to change later, you would need to update that logic in the view, which is bad).
I added a sort function to handle this, that just takes the property. You can set this up however you need to.
HTML:
<div ng-app="miniapp">
<div ng-controller="Ctrl1">
<ul class="restaurant-filter">
<li> <i class="icon-search"></i>Organize results by "Price" & "Rating"…
<ul>
<li ng-repeat="option in sortOptions" ng-click="setSort (option)">{{option}}</li>
</ul>
</li>
</ul>
<ul class="available-restaurants">
<li ng-repeat="r in availableRestaurants | orderBy:sort">
<span>{{r.name}} - {{r.rating}}</span>
</li>
</ul>
</div>
</div>
JS:
function Ctrl1($scope) {
$scope.sortOptions = ["Price", "Rating"];
$scope.sort = "Price";
$scope.setSort = function(type) { $scope.sort = type.toLowerCase(); };
$scope.availableRestaurants = [{name: "Chevy's", rating: 6, price: 6},
{name: "Burger King", rating: 2, price: 1},
{name: "Red Robin", rating: 4, price: 4},
{name: "Carl's Jr", rating: 5, price: 2}];
}
I've copied and pasted your code out into a fiddle:
http://jsfiddle.net/blesh/tSHUf/
ng-click needs to do something, it doesn't have to call a function as Tyrsius asserted, but it should execute some code. As you'll see in my fiddle I'm setting a property on the scope named order = 'price' or 'rating' depending on what you click.
Then you orderBy:order which will $eval whatever is in $scope.order, which was set by your click.
I hope that helps.