The goal is to create this
<h3>11.4.2013</h3>
<ul>
<li>entry 1</li>
<li>entry 2</li>
<li>entry 3</li>
</ul>
<h3>10.4.2013</h3>
<ul>
<li>entry 4</li>
<li>entry 5</li>
<li>entry 6</li>
</ul>
from this
[
{
"name": "entry1",
"date": "11.4.2013"
},
{
"name": "entry2",
"date": "11.4.2013"
},
{
"name": "entry3",
"date": "11.4.2013"
},
{
"name": "entry4",
"date": "10.4.2013"
},
{
"name": "entry5",
"date": "10.4.2013"
},
{
"name": "entry6",
"date": "10.4.2013"
}
]
The problem is that ng-repeat would have to be on li so I wouldn't never be able to do this using ng-repeat, is that right? I found this http://jsfiddle.net/mrajcok/CvKNc/ example from Mark Rajnoc, but it's still pretty limiting..
What other choices do I have? Write my own ng-repeat like directive? Or is there another way to do it without writting one?
You could write your own filter that filters out the unique dates for an outer ng-repeat, something like:
filter('unique',function(){
return function(items,field){
var ret = [], found={};
for(var i in items){
var item = items[i][field];
if(!found[item]){
found[item]=true;
ret.push(items[i]);
}
}
return ret;
}
});
with the following markup:
<div ng-repeat="dateItem in items | unique:'date' | orderBy:'date'">
<h3>{{dateItem.date}}</h3>
<ul>
<li ng-repeat="item in items | filter:dateItem.date">
{{item.name}}
</li>
</ul>
</div>
Have a look at this working plnkr example -- or this updated example with adding items
However, if your going to have a lot of items (hundreds or thousands) this solution is not the most optimal. An alternative approach would be to create a more optimal data structure. You can even get this to work with your original data structure by adding a $watch - something like:
$scope.$watch('items',function(){
var itemDateMap = {};
for(var i=0; i<$scope.items.length; i++){
var item = $scope.items[i], key = item.date;
if(!itemDateMap[key]){
itemDateMap[key] = [];
}
itemDateMap[key].push(item);
}
$scope.itemDateMap=itemDateMap;
},true);
Works with this markup:
<div ng-repeat="(date,subItems) in itemDateMap">
<h3>{{date}}</h3>
<ul>
<li ng-repeat="item in subItems">
{{item.name}}
</li>
</ul>
</div>
Here is an example where you can add lots of random items.
When I have same needs like yours, I used Object instead of Array.
<div ng-repeat="item in data">
<h1>{{item.date}}</h1>
<ul ng-repeat="name in item.names">
<li>{{name}}</li>
</ul>
</div>
http://jsfiddle.net/shoma/DqDsE/1/
This question page would help you.
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
Related
I want to list an item inside a specific key using a filter. Now listing like this.
Sanal
Jin
John
Tim
Jeff
Sam
John
Tim
I want like this using a filterJeffSam
Here is the Fiddle
function testCtrl($scope) {
$scope.items = {"id":"B716","day":8,"di":{"type":"normal","one":[{"name":"Sanal","age":"18"},{"name":"Jin","age":"25"}],"two":[{"name":"Jeff","age":"55"},{"name":"Sam","age":"32"}],"three":[{"name":"John","age":"34"},{"name":"Tim","age":"39"}]}};
}
<div ng-app="" ng-controller="testCtrl">
<ul>
<li ng-repeat="val in items.di">
<ul>
<li ng-repeat="value in val">{{value.name}}</li>
</ul>
</li>
</ul>
</div>
in your ng repeat change it as
<li ng-repeat="value in val | filter: val.name='jeff'">{{value.name}}</li>
I have used name you can use the value that you want ot test
Get an object from server. look like this:
"courses": [
{
"id": 1,
"name": "piano",
"classes": [
{
"id": 1,
"name": "piano1",
},
{
"id": 2,
"name": "piano2",
}
],
"classes_count": 2,
"feedbacks_count": 4
},
]
JS: there I get data to Scope
httpService.getService(url, data).then(function(res) {
$scope.datas = res.body.courses;
console.log($scope.datas);
})
I need to print classes id in ng-repeat. try to do this
<ul class="dropdown-list" style="display: none;">
<li ng-repeat="course in datas | filter:{'id': showprofile}:true ">{{course.classes.id}}</li>
</ul>
But it's work when I print {{course.classes}} and I get an array with 2 objects. How to show in "li" element course.classes.id ?
You have array in array, so you have got 2 ng-repeats: first iterating over courses and second over classes.
<ul ng-repeat="course in datas" class="dropdown-list" style="display: none;">
<li ng-repeat="class in course.classes">{{class.id}}</li>
</ul>
You need to add another loop for nested elements
<ul class="dropdown-list" style="display: none;">
<li ng-repeat="course in datas" ng-if='$first'> {{course.id}}</li>//
<ul ng-if="course.classes.length">
<li ng-repeat="classes in course"> {{course.classes.id}}</li>
</ul>
</ul>
I have an array:
var myArray = [1, 2, 4, 5]
and i have an html list
<ul>
<li> list 1 </li>
<li> list 2 </li>
<li> list 3 </li>
<li> list 4 </li>
<li> list 5 </li>
<li> list 6 </li>
</ul>
I am trying to hide all the nth-child that do not match a number in my array.
Kind of like this:
if li:nth-child("number not in myArray").remove();
i can't seem to figure out who to loop properly, any help will be greatly appreciated.
thanks
You need to iterate lis using foreach and check number of every li in it. If number of li doesn't exist in array, remove li.
var myArray = [1, 2, 4, 5];
$("li").each(function(index){
var liNum = $(this).text().match(/[\d]+/g)[0];
if (myArray.indexOf(parseInt(liNum)) == -1)
$(this).remove();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li> list 1 </li>
<li> list 2 </li>
<li> list 3 </li>
<li> list 4 </li>
<li> list 5 </li>
<li> list 6 </li>
</ul>
You can use jQuery.each and Array.prototype.indexOf methods to accomplish the task:
var array = [1, 3, 5, 7];
setTimeout(function() {
var $list = $('.listToFilter li');
$list.each(function(index, element) {
if (array.indexOf(index) >= 0) {
element.remove();
}
});
}, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>wait 1 second to see the items being removed:</p>
<ul class="listToFilter">
<li>Item 0</li>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
</ul>
You don't have to use setTimeout, I've used it just to make example a little more verbose.
You can use the filter and indexOf methods. :D
$("ul li").filter(function(){
if([1, 2, 4, 5].indexOf($(this).index()+1)==-1)
{return true;}
}).remove();
Maybe i should have added that i am getting the array dynamically based on a user input and the answers here so far gives me this error:
Uncaught TypeError: Cannot read property '0' of null
I solved my issue like this it may be a bit verbose but it does work so far:
$(#myimput).each( function () {
var myField = $(this).text();
var myFieldArray = ("" + myField).split("");
var listItem = ":nth-child(";
var listItemAssambled = "";
var x;
for (x in myFieldArray ) {
listItemAssambled += listItem + myFieldArray[x] + "), ";
}
listItemAssambled = listItemAssambled.substring(0, listItemAssambled.lastIndexOf(','));
if ($(this).text() >= 1) {
$(#mylist li).not(listItemAssambled).remove();
}
});
Problem:
I have an unordered list of items which are returned from a json call and are output using ng-repeat. Each one of these items has a class name (there are about 9 categories).
I have a second unordered list which is simply a list of available categories.
Aim:
I want to be able to select one of the categories in the right hand list, which will apply a filter to the actual list of returned elements. This should be activated via a toggle (so click once: filtered, click again: filter removed). So it is simply looking to match the classname in the clicked element, to the elements that share the same classname in the list of json data.
I cannot use ng-model (as this is reserved for certain form elements).
For my jsfiddle I am simply using static html.
Here is my angular code:
/* angular custom filter on returned ajax api data */
var app = angular.module('app', []);
app.controller('main', function($scope) {
$scope.chFilters = {};
$scope.links = [
{name: 'atm'},
{name: 'internet'},
{name: 'mobile'},
{name: 'sms'},
{name: 'postal'}
];
$scope.channels = ["ATM", "INTERNET", "SMS", "POSTAL","MOBILE"];
});
(this is based on another question I found on SO). Unfortunately the fiddle is a bit messy and has some extraneous code in it.
HTML:
<div ng-app="app">
<div ng-controller="main">
<ul>
<li class="atm">Some stuff ATM</li>
<li class="internet">Some stuff INTERNET</li>
<li class="sms">Some stuff ATM</li>
<li class="atm">Some stuff ATM</li>
<li class="postal">Some stuff POSTAL</li>
<li class="atm">Some stuff ATM</li>
<li class="internet">Some stuff INTERNET</li>
<li class="postal">Some stuff POSTAL</li>
<li class="postal">Some stuff POSTAL</li>
<li class="atm">Some stuff ATM</li>
<li class="sms">Some stuff SMS</li>
<li class="mobile">Some stuff MOBILE</li>
<li class="internet">Some stuff INTERNET</li>
<li class="mobile">Some stuff MOBILE</li>
</ul>
<ul class="channel-filters">
<li ng-repeat="link in links | filter:chFilters" class="{{link.name | lowercase}}"><a ng-click="chFilters.name = link.name">{{link.name | uppercase}}</a></li>
<li class="last" ng-click="chFilters.name = ''">CLEAR FILTER</li>
</ul>
<ul>
<li ng-repeat="channel in channels | filter:chFilters">
<strong>{{channel}}</strong>
<a ng-click="chFilters = channel">{{channel}}</a>
</li>
</ul>
<!-- original -->
<ul>
<li ng-repeat="link in links | filter:chFilters">
<strong>{{link.name}}</strong>
<a ng-click="chFilters.name = link.name">{{link.name}}</a>
</li>
</ul>
</div>
</div>
This is the actual HTML from the application (with the call to the api).
<ul class="accordion">
<li class="search-text-channel">
<input type="textarea" ng-model="searchTextChannel.$" placeholder="Search"/>
</li>
<li ng-repeat="day in interactions.model.byDay | filter:searchTextChannel" ng-click="hidden = !hidden" ng-init="hidden = false" class="{{day.channel | removeSpace | lowercase}}" ng-class="{'closed': !hidden, 'open': hidden}">
<span class="date">{{day.date}}</span>
<span class="subheading">{{day.channel}}</span>
<ul ng-show="hidden">
<li ng-repeat="interaction in day.interactions">
{{interaction.time}} {{interaction.description | removeUnderscore}}
</li>
</ul>
</li>
<li class="load-more">
<i class="fa fa-plus"></i>LOAD MORE
</li>
</ul>
I have managed to recreate this functionality in jquery, but I think it would be better to implement an angular solution in an angular application.
I've tried researching and also attempted to implement show/hide as well as a custom filter, but so far no joy.
Here is my (messy) jsfiddle
<ul>
<li ng-repeat="channel in channels | filter:chFilters.name">
<strong>{{channel}}</strong>
<a ng-click="chFilters = channel">{{channel}}</a>
</li>
</ul>
<!-- original -->
<ul>
<li ng-repeat="link in links | filter:chFilters.name">
<strong>{{link.name}}</strong>
<a ng-click="chFilters.name = link.name">{{link.name}}</a>
</li>
</ul>
Update Plunker
Let me know if you have any question on this.
Here is part of the solution:
Suppose your items look like this:
$scope.items = [
{ class: "atm", label: "Some stuff ATM" },
{ class: "internet", label: "Some stuff INTERNET" },
{ class: "sms", label: "Some stuff SMS" },
{ class: "postal", label: "Some stuff POSTAL" },
...
];
To show a filtered list (only filtering by a single channel for now): create a separate list in the scope, with the filters applied:
$scope.click = function(name) {
$scope.chFilters.name = name;
$scope.filteredItems = $scope.items.filter(function(item) {
return item.class === $scope.chFilters.name;
});
};
Call this click handler from the bottom list:
...<a ng-click="click(link.name)">{{link.name | uppercase}}</a>....
And show filteredItems in the top list:
<ul>
<li ng-repeat="item in filteredItems" ng-class="item.class">{{item.label}}</li>
</ul>
So this is really just a starting point, it should be extended to handle multiple filters, etc...
I have a set of JSON data that I would like to display in a nested list:
The JSON comes in the following format:
["Item 1", "Item 2", "Item 3", ["Nested Item 1", "Nested Item 2"]]
The html should be:
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>
<ul>
<li>Nested Item 1</li>
<li>Nested Item 2</li>
</ul>
</li>
</ul>
I don't have control of the JSON, and it may be deeper than 2 levels.
Of course, I tried this:
<ul>
<li ng-repeat="item in data">{{item}}</li>
</ul>
but it doesn't work because for nested items, it simply displays the json.
How can I achieve nested lists in AngularJs?
Fiddle: http://jsfiddle.net/m7ax7tsa/
Have a look at this nice blog post that has a complete example at the end. In short you need to build nested directives, where inner directive will have recursive call to outer directive:
<body>
<div ng-controller="IndexCtrl">
<collection collection='tasks'></collection>
</div>
</body>
angular.module('APP', [])
.directive('collection', function () {
return {
...
template: "<ul><member ng-repeat='member in collection' member='member'></member></ul>"
}
})
.directive('member', function ($compile) {
return {
...
template: "<li>{{member.name}}</li>",
link: function (scope, element, attrs) {
if (angular.isArray(scope.member.children)) {
element.append("<collection collection='member.children'></collection>");
$compile(element.contents())(scope)
}
}
}
I found a solution thanks to zsong and Blackhole.
Resulting HTML:
<div ng-app="testApp">
<div ng-controller="TestCtrl">
<script type="text/ng-template" id="/nestedList.html">
<ul>
<li ng-repeat="item in data">
<div ng-switch="isString( item )">
<div ng-switch-when="true">{{item}}</div>
<div ng-switch-when="false">
<!-- Recursive template!! -->
<div ng-include="'/nestedList.html'" ng-init="data = item">
</div>
</div>
</div>
</li>
</ul>
</script>
<div ng-include="'/nestedList.html'"></div>
</div>
</div>
I used a recursive template which included itself. It borrows heavily on the answer of Unknown number of sublists with AngularJS. In addition, I had to add the following code to the controller:
$scope.isString = function (item) {
return typeof item === "string";
};