ng-repeat unique values only (combine duplicates) - angularjs

I have an ng-repeat like this:
<option ng-repeat="item in items | orderBy:'priority'"
value="{{item.SAC}}.{{item.difficulty}}">
{{item.SAC}}.{{item.difficulty}} - {{SACItem(item.SAC).description}}
</option>
I want it to combine any duplicates based on both item.SAC and item.difficulty if both of those are a duplicate of the repeat they should be combined, otherwise they can be seperate.
I tried the unique filter but I couldn't get it to work with 2 variables.

You can create a filter:
var app = angular.module('app', []);
app.controller('ctrl', function($scope) {
$scope.items = [
{ name: 'john', age:45 },
{ name: 'tim', age: 36 },
{ name: 'bob', age: 44 },
{ name: 'john', age: 45 }
];
});
app.filter('unique', function() {
var uniqueItems = [];
function exists(item, parms) {
return uniqueItems.some((i) => parms.every((p) => i[p] == item[p]));
}
return function(arr, name, age) {
if (arr) {
uniqueItems.length = 0;
angular.forEach(arr, function(item) {
if (!exists(item, [name,age])) {
uniqueItems.push(item);
}
});
return uniqueItems;
}
return arr;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.4.1/es5-shim.js"></script>
<div ng-app ="app" ng-controller="ctrl">
<h1>Original Array</h1>
<ul>
<li ng-repeat="item in items">
{{item.name }} - {{ item.age }}
</li>
</ul>
<h1>Unique Array</h1>
<ul>
<li ng-repeat="item in items | unique:'name':'age'">
{{item.name }} - {{ item.age }}
</li>
</ul>
</div>
You can also use this filter in the controller instead of the view:
app.controller('ctrl', function($scope, $filter) {
var items = [...];
$scope.items = $filter('unique')(items, 'name', 'age');
});

Related

Display a value from an object based on an id in AngularJS

I'm currently using a custom filter to display a name when a user id is displayed. So selected_user might be 5, so it then displays "bob" instead of "5":
{{ selected_user | display_name:users }}
The users object contains the entire users table.
The code:
angular.module('myApp', []);
function xCtrl($scope) {
$scope.users = [
{"id":1, "name":"joe"},
{"id":5, "name":"bob"},
{"id":10, "name":"charlie"},
];
$scope.select_user = function(user) {
$scope.selected_user = user;
}
}
angular.module('myApp').filter("display_name", function () {
return function (user_id, users) {
if(user_id) {
return(users[users.map(function(x) { return x.id; }).indexOf(user_id)].name);
}
}
});
This works fine, but it feels like I am doing this inefficiently by passing the entire users object to the filter each time a name is displayed. Is there a better way to do this with filters or are filters the wrong approach?
Demo: http://jsfiddle.net/h2rn3qnw/
One approach is to have the ng-click directive return the $index:
<div ng-repeat="user_data in users"
ng-click="select($index)" class="selection">
{{ user_data.id }} - {{ user_data.name }} - {{$index}}
</div>
$scope.select = function(index) {
$scope.selection = index;
};
Selected User ID: {{ users[selection].id }}<br>
Selected User Name: {{ users[selection].name }}
The DEMO
angular.module('myApp', [])
.controller("xCtrl", function xCtrl($scope) {
$scope.users = [
{"id":1, "name":"joe"},
{"id":5, "name":"bob"},
{"id":10, "name":"charlie"},
];
$scope.select = function(index) {
$scope.selection = index;
};
})
.selection { color:blue; cursor:pointer; }
<script src="//unpkg.com/angular/angular.js"></script>
<div ng-app='myApp'>
<div ng-controller="xCtrl">
Users:<br>
<div ng-repeat="user_data in users"
ng-click="select($index)" class="selection">
{{ user_data.id }} - {{ user_data.name }} - {{$index}}
</div> <!-- ng-repeat -->
<hr>
Selected User ID: {{ users[selection].id }}<br>
Selected User Name: {{ users[selection].name }}
</div> <!-- ng-controller -->
</div> <!-- ng-app -->

How to sort dropdown list of objects in AngularJS

I have:
<select class="form-control" id="field_productDelivered" name="productDelivered" ng-model="vm.productDelivered.productDelivered" ng-options="product as product.name for product in vm.products track by product.id">
<option value=""></option>
</select>
Output is sorted by id, but I need to have it sorted by name.
I tried:
ng-options="product as product.name for product in vm.products track by product.id" | toArray | orderBy : 'name'"
but in console I get:
TypeError: dbg is undefined.
How can I sort it using Angular?
I think this should get you output going in the right direction. Hope it helps! Included a working example, with it the third param true to show items in reverse order.
This is what I imagine yours will look like to get them ordered accordingly.
ng-options="product as product.name for product in vm.products | toArray | orderBy : 'name' track by product.id"
function exampleController($scope) {
$scope.phones = [{
model: 'anteater'
}, {
model: 'bee'
}, {
model: 'cat'
}];
}
angular
.module('example', [])
.controller('exampleController', exampleController);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="container-fluid" ng-app="example">
<div class="container" ng-controller="exampleController">
<div class="row">
<ol>
<li ng-repeat="phone in phones | orderBy: 'model': true track by $index" ng-bind="phone.model"></li>
</ol>
</div>
</div>
</div>
This is a working example as same as in your case.
It looks you had the following two things which should be sorted.
You have closed the double quotes before the " | toArray in
"product as product.name for product in vm.products track by
product.id" | toArray | orderBy : 'name'"
Also the track by product.id should be at the end after the
filters like ng-options="product as product.name for product in vm.products | toArray | orderBy : 'name' track by product.id"
You can see the working example below as same as the one in the question.
var app = angular.module('app', []);
app.controller('TestController', function() {
this.productDelivered = {};
this.products = {
'product1': {
id: 4,
name: 'product B'
},
'product2': {
id: 3,
name: 'product D'
},
'product3': {
id: 1,
name: 'product A'
},
'product4': {
id: 2,
name: 'product C'
}
};
});
app.filter("toArray", function() {
return function(input) {
if(!input) return;
if (input instanceof Array) {
return input;
}
return $.map(input, function(val) {
return val;
});
};
});
angular.bootstrap(document, ['app']);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="TestController as vm">
<select class="form-control" id="field_productDelivered" name="productDelivered" ng-model="vm.productDelivered.productDelivered"
ng-options="product as product.name for product in vm.products | toArray | orderBy : 'name' track by product.id">
<option value=""></option>
</select>
</div>

ng-repeat array in array filtering by property of parent with AngularJS

I want to select an array in array by property of the parent. I don't know the index (in this case 1), but I know the id. Can I do it? This is my working example with index, I want same results by id (43).
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div data-ng-app="myApp" data-ng-controller="myCtrl">
<ul>
<li ng-repeat="p in people[1].rooms" ng-bind="p"></li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.people = [
{
id: 25,
nick: 'Joker',
rooms: ['room1', 'room2']
},
{
id: 43,
nick: 'King',
rooms: ['room3', 'room4']
},
{
id: 78,
nick: 'Queen',
rooms: ['room5', 'room6']
}
]
});
</script>
You should separate get rooms for particular user by its id using strict filter on people array. Its obvious that you are going to a single record as its id based search.
Code
<ul>
<li ng-repeat="p in filteredPeople = (people | filter: {id: l.id })[0].rooms">
{{p}}
</li>
</ul>
This is one option:
<ul ng-repeat="p in people | filter: { id: 45 }">
<li ng-repeat="room in p.rooms" ng-bind="room"></li>
</ul>

How to filter first letter in a button using AngularJS

How to make the button functional, and the button has default value, for example button A-B has a range for a to b of first letter to be filter. Thanks, Sorry for my last post, it was hard to understand. =)
var app = angular.module('app', []);
app.filter('startsWithLetter', function () {
return function (items, letter) {
var filtered = [];
var letterMatch = new RegExp(letter, 'i');
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (letterMatch.test(item.name.substring(0, 1))) {
filtered.push(item);
}
}
return filtered;
};
});
app.controller('PersonCtrl', function () {
this.friends = [{
name: 'Andrew'
}, {
name: 'Baldo'
}, {
name: 'Carlo'
}, {
name: 'Delo'
}, {
name: 'Emman'
}, {
name: 'Ferman'
}];
});
</style>
<script src="//code.angularjs.org/1.3.0-beta.7/angular.js"></script>
<style>
<div ng-app="app">
<div ng-controller="PersonCtrl as person">
<input type="text" ng-model="letter" placeholder="Enter a letter to filter">
<button>A-B</button>
<button>C-D</button>
<button>E-F</button>
<ul>
<li ng-repeat="friend in person.friends | startsWithLetter:letter">
{{ friend }}
</li>
</ul>
</div>
</div>
Is this what you meant? Did you want clicking each button to filter by those 2 letters?
All I changed was set the markup for the buttons to be:
<button ng-click="letter='[AB]'">A-B</button>
<button ng-click="letter='[CD]'">C-D</button>
<button ng-click="letter='[EF]'">E-F</button>
var app = angular.module('app', []);
app.filter('startsWithLetter', function () {
return function (items, letter) {
var filtered = [];
var letterMatch = new RegExp(letter, 'i');
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (letterMatch.test(item.name.substring(0, 1))) {
filtered.push(item);
}
}
return filtered;
};
});
app.controller('PersonCtrl', function () {
this.friends = [{
name: 'Andrew'
}, {
name: 'Baldo'
}, {
name: 'Carlo'
}, {
name: 'Delo'
}, {
name: 'Emman'
}, {
name: 'Ferman'
}];
});
</style>
<script src="//code.angularjs.org/1.3.0-beta.7/angular.js"></script>
<style>
<div ng-app="app">
<div ng-controller="PersonCtrl as person">
<input type="text" ng-model="letter" placeholder="Enter a letter to filter">
<button ng-click="letter='[AB]'">A-B</button>
<button ng-click="letter='[CD]'">C-D</button>
<button ng-click="letter='[EF]'">E-F</button>
<ul>
<li ng-repeat="friend in person.friends | startsWithLetter:letter">
{{ friend }}
</li>
</ul>
</div>
</div>

How do I cancel drag between different lists when using angular-ui's sortable module

I am using angular-ui's sortable-ui module and am trying to raise a cancel so that the dragged items returns to it original location in the source list. Unfortunately I cannot get this working. Here is an example:
http://jsfiddle.net/Ej99f/1/
var myapp = angular.module('myapp', ['ui.sortable']);
myapp.controller('controller', function ($scope) {
$scope.list = ["1", "2", "3", "4", "5", "6"];
$scope.list2 = ["7", "8", "9"];
$scope.sortableOptions = {
update: function(e, ui) {
if (Number(ui.item.text()) === 6) {
ui.item.parent().sortable('cancel');
}
},
receive: function(e, ui) {
ui.sender.sortable('cancel');
ui.item.parent().sortable('cancel');
},
connectWith: ".group",
axis: 'y'
};
});
angular.bootstrap(document, ['myapp']);
Any help would be gratefully appreciated.
well, when it comes to angular, all roads lead to the data "the single source of truth". So update your model back to it's original state, before the move, and you're all set :)
example below has two lists, the first one being restricted for
its sorting (the update method)
and for sending an item (receive method on list 2)
the second list you can sort, and send items to list 1
(using foundation4 for css)
<div ng-app="test">
<div ng-controller="sortableTest">
<div class="small-4 columns panel">
<ul data-drop="true"
ui-sortable="sortable.options.list1" ng-model="sortable.model.list1">
<li ng-repeat="fruit in sortable.model.list1"
data-id="{{ fruit.id }}">{{ fruit.label }}</li>
</ul>
</div>
<div class="small-4 columns panel">
<ul data-drop="true"
ui-sortable="sortable.options.list2" ng-model="sortable.model.list2">
<li ng-repeat="element in sortable.model.list2"
data-id="{{ element.id }}">{{ element.label }}</li>
</ul>
</div>
<div class="clear"></div>
<br />
<span ng-repeat="fruit in sortable.model.list1">{{ fruit.label }} </span><br />
<span ng-repeat="element in sortable.model.list2">{{ element.label }} </span><br />
<span ng-repeat="fruit in sortable.oldData.list1">{{ fruit.label }} </span><br />
<span ng-repeat="element in sortable.oldData.list2">{{ element.label }} </span><br />
</div>
</div>
js:
var test = angular.module('test', ['ui.sortable']);
test.controller('sortableTest', function($scope, $timeout) {
$scope.sortable = {
model: {
list1: [{id: 1, label: 'apple'},{id: 2, label: 'orange'},{id: 3, label: 'pear'},{id: 4, label: 'banana'}],
list2: [{id: 5, label: 'earth'},{id: 6, label: 'wind'},{id: 7, label: 'fire'},{id: 8, label: 'water'}]
},
oldData: {
list1: [],
list2: []
},
options: {
list1: {
update: function(event, ui) {
console.debug('up-list1');
$scope.sortable.oldData.list1 = $scope.sortable.model.list1.slice(0);
$scope.sortable.oldData.list2 = $scope.sortable.model.list2.slice(0);
// DO NOT USE THIS! it messes up the data.
// ui.item.parent().sortable('cancel'); // <--- BUGGY!
// uncomment and check the span repeats..
$timeout(function(){
$scope.sortable.model.list1 = $scope.sortable.oldData.list1;
$scope.sortable.model.list2 = $scope.sortable.oldData.list2;
});
},
connectWith: 'ul'
},
list2: {
update: function(event, ui) {
console.debug('up-list2');
},
connectWith: 'ul',
receive: function(event, ui) {
console.debug('re-list2');
$timeout(function(){
$scope.sortable.model.list1 = $scope.sortable.oldData.list1;
$scope.sortable.model.list2 = $scope.sortable.oldData.list2;
});
}
}
}
};
});
you can of course use a service or something to store the old value. One can use ui.sender to differentiate the senders, if you have more that two..

Resources