Advanced filtering with AngularJS - angularjs

I'm looking to filter the following list with AngularJS by both category and location. The difficulty I'm having is being able to properly nest the results beneath their respective categories (see below) after the filtering has occurred.
var jobs = [
{
title: "Software Engineer",
category: "Engineering",
location: "New York"
},
{
title: "Web Developer",
category: "Engineering",
location: "Chicago"
},
{
title: "UX Designer",
category: "Design",
location: "New York"
}
]
This is the desired output (also note the alphabetically ordered categories):
Design
UX Designer
Engineering
Software Engineer
Web Developer
Any help would be much appreciated. Thanks!

Here is a working example with your data on plnkr using the filter solution from AngularJS Group By Directive without External Dependencies:
http://plnkr.co/edit/w5UJUN?p=preview

Do you have something like this in your mind:
<b>Design</b>
<ul>
<li ng-repeat="job in jobs | filter: { category:'Design'}">
{{job.title}}
</li>
</ul>
<b>Engineering</b>
<ul>
<li ng-repeat="job in jobs | filter: { category:'Engineering'}">
{{job.title}}
</li>
</ul>
There is working JSFiddle.

I would re-structure my JSON as follows:
$scope.parsedJobs = [];
angular.forEach($scope.jobs, function(value, key) {
var j = $scope.parsedJobs.filter(function(element) {
return element.category == value.category;
});
if (j.length > 0) {
$scope.parsedJobs[0].elements.push({
title: value.title,
location: value.location
});
} else {
$scope.parsedJobs.push({
category: value.category,
elements: [{
title: value.title,
location: value.location
}]
});
}
});
Fiddle

Related

Vue router-link param undefined in nested loop

I have a loop inside another loop.
The videos.id is undefined in the <router-link> but is rendered just fine otherwise..
<ul>
<li v-for="category in categories" :key="category.id">
{{category.name}}
<div v-for="videos in category.videos" :key="videos.id">
<router-link v-bind:to="/video-player/ + videos.id"> {{videos.id}} {{videos.name}}</router-link>
</div>
</li>
</ul>
Based on your codepen: https://codepen.io/cwerner/pen/mdyjLBv
It is showing videos.id as undefined because this data doesn't exist:
{
id: 1,
name: "Category",
videos: [
{
name: "Video1"
},
{
name: "Video 2",
}
],
}
There is only an id for category, so you must add id's in the video objects too:
{
id: 1,
name: "Category",
videos: [
{
name: "Video1",
id: 1
},
{
name: "Video 2",
id: 2
}
],
}
Content of binded property has to be valid JS expression. /video-player/ + videos.id is not valid JS expression. Change it to v-bind:to="'/video-player/' + videos.id"

multiple filter in angularjs

I am new to angular. I have a json object:
[
{
"id": 1,
"name": "name1",
"speciality": "speciality1",
"address": "address1",
"phoneNumber": 9999999999
},
{
"id": 2,
"name": "name2",
"speciality": "speciality2",
"address": "address2",
"phoneNumber": 9999999999
},
...
]
and I want to filter based on two properties: name and speciality. This search is matching insensitive substring. How to do this? The way I am doing this is as follows, which doesn't work:
$filter('filter')(doctor.details, { $: query }, false, [name, speciality]);
$filter('filter')(doctor.details, { name: query } || {speciality : query }, false);
doctor.details is the json object array, query is the (sub)string to be matched with name or speciality.
You can write a AngularJS filter to filter out the list based on query matching name and speciality properties.
app.filter('filterDetails', function() {
return function(details, query) {
var filteredDetails = [];
filteredDetails = details.filter(function(obj) {
// look whether query is substring of name or speciality
if (~obj.name.indexOf(query) || ~obj.speciality.indexOf(query)) {
return obj;
}
})
// finally return filtered array
return filteredDetails;
};
});
HTML
Query :
<input type="text" ng-model="query" />
<ul>
<li ng-repeat="detail in doctor.details | filterDetails:query">
{{ detail }}
</li>
</ul>
Plunker
Did it using lodash as this was the easiest I could think of:
_.union($filter('filter')(doctor.details, { name: query }), $filter('filter')(doctor.details, { speciality: query }));

Sort subarrays in array with AngularJS

If I have a dataset that contains an array of objects, with each object having an array inside of them, how can I sort the subarrays by their properties? For example, here is some sample music data, it has 2 albums with tracks within them:
albums: [
{
type: "album",
name: "Brothers",
tracks: [
{
type: "track",
name: "Everlasting Light"
},
{
type: "track",
name: "Next Girl"
},
{
type: "track",
name: "Tighten Up"
}
]
},
{
type: "album",
name: "Listen",
tracks: [
{
type: "track",
name: "Around Town"
},
{
type: "track",
name: "Forgive & Forget"
}
]
}
]
The result would look like this:
- Around Town
- Everlasting Light
- Forgive & Forget
- Next Girl
- Tighten Up
Is there any way I can use an ng-repeat to create an alphabetically sorted list of music tracks?
I'd imagine it working something like below, but I tried with no success.
<p ng-repeat="track in albums.tracks">{{track.name}}</p>
Since you need only the tracks of albums, you should merge all the tracks in a single array and then just sort it alphabetically. Here's is a snippet working:
var app = angular.module('app', []);
app.controller('mainCtrl', function($scope) {
$scope.albums = [
{
"type":"album",
"name":"Brothers",
"tracks":[
{
"type":"track",
"name":"Everlasting Light"
},
{
"type":"track",
"name":"Next Girl"
},
{
"type":"track",
"name":"Tighten Up"
}
]
},
{
"type":"album",
"name":"Listen",
"tracks":[
{
"type":"track",
"name":"Around Town"
},
{
"type":"track",
"name":"Forgive & Forget"
}
]
}
];
$scope.tracks = [];
angular.forEach($scope.albums, function(value) {
$scope.tracks = $scope.tracks.concat(value.tracks);
});
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js">
</script>
</head>
<body ng-controller="mainCtrl">
<div ng-repeat="track in tracks | orderBy:'name'">
<span ng-bind="track.name"></span>
</div>
</body>
</html>
You can use the orderby filter to sort your list based on a property. The angular website has more info on this https://docs.angularjs.org/api/ng/filter/orderBy
Here is a plunker that might help, https://plnkr.co/edit/goxNA9PvBeFL0hyWp9hR?p=preview
You can use the filter directly in your html to sort by a property.
<div ng-repeat="album in albums">
<h2>{{album.name}}</h2>
<div ng-repeat="track in album.tracks | orderBy:'name'">
{{track.name}}
</div>
</div>

Angularjs and Jade ng-repeat nested issue

I am beginning to play around with Jade and I am having this weird issue. I'm sure it's something stupid, but I've been trying for one hour without success.
I have an object that contains groups, each group contains items. So, there is one ng-repeat nested inside the other.
ul.page-sidebar-menu(ng-repeat="group in menuItems")
li(ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
ul.sub-menu(ng-repeat="item in group.items")
li
a(href="{{item.target}}") {{item.name}}
The object source is like this:
[
{
name: "Inicio",
target: "/",
groupStyle: {
start: "start",
active: "active"
},
spanStyle: {
selected: "selected"
},
iconStyle: "icon-home"
},
{
name: "Catalogo",
target: "javascript:;",
groupStyle: { },
spanStyle: {
arrow: "arrow"
},
iconStyle: "icon-book",
items: [
{ name: "Clientes", target: "view1" },
{ name: "Rutas", target: "view1" },
{ name: "Transportistas", target: "view1" }
]
},
{
name: "Panel de Control",
target: "javascript:;",
groupStyle: { },
spanStyle: {
arrow: "arrow"
},
iconStyle: "icon-cogs",
items: [
{ name: "Usuarios", target: "view2" },
{ name: "Configuracion", target: "view2" }
]
}
]
So, theoretically each group has some number of items and nesting should be possible. Here comes the funny part: when Jade renders the HTML based on a list it only renders the first child of every group. This is the output:
But when I added a table before the list with another ng-repeat it works fine. The code:
ul.page-sidebar-menu(ng-repeat="group in menuItems")
li(ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
table
tr(ng-repeat="item in group.items")
td {{item.name}}
ul.sub-menu(ng-repeat="item in group.items")
li
a(href="{{item.target}}") {{item.name}}
And the output:
So, please someone with more coffee in their body or more skills in Jade give me a hand. I'm sure it must be something obvious.
Thanks in advance.
As I guessed it was a stupid issue. The ng-repeat should be at the items level, not at the list level. Here is the code fixed.
ul.page-sidebar-menu
li(ng-repeat="group in menuItems", ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
ul.sub-menu
li(ng-repeat="item in group.items")
a(href="{{item.target}}") {{item.name}}

Create an unordered list n-levels deep using AngularJS

While going through some AngularJS examples, I see how easy it is to repeat and create structures. However, I couldn't figure out how to do the following.
Assume we have a json structure like
{
"Asia": {
"India": {
"Bangalore": {},
"Mumbai": {},
"New Delhi": {}
},
"China": {
"Beijing": {},
"Shanghai": {}
}
},
"Europe": {
"France": {
"Paris": {}
},
"Germany": {
"Berlin": {}
}
}
}
What I want to do is - Convert this JSON structure to an Unordered list - The depth of this kind of structure is not known, and can possibly go deeper. How do I perform repeats dynamically using Angular JS?
Your JSON is poorly structured, you're using property names to carry data.
What you really want is something like this:
$scope.continents = [
{
name: 'Asia',
countries: [
{
name: 'India',
cities: [
'Bangalore',
'Mumbai',
'New Delhi'
]
},
{
name: 'China',
cities: [
'Beijing',
'Shanghai'
]
},
]
},
{
name: 'Europe',
countries: [
{
name: 'France',
cities: [
'Peris'
]
},
{
name: 'Germany',
cities: [
'Berlin'
]
},
]
}
];
That said... what it sounds like you're looking to do is create a recursive tree directive of some sort. That gets a little tricky. You'll need to normalize your structure a bit so you can recursively examine it. Then you'll have to create two directives. One for a list, and one for an item:
Here is an example of what I mean...
function Item(name, items) {
this.name = name;
this.items = items || [];
}
app.controller('MainCtrl', function($scope) {
$scope.items = [
new Item('test'),
new Item('foo', [
new Item('foo-1'),
new Item('foo-2', [
new Item('foo-2-1'),
new Item('foo-2-2')
])
]),
new Item('whatever')
];
});
app.directive('tree', function() {
return {
template: '<ul><tree-node ng-repeat="item in items"></tree-node></ul>',
restrict: 'E',
replace: true,
scope: {
items: '=items'
}
};
});
app.directive('treeNode', function($compile) {
return {
restrict: 'E',
template: '<li>{{item.name}}</li>',
link: function(scope, elm, attrs) {
//MAGIC HERE!!!: this will do the work of inserting the next set of nodes.
if (scope.item.items.length > 0) {
var children = $compile('<tree items="item.items"></tree>')(scope);
elm.append(children);
}
}
};
});
In case anyone is interested in the "least-effort" way to do this without creating a directive (not that you shouldn't, but just offering a variation), here is a simple example:
http://jsbin.com/hokupe/1/edit
Also here's a blog post and a 10-15 minutes video on how it works:
http://gurustop.net/blog/2014/07/15/angularjs-using-templates-ng-include-create-infinite-tree/
Sample Code:
<script type="text/ng-template" id="treeLevel.html">
<ul>
<li ng-repeat="item in items">
<input type="checkbox"
name="itemSelection"
ng-model="item._Selected" />
{{item.text}}
<div ng-include=" 'treeLevel.html'"
onload="items = item.children">
</div>
</li>
</ul>
</script>
<div ng-include=" 'treeLevel.html' "
onload="items = sourceItems">
</div>

Resources