AngularJS - Filter multiselect - javascript

Im using $filter to filter my array.
var filteredData = params.filter() ?
$filter('filter')($scope.myNgTable.data, params.filter()):
$scope.myNgTable.data;
I created a multiple select filter, to filter by, but it returns an array:
col: ["a","b"], which does not work with $filter for what I see.
I want when the filter is col: ["a","b"] it would show all rows with col contains "a" or col contains "b". (if there is only a way for equal that s fine: col == "a" or col == "b")
Is that possible?
Answer:
.filter("in_Array", function ($filter){
return function(data, filter){
var out = [];
$.each(filter, function(key,val) {
var obj = {};
for(var i=0;i<val.length;i++) {
obj[key] = val[i];
var tmp = $filter('filter')(data, obj);
out = $.unique($.merge(out, tmp));
}
});
return out;
};
});

Look this example. This use the multi filter when click in checkbox
http://jsfiddle.net/rzgWr/123/
HTML:
<div ng-controller="MyCtrl">
<h4>Pick a brand to see the models</h4>
<div ng-init="group = (cars | groupBy:'make')" style="width:100%">
<div ng-repeat="m in group" style="width:100px; float:left;">
<b><input type="checkbox" ng-model="useMakes[$index]"/>{{m}}</b>
</div>
</div>
<div style="clear:both;"></div>
<div>
<ul>
<li ng-repeat="car in cars | filter:filterMakes()"><p><b>{{car.make}} - {{car.model}}</b></p></li>
</ul>
</div>
</div>
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope, filterFilter) {
$scope.useMakes = [];
$scope.filterMakes = function () {
return function (p) {
for (var i in $scope.useMakes) {
if (p.make == $scope.group[i] && $scope.useMakes[i]) {
return true;
}
}
};
};
$scope.cars = [
{model: '316', make: 'Bmw'},
{model: '520', make: 'Bmw'},
{model: 'Fiesta', make: 'Ford'},
{model: 'Focus', make: 'Ford'},
{model: 'Clio', make: 'Renault'},
{model: 'Toledo', make: 'Seat'},
{model: 'Leon', make: 'Seat'},
{model: 'Insignia', make: 'Opel'},
{model: 'Astra', make: 'Opel'},
{model: 'Corsa', make: 'Opel'}
];
}
var uniqueItems = function (data, key) {
var result = new Array();
for (var i = 0; i < data.length; i++) {
var value = data[i][key];
if (result.indexOf(value) == -1) {
result.push(value);
}
}
return result;
};
myApp.filter('groupBy',
function () {
return function (collection, key) {
if (collection === null) return;
return uniqueItems(collection, key);
};
});

Related

AngularJS filter | orderBy with custom order

I am trying to order a list in a ng-repeat. What i want to do is set the order by hand, so i can say players first, coach second and waterboy last in the list. This is where i got but now got stuck with filters (kinda new to me):
$scope.Team= [{
Name: "Saras"
Role: "coach"
},{
Name: "Arya"
Role: "player"
},{
Name: "Adam"
Role: "waterboy"
},{
Name: "Theo"
Role: "player"
},{
Name: "Mark"
Role: "player"
},{
Name: "Oscar"
Role: "player"
},{
Name: "Tom"
Role: "player"
},{
Name: "Gus"
Role: "coach"
}];
<div ng-repeat="person in Team | orderBy:Role>
<div>{{person.Name}}</div>
</div>
i would like to orderBy 'Role' this way, where i can set the order
'player', 'coach', 'waterboy'
with the filter below i am able to set the order correct and add the label of the Role (coach, player etc.), however the labels sometimes get repeated. How could i fix that?
<div ng-repeat="person in Team | myOrder:'Role'>
<h2 ng-show="item.Role_CHANGED">{{item.Role}}</h2>
<p>{{person.Name}}</p>
</div>
app.filter('myOrder', function () {
return function (list, group_by) {
function CustomOrder(item) {
switch (item) {
case 'coach':
return 2;
case 'player':
return 1;
case 'waterboy':
return 3;
}
}
var filtered = [];
var prev_item = null;
var group_changed = false;
var new_field = group_by + '_CHANGED';
angular.forEach(list, function (item) {
group_changed = false;
if (prev_item !== null) {
if (prev_item[group_by] !== item[group_by]) {
group_changed = true;
}
} else {
group_changed = true;
}
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
filtered.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
return filtered;
};
})
try this example :
html :
<div class="test" ng-controller="Ctrl">
<div ng-repeat="division in divisions | orderBy:['group','sub']">{{division.group}}-{{division.sub}}</div>
<div>
js :
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.divisions = [{'group':1,'sub':1}, {'group':2,'sub':10}, {'group':1,'sub':2},{'group':1,'sub':20},{'group':2,'sub':1},{'group':2,'sub':11}];
}
alright what i did to solve this is that i moved the sort function to the front when i GET the data from the JSON repsonse.
$http({
method: 'Get',
url: "https://xxxxxxx.azurewebsites.net/api/team/" + id_company
})
.success(function (data) {
var neworder = [];
function CustomOrder(item) {
switch (item) {
case 'player':
return 1;
case 'coach':
return 2;
case 'waterboy':
return 3;
}
}
angular.forEach(data, function (item) {
neworder.push(item);
});
neworder.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
$scope.Team = neworder;
});
after i just run the filter without the sort function;
<div ng-repeat="person in Team | myOrder:'Role'>
<h2 ng-show="item.Role_CHANGED">{{item.Role}}</h2>
<p>{{person.Name}}</p>
</div>
app.filter('myOrder', function () {
return function (list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
var new_field = group_by + '_CHANGED';
angular.forEach(list, function (item) {
group_changed = false;
if (prev_item !== null) {
if (prev_item[group_by] !== item[group_by]) {
group_changed = true;
}
} else {
group_changed = true;
}
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
})
it's a bit of a work-around but it did the job
just to be complete, if you just want to sort the ng-repeat in a filter and no need for the lables. you can just do this:
<div ng-repeat="person in Team | Sorter:'Role'>
<p>{{person.Name}}</p>
</div>
app.filter('Sorter', function () {
function CustomOrder(item) {
switch (item) {
case 'player':
return 1;
case 'coach':
return 2;
case 'waterboy':
return 3;
}
}
return function (items, field) {
var filtered = [];
angular.forEach(items, function (item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
return filtered;
};
});

Get selected option from backend using Angularjs

How can i init my select with option, that selects on backend (Symfony). Now i init my select (ng-model="myselect" example) like ng-init="myselect='0'"
I need to set actual selected option in ng-init, but if i remove ng-init directive it creates empty option with value ? undefined:undefined ?
What should i do?
<div ng-app="myapp">
<fieldset ng-controller="FirstCtrl">
<select
ng-options="p.id as p.first + ' ' + p.last for p in people"
ng-model="selectedPerson"></select>
{{ selectedPerson }}
</fieldset>
var myapp = angular.module('myapp', []);
myapp.controller('FirstCtrl', function ($scope) {
$scope.selectedPerson = 2;
$scope.people = [
{ id: 1, first: 'John', last: 'Rambo', actor: 'Silvester' },
{ id: 2, first: 'Rocky', last: 'Balboa', actor: 'Silvester' },
{ id: 3, first: 'John', last: 'Kimble', actor: 'Arnold' },
{ id: 4, first: 'Ben', last: 'Richards', actor: 'Arnold' }
];
});
This is what you expected..
Also check with this link http://jsfiddle.net/kaehejgo/
I was able to win this problem.
Anonymous function that turn vanilla html selects into angular models using my own function that converts string into object reference.
(function(){
window.selectControl = [];
var selects = document.querySelectorAll(".filter__select_range");
selects.forEach( function(elem, index) {
var _elem = angular.element(elem);
if (elem.querySelector("[selected]") !== undefined) {
window.selectControl[_elem.attr("data-model")] = elem.querySelector("[selected]").value;
_elem.attr({
"ng-model": _elem.attr("data-model"),
"ng-change": _elem.attr("data-change"),
"ng-init": _elem.attr("data-model") + "='"+ _elem.find('option:selected').val() +"'",
});
_elem.removeAttr('data-model').removeAttr('data-change');
stringToPath($scope, _elem.attr('ng-model'), elem.querySelector("[selected]").value);
}
});
})();
And stringToPath function
function stringToPath(obj, str, val) {
str = str.split(".");
for (var i = 0; i < str.length; i++) {
if (i != str.length-1 && str[i] != '') {
if (typeof obj[str[i]] !== "object") {
obj[str[i]] = {}
}
stringToPath(obj[str[i]], str.splice(1).join("."), val);
}
else {
obj[str[i]] = val;
}
}
return obj;
}
And one of my select in twig syntax
{{- form_widget(form, {'attr': attr|merge({'class': 'filter__select filter__select_range', 'data-model': 'FilterCollection.range.area.min', 'data-change': "checkSelectedFilters();"})}) -}}

Storing checked checkboxes into array in angularjs tree structure

Currently i am able to display item that have parent child relation in tree structure using checkboxes. Now I need to store the checked checkboxes into one array so that I can submit that data to server via ajax.
I am new to angularjs. I tried printing using ng-model value. But it doesn't work.
Can you help me with how I can store the checked checkboxes into array.
Below is the code.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var app, list;
list = [
{
name: 'Developer',
opened: true,
children: [
{
name: 'Front-End',id:1,
children: [
{
name: 'Jack',id:2,
title: 'Leader'
},
{
name: 'John',id:3,
title: 'Senior F2E'
},
{
name: 'Jason',id:4,
title: 'Junior F2E'
}
]
},
{
name: 'Back-End',id:5,
children: [
{
name: 'Mary',id:6,
title: 'Leader'
},
{
name: 'Gary',id:7,
title: 'Intern'
}
]
}
]
},
{
name: 'Design',id:8,
children: [{
name: 'Freeman',id:9,
title: 'Designer'
}]
},
{
name: 'S&S',id:10,
children: [{
name: 'Nikky',id:11,
title: 'Robot'
}]
}
];
app = angular.module('testApp', []).controller('treeTable', [
'$scope',
'$filter',
function ($scope, $filter) {
$scope.list = list;
//$scope.item.selected={};
$scope.initCheckbox = function (item, parentItem) {
return item.selected = parentItem && parentItem.selected || item.selected || false;
};
$scope.toggleCheckbox = function (item, parentScope) {
if (item.children != null) {
$scope.$broadcast('changeChildren', item);
}
if (parentScope.item != null) {
return $scope.$emit('changeParent', parentScope);
}
};
$scope.$on('changeChildren', function (event, parentItem) {
var child, i, len, ref, results;
ref = parentItem.children;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
child.selected = parentItem.selected;
if (child.children != null) {
results.push($scope.$broadcast('changeChildren', child));
} else {
results.push(void 0);
}
}
return results;
});
return $scope.$on('changeParent', function (event, parentScope) {
var children;
children = parentScope.item.children;
parentScope.item.selected = $filter('selected')(children).length === children.length;
parentScope = parentScope.$parent.$parent;
if (parentScope.item != null) {
return $scope.$broadcast('changeParent', parentScope);
}
});
}
]);
app.filter('selected', [
'$filter',
function ($filter) {
return function (files) {
return $filter('filter')(files, { selected: true });
};
}
]);
</script>
</head>
<body>
<div class="wrapper" ng-app="testApp" ng-controller="treeTable">
<table class="table-nested">
<tbody ng-class="{opened: item.opened}" ng-include="'table_tree.html'" ng-repeat="item in list"></tbody>
</table>
<script id="table_tree.html" type="text/ng-template">
<tr ng-class="{parent: item.children}" ng-init="parentScope = $parent.$parent; initCheckbox(item, parentScope.item)">
<td class="cell-name">
<div class="indent" ng-click="item.opened = !item.opened"></div>
<input ng-change="toggleCheckbox(item, parentScope)" ng-model="item.selected" type="checkbox" />
{{item.name}}
</td>
</tr>
<tr class="children" ng-if="item.children && item.children.length > 0">
<td colspan="4">
<table class="table-child">
<tbody ng-class="{opened: item.opened}" ng-include="'table_tree.html'" ng-init="level = level + 1" ng-repeat="item in item.children"></tbody>
</table>
</td>
</tr>
</script>
{{item.selected | json}}
</div>
</body>
check plunker here
Maybe you can do it like this:
JS:
$scope.seleceds = {};
$scope.initCheckbox = function (item, parentItem) {
return $scope.seleceds[item.id] = parentItem && $scope.seleceds[parentItem.id] || $scope.seleceds[item.id] || false;
};
$scope.toggleCheckbox = function (item, parentScope) {
if (item.children != null) {
$scope.$broadcast('changeChildren', item);
}
if (parentScope.item != null) {
return $scope.$emit('changeParent', parentScope);
}
};
$scope.$on('changeChildren', function (event, parentItem) {
var child, i, len, ref, results;
ref = parentItem.children;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
$scope.seleceds[child.id] = $scope.seleceds[parentItem.id];
if (child.children != null) {
results.push($scope.$broadcast('changeChildren', child));
} else {
results.push(void 0);
}
}
return results;
});
return $scope.$on('changeParent', function (event, parentScope) {
var children;
children = parentScope.item.children;
$scope.seleceds[parentScope.item.id] = $filter('selected')(children, $scope.seleceds).length === children.length;
parentScope = parentScope.$parent.$parent;
if (parentScope.item != null) {
return $scope.$broadcast('changeParent', parentScope);
}
});
Extra filter:
app.filter('selected', ['$filter', function ($filter) {
return function (files, obj) {
return $filter('filter')(files, function (value) {
return obj[value.id]
});
};
}]);
app.filter('objToArray', function () {
return function (input) {
var results = [];
for (var key in input) {
input[key] && results.push(Number(key))
}
return results;
}
});
HTML:
<input ng-change="toggleCheckbox(item, parentScope)" ng-model="seleceds[item.id]" type="checkbox"/>
And change {{item.selected | json}} to {{seleceds|objToArray}}
you can see a demo HERE

Duplication in ng-repeat with filter applied

I am trying to output an object with specific blood group. I am using a Filter for it.
I am getting duplication error on this. I used chrome debugger and found out that its executing multiple times. Data is passed to filter in each iteration and filter do its complete work from scratch each time.
This is my Filter method
Filters.filter('bloodFilter', function() {
var i, filtered = [];
return function(data, bloodGroup) {
if (!data) {
data = GitHub.USERS;
}
if (!bloodGroup) {
bloodGroup = 'O+'
}
for (i = 0; i < data.length; i += 1) {
var value = data[i];
if (value.blood === bloodGroup) {
filtered.push(value);
}
}
return filtered;
}
});
This is My Data
GitHub.USERS = [
{
fname: "Usman",
lname: "Tahir",
blood: "O+"
},
{
fname: "Ali",
lname: "Hassan",
blood: "B+"
},
{
fname: "Aqib",
lname: "Javed",
blood: "AB+"
}
];
This is my Controller
Controllers.controller('BodyController', function($scope) {
$scope.users = GitHub.USERS;
});
and this is my index code
<body ng-controller="BodyController">
<ul>
<li ng-repeat="item in users | bloodFilter">
<b>{{item.fname}}</b> {{item.lname}} {{item.blood }}
</li>
</ul>
</body>
Issue is related to JavaScript Closures. I defined array to return outside return block which created problem.
My Filter Function Should be like this, i and filtered should be inside return block.
Filters.filter('bloodFilter', function() {
return function(data, bloodGroup) {
var i, filtered = [];
if (!data) {
data = GitHub.USERS;
}
if (!bloodGroup) {
bloodGroup = 'O+'
}
for (i = 0; i < data.length; i += 1) {
var value = data[i];
if (value.blood === bloodGroup) {
filtered.push(value);
}
}
return filtered;
}
});

Adding and removing items from ng-repeat, simple cart implementation, using angular

I'm trying to achieve an array with items that displays as a title and image and when the user click the title the item should be added to a div. However, I canĀ“t make it to work as when I click on "Add me", there is only a blank item added, with no image and no title.
I got the delete-function to work but not the add-function.
Here is what I got:
The "Add me"-button and the list of items
<li ng-repeat="item in items.data">
{{item.title}} <img ng-src="{{ item.image }}" /><a ng-click="deleteItem($index)" class="delete-item">x</a>
<button ng-click="addItem()">Add Me</button>
</li>
The array
var myApp = angular.module("myApp", []);
myApp.factory("items", function () {
var items = {};
items.data = [{
title: "Item 1",
image: "img/item01.jpg"
}, {
title: "Item 2",
image: "img/item02.jpg"
}, {
title: "Item 3",
image: "img/item03.jpg"
}, {
title: "Item 4",
image: "img/item04.jpg"
}];
return items;
});
And the functions for add and delete
function ItemsController($scope, items) {
$scope.items = items;
$scope.deleteItem = function (index) {
items.data.splice(index, 1);
}
$scope.addItem = function () {
items.data.push({
title: $scope.items.title
});
}
}
You need to pass the item under iteration.
<button ng-click="addItem(item)">Add Me</button>
and add the title to the newly added:
$scope.addItem = function(item) {
items.data.push({
title: item.title
});
}
it is better not to use $index (it can cause issue when used with filters eg: orderBy filter) instead just pass the item to delete and:
$scope.deleteItem = function(item) {
items.data.splice(items.indexOf(item), 1);
}
You also have an invalid html, li must be a child of ul or ol.
A sample implementation:
function($scope, items) {
$scope.items = items;
$scope.cart = [];
$scope.deleteItem = function(item) {
var cart = $scope.cart;
//Get matched item from the cart
var match = getMatchedCartItem(item);
//if more than one match exists just reduce the count
if (match.count > 1) {
match.count -= 1;
return;
}
//Remove the item if current count is 1
cart.splice(cart.indexOf(item), 1);
}
$scope.addItem = function(item) {
//Get matched item from the cart
var match = getMatchedCartItem(item), itemToAdd ;
//if item exists just increase the count
if (match) {
match.count += 1;
return;
}
//Push the new item to the cart
itemToAdd = angular.copy(item);
itemToAdd.count = 1;
$scope.cart.push(itemToAdd);
}
function getMatchedCartItem(item) {
/*array.find - Find the shim for it in the demo*/
return $scope.cart.find(function(itm) {
return itm.id === item.id
});
}
Demo
angular.module('app', []).controller('ctrl', function($scope, items) {
$scope.items = items;
$scope.cart = [];
$scope.deleteItem = function(item) {
var cart = $scope.cart;
var match = getMatchedCartItem(item);
if (match.count > 1) {
match.count -= 1;
return;
}
cart.splice(cart.indexOf(item), 1);
}
$scope.addItem = function(item) {
var match = getMatchedCartItem(item);
if (match) {
match.count += 1;
return;
}
var itemToAdd = angular.copy(item);
itemToAdd.count = 1;
$scope.cart.push(itemToAdd);
}
function getMatchedCartItem(item) {
return $scope.cart.find(function(itm) {
return itm.id === item.id
});
}
}).factory("items", function() {
var items = {};
items.data = [{
id: 1,
title: "Item 1",
image: "img/item01.jpg"
}, {
id: 2,
title: "Item 2",
image: "img/item02.jpg"
}, {
id: 3,
title: "Item 3",
image: "img/item03.jpg"
}, {
id: 4,
title: "Item 4",
image: "img/item04.jpg"
}];
return items;
});
if (!Array.prototype.find) {
Array.prototype.find = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return value;
}
}
return undefined;
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="item in items.data" id="item{{item.id}}">
{{item.title}}
<img ng-src="{{ item.image }}" />
<button ng-click="addItem(item)">Add Me</button>
</li>
</ul>
<p>Cart:</p>
<ul>
<li ng-repeat="item in cart">
{{item.title}} | Count: {{item.count}}
<a ng-click="deleteItem(item)" class="delete-item">X</a>
</li>
</ul>
</div>
I am using Array.prototype.find, you would need to add the shim (as mentioned in the demo) to be able to make it work for non supported browsers.

Categories

Resources