Skip duplicated items while ng-repeat - arrays

There is a nested array which consist of bundles and those bundles in turn have items. So, I want to skip identical item(s) of the next bundle based on previous bundle items while iterating. Below is data model and code snippets:
vm.data
[
{
id: '01',
name: 'Dummy1',
items: [{
id: 'itemOne',
name: 'ItemOne',
desc: 'ItemOne description'
}]
},
{
id: '02',
name: 'Dummy2',
items: [{
id: 'itemOne',
name: 'ItemOne',
desc: 'ItemOne description'
},
{
id: 'otherItem',
name: 'OtherItem',
desc: 'OtherItem description'
}]
},
...
]
Html:
<div ng-repeat="bundle in vm.data track by $index">
...
<ul>
<li ng-repeat="item in bundle.items" ng-if="vm.check(item, $parent.$index)">
<span ng-bind="item.name"></span>
...
</li>
</ul>
</div>
vm.check:
vm.check = function(item, bundleIdx) {
if (bundleIdx > 0) {
return _.some(vm.data[bundleIdx-1].items, function(obj) {
return obj.id !== item.id;
});
} else {
// first bundle, so show all items
return true;
}
};
Demo is here.
It works partially, i.e. second bundle correctly matches conditions, but third bundle not. So, what I'm missing? Any help would be appreciated!

I would keep the complex logic out of your template. Instead you should transform vm.data before you attempt to consume it.
var items = {};
vm.bundles = [];
vm.data.forEach(function(data) {
var bundle = {
id: data.id,
name: data.name,
items: []
};
data.items.forEach(function(item) {
if (!items[item.id]) {
bundle.items.push(item);
}
items[item.id] = true;
});
vm.bundles.push(bundle);
});
Then your template can simply consume the transformed data.
<div ng-repeat="bundle in vm.bundles track by $index">
...
<ul>
<li ng-repeat="item in bundle.items">
<span>{{item.name}}</span>
...
</li>
</ul>
</div>

Related

dynamic css active class on ul li reflect according to url changes using angualar js

dynamic css class active applied on li while clickin on li and each li contain link.but if we open a link then dynamic css class active not working properly.
so how can i develop that type of functionality like after book mark perticular li link then when we open that link its need to be selected.
html
<ul>
<li ng-repeat="item in items" ng-class="{'glyphicon glyphicon-menu-down' : item.selected, 'glyphicon glyphicon-menu-right' : !item.selected}" style="display: block;line-height:20px;">
<span ng-click="showChilds($index)">{{item.name}}</span>
<ul>
<li ng-click="showGlyph(subItem);" class="sub-item" id="sub-item-{{$parent.$index}}{{$index}}" ng-class="{'glyphicon glyphicon-envelope' : subItem.active}" ng-repeat="subItem in item.subItems" ng-show="item.selected">
<a ui-sref="{{subItem.url}}">
<span>{{subItem.name}}</span>
</li>
</ul>
</li>
</ul>
javacript controller
$scope.showGlyph = function(subItem) {
angular.forEach($scope.items, function (item) {
angular.forEach(item.subItems, function (sItem) {
sItem.active = false;
});
});
subItem.active = true;
};
$scope.showChilds = function(index){
$scope.items[index].selected= !$scope.items[index].selected;
collapseAnother(index);
};
var collapseAnother = function(index){
for(var i=0; i<$scope.items.length; i++){
if(i!=index){
$scope.items[i].selected = false;
}
}
};
$scope.items = [
{
name: "Item1",
subItems: [
{name: "SubItem1" ,url:'user.create'},
{name: "SubItem2" ,url:'user.edit'}
]
},
{
name: "Item2",
subItems: [
{name: "SubItem3",url:'user.new'},
{name: "SubItem4",url:'user.old'},
{name: "SubItem5" , url:'user.newnew'}
]
},
{
name: "Item3",
subItems: [
{name: "SubItem6" , url:'user.oldold'}
]
}
];

ng-repeat not copying correctly over

I have ng-repeat working within a different template, however, I can not get my album.html template to display content. Here is what I have.
app.js
...
.state('album', {
url: '/album',
controller: 'AlbumCtrl as album',
templateUrl: '/templates/album.html'
})
...
AlbumCtrl.js
(function() {
function AlbumCtrl() {
this.albumData = angular.copy(albumPicasso);
};
angular
.module('blocJams')
.controller('AlbumCtrl', AlbumCtrl);
})();
fixture.js (where my object I want to copy lives)
var albumPicasso = {
title: 'The Colors',
artist: 'Pablo Picasso',
label: 'Cubism',
year: '1881',
albumArtUrl: 'assets/images/album_covers/01.png',
songs: [
{ title: 'Blue', duration: 161.71, audioUrl: 'assets/music/bloc_jams_music/blue' },
{ title: 'Green', duration: 103.96, audioUrl: 'assets/music/bloc_jams_music/green' },
{ title: 'Red', duration: 268.45, audioUrl: 'assets/music/bloc_jams_music/red' },
{ title: 'Pink', duration: 153.14, audioUrl: 'assets/music/bloc_jams_music/pink' },
{ title: 'Magenta', duration: 374.22, audioUrl: 'assets/music/bloc_jams_music/magenta' }
]
};
albumt.html (here is my template with ng-repeat)
<main class="album-view container narrow">
<section class="clearfix">
<div class="column half">
<img src="/assets/images/album_covers/01.png" class="album-cover-art">
</div>
<div class="album-view-details column half">
<h2 class="album-view-title">The Colors</h2>
<h3 class="album-view-artist">Pablo Picasso</h3>
<h5 class="album-view-release-info">1909 Spanish Records</h5>
</div>
</section>
<table class="album-view-song-list">
<tr class="album-view-song-item" ng-repeat="album in album.albumData" ng-mouseover="hovered = true" ng-mouseleave="hovered = false">
<td class="song-item-number">
<span ng-show="!playing && !hovered"></span>
<a class="album-song-button" ng-show="!playing && hovered"><span class="ion-play"></span></a>
<a class="album-song-button" ng-show="playing"><span class="ion-paused"></span></a>
</td>
<td class="song-item-title">{{ album.songs.title }}</td>
<td class="song-item-duration">{{ album.songs.duration }}</td>
</tr>
</table>
</main>
<ng-include src="'/templates/player_bar.html'"></ng-include>
{{ album.songs.title }} and {{ album.songs.duration }} isn't displaying any content nor am I receiving any errors. I personally believe I am not copying my object through my controller correctly? Further, how can I see my input of said object through my controller to test whether my object albumPicasso was copied correctly?
For Reference
This controller (CollectionCtrl.js) is working correctly and mirrors what I want to do besides the for loop.
(function() {
function CollectionCtrl() {
this.albums = [];
for (var i=0; i < 12; i++) {
this.albums.push(angular.copy(albumPicasso));
}
}
angular
.module('blocJams')
.controller('CollectionCtrl', CollectionCtrl);
})();
album in album.albumData
May be you want to not override album variable name?
Controller alias is just variable in scope. You are overwriting it.
It looks like:
for (var i = 0; i < album.albumData.length; i++) {
album = album.albumData[i]; // <-- We brake all things here
}
Just rename album in ngRepeat to something like item:
ng-repeat="item in album.albumData"
Also, you want iterate a songs array, not an object:
ng-repeat="song in album.albumData.songs"
and
{{song.title}}
albumPicasso doesn't seem to be a global variable so it seems it's not accessible from AlbumCtrl.js. Try setting this.albumData to some dummy data.
I would personally create an angular service that provides the album data and inject it in the controller.
you can create a service for this
(function() {
var albumPicasso = {
title: 'The Colors',
artist: 'Pablo Picasso',
label: 'Cubism',
year: '1881',
albumArtUrl: 'assets/images/album_covers/01.png',
songs: [{
title: 'Blue',
duration: 161.71,
audioUrl: 'assets/music/bloc_jams_music/blue'
}, {
title: 'Green',
duration: 103.96,
audioUrl: 'assets/music/bloc_jams_music/green'
}, {
title: 'Red',
duration: 268.45,
audioUrl: 'assets/music/bloc_jams_music/red'
}, {
title: 'Pink',
duration: 153.14,
audioUrl: 'assets/music/bloc_jams_music/pink'
}, {
title: 'Magenta',
duration: 374.22,
audioUrl: 'assets/music/bloc_jams_music/magenta'
}]
};
function fixtureService() {
this.getAlbumData = function(){
return albumPicasso;
};
}
angular
.module('myApp')
.service('fixtureService', fixtureService);
})();
and this service can be injected into your controller
(function() {
function AlbumController(fixtureService) {
this.albumData = fixtureService.getAlbumData();
console.log(this.albumData);
}
angular
.module('myApp')
.controller('AlbumController',['fixtureService', AlbumController]);
})();

Repeating multidimensional array object in angular

I have an array that looks like this:
[Object{ 82893u82378237832={ id=8, no=1, type="event", name="Sample1"}}, Object{ 128129wq378237832={ id=9, no=1, type="event", name="Sample2"}} ]
Now ignoring the first part which is just a random token i want to get the array inside that. i.e. id,no,type,name and display them in an ng-repeat, how can i achieve this?
This is how i create the above array:
var obj = {};
obj[data.responseData] = {
id: 8,
no: 1,
type: 'event',
name: ''
}
$scope.items.push(obj);
Your example doesn't include an array.
Anyway here's a good example.
Use map to transform an array to another array and use ng-repeat in a similar way that I've done.
Html:
<div ng-app="app">
<div ng-controller="ctrl">
<div ng-repeat="item in myArr">
{{item}}
</div>
</div>
</div>
JS:
angular.module('app', []).
controller('ctrl', function ($scope) {
var arr = [{
myId: '82893u82378237832',
id: 8,
no: 1,
type: "event",
name: "Sample1"
}, {
myId: '128129wq378237832',
id: 9,
no: 1,
type: "event",
name: "Sample2"
}];
// mapped the new object without myId
$scope.myArr = arr.map(function (item) {
return {
id: item.id,
no: item.no,
type: item.type,
name: item.name
}
});
});
JSFIDDLE.

How to target ng-show on specific items in ng-repeat?

http://plnkr.co/edit/aiGCPJEwTj0RWnuoCjsW?p=preview
I want the remove button to only show the popover for that item.
How would you go about this?
HTML:
<li ng-repeat="acc in accounts">
<div class="well well-sm">
<div class="popover-remove" ng-show="popoverRemove">Click to remove item</div>
<h4>{{acc.label}}</h4>
<button class="btn btn-default"
ng-mouseover="showPopover()"
ng-mouseleave="hidePopover()">Remove</button>
</div>
</li>
Angular Controller:
var app = angular.module('myApp', [])
.controller('AccountsCtrl',
['$scope',
function($scope) {
var vs = $scope;
vs.accounts = [
{
id: '1',
label: 'Bitage'
},
{
id: '2',
label: 'Blockchain.info'
},
{
id: '3',
label: 'Coinbase wallet'
},
{
id: '4',
label: 'Xapo wallet'
}
];
vs.showPopover = function() {
vs.popoverRemove = true;
};
vs.hidePopover = function() {
vs.popoverRemove = false;
};
}]);
Plunker for you
The thing is that ng-repeat creates it's own scope.So, 'popoverRemove' is the local variable for each scope.You can set true or false to local variable by sending context to controller of that particular element of ng-repeat and set it's value using 'this'.
<button class="btn btn-default"
ng-mouseover="showPopover(this)"
ng-mouseleave="hidePopover(this)">Remove</button>
vs.showPopover = function(context) {
context.popoverRemove = true;
};
vs.hidePopover = function(context) {
context.popoverRemove = false;
};
You can also create a property on each iteration like so:
Instead of calling a function on the scope I simply set the value of well.show to true/false, but you get the jist of the idea!
<li ng-repeat="acc in accounts track by $index">
<div class="well well-sm">
<div class="popover-remove" ng-show="well.show">Click to remove item</div>
<h4>{{acc.label}}</h4>
<button class="btn btn-default"
ng-mouseover="well.show=true"
ng-mouseleave="well.show=false">Remove</button>
</div>
</li>
Plunker updated
Move popOverRemove to each accounts. Then you can control it from each child scope. Update your showPopover/hidePopover as well.
vs.accounts = [
{
id: '1',
label: 'Bitage',
popoverRemove: false
},
{
id: '2',
label: 'Blockchain.info',
popoverRemove: false
},
{
id: '3',
label: 'Coinbase wallet',
popoverRemove: false
},
{
id: '4',
label: 'Xapo wallet',
popoverRemove: false
}
];
I have updated here
http://plnkr.co/edit/uAjhiYTDyXIBmkxAQzzw?p=preview

exact filter in angular

In Angular, is there a way to modify the filter such that it only returns exact matches?
Example:
var words = [
{ title: "ball" },
{ title: "wall" },
{ title: "all" },
{ title: "alloy" }
];
var wordsFiltered = filter('filter')
(
words,
{
'title': 'all'
}
);
The above will match 'ball', 'wall', 'all' and 'alloy'. But I would like it to only match 'all'. Any way to change it?
UPDATE
Starting from AngularJS v.1.1.3 the exact filtering is provided natively:
Find words that exactly match title:
<input ng-model="match.title" />
<br>
and exactly match type:
<input ng-model="match.type" />
<hr>
<table>
<tr ng-repeat="word in words | filter:match:true">
<td>{{word.title}}</td>
</tr>
</table>
Plunker
Your question implies that you would want to match against multiple object properties so here's a filter that does that:
app.controller('AppController',
[
'$scope',
function($scope) {
$scope.match = {};
$scope.words = [
{ title: "ball", type: 'object' },
{ title: "wall", type: 'object' },
{ title: "all", type: 'word' },
{ title: "alloy", type: 'material' }
];
}
]
);
app.filter('exact', function(){
return function(items, match){
var matching = [], matches, falsely = true;
// Return the items unchanged if all filtering attributes are falsy
angular.forEach(match, function(value, key){
falsely = falsely && !value;
});
if(falsely){
return items;
}
angular.forEach(items, function(item){ // e.g. { title: "ball" }
matches = true;
angular.forEach(match, function(value, key){ // e.g. 'all', 'title'
if(!!value){ // do not compare if value is empty
matches = matches && (item[key] === value);
}
});
if(matches){
matching.push(item);
}
});
return matching;
}
});
<body ng-controller="AppController">
Find words that exactly match title:
<input ng-model="match.title" />
<br>
and exactly match type:
<input ng-model="match.type" />
<hr>
<table>
<tr ng-repeat="word in words | exact:match">
<td>{{word.title}}</td>
</tr>
</table>
</body>
PLUNKER
Try this :
var words = [
{ title: "ball" },
{ title: "wall" },
{ title: "all" },
{ title: "alloy" }
];
var wordsFiltered = filter('filter')
(
words,
{
'title': 'all'
},
true
);
You can use Regex to achieve a simple implementation:
<input ng-model="query" />
<tr ng-repeat="word in words | filter: myFilter">
In the controller:
$scope.myFilter = function (word) {
if ($scope.query === '') return true;
var reg = RegExp("^" + $scope.query + "$");
return reg.test(word.title);
};
I would create a new filter. Is this what you want?
HTML
<div ng-controller="MyCtrl">
{{words | exactMatch:'all'}} !
</div>
JavaScript
var myApp = angular.module('myApp',[]);
myApp.filter('exactMatch', function() {
return function(words, pattern) {
var result = [];
words.forEach(function (word) {
if (word.title === pattern) {
result.push(word);
}
});
return result;
}
});
function MyCtrl($scope) {
$scope.words = [
{title: "ball", other: 1},
{title: "wall", other: 2},
{title: "all", other: 3},
{title: "alloy", other: 4},
{title: "all", other: 5},
];
}
JsFiddle: jsfiddle
More information about custom filters: filters, creating custom filters and using filters
If you want use filter in Javascript instead of html you should look here: jsfiddle
I created my own filter.
<select
ng-model="selection.neType"
ng-options="option.NE_TYPE_ID as option.NAME for option in networkElementTypes">
<option value="">Todos</option>
</select>
<select
ng-model="selection.neTypeVendor"
ng-options="option.VENDOR_TYPE_ID as option.NAME for option in networkElementTypeVendors | exactMatch: {FK_NE_TYPE_ID: selection.neType}">
<option value="">All</option>
</select>
app.filter('exactMatch', function() {
return function(elements, pattern) {
var result = [];
var fieldSearch = Object.keys(pattern)[0];
elements.forEach(function (element) {
if (element[fieldSearch] == pattern[fieldSearch]) {
result.push(element);
}
});
return result;
}
});
angular-filter is a useful library of filters.
One of their filters is the where filter that does an exact match.

Resources