Custom Sorting in AngularJs - javascript

I have JSON field like
{"facebook":"aravind#facebook.com",
"homeAddress":"26, New Street, Blr",
"officeAddress":"31, Old Office Street, Blr",
"city":"Blr",
name:"aravind"
},
{"facebook":"ashok#facebook.com",
"homeAddress":"26, New Street, che",
"officeAddress":"31, Old Office Street, che",
"city":"che",
name:"ashok"
}
In Angular js, I have ordered the field by name defaultly, I have one text box and button
<input type="text" class="form-control" ng-model="search.location" />
<div class="search" ng-click="locationSorting()"></div>
When i click the button after enter the location in text box( For Ex: blr). The list is sorted by location.
Currently i have finishing the filter by location. But i dont want filtering. I want sorting the list
var locationFilterHome = {homeAddress: location};
filterData = $filter('filter')(data, locationFilterOffice)
I am using above code for filtering. How to do sorting based on location

From what I understand, you wish to ask how to sort the data by location in the controller.
One way would be to use the 'orderBy' filter. You can use it just like you use the 'filter' filter: filterData=$filter('orderBy')(data,'location');
Please check out this plunk for an example of what I am trying to say: http://plnkr.co/edit/BU4DaxlBSNcmRkXG6krc?p=preview
angular.module('app',[])
.controller('mainCtrl',function($filter){
var data=[{"facebook":"aravind#facebook.com",
"homeAddress":"26, New Street, Blr",
"officeAddress":"31, Old Office Street, Blr",
"city":"Zlr",
name:"aravind"
},
{"facebook":"ashok#facebook.com",
"homeAddress":"26, New Street, che",
"officeAddress":"31, Old Office Street, che",
"city":"che",
name:"ashok"
}];
var modifiedData=$filter('orderBy')(data,'city');
console.log(modifiedData);
})

Your can create filter like this way!!
`bizBrainCommon.filter('test', function () {
return function (items, field, sortingValue) {
var filtered = [];
if(!field || !sortingValue)
return items;
angular.forEach(items, function (item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
var upA = a[field].toUpperCase();
if (upA > sortingValue.toUpperCase() || upA < sortingValue.toUpperCase()) return 1;
if (upA == sortingValue.toUpperCase())
return -1;
});
return filtered;
};
});`
HTML will be like:
ng-repeat="(id, user) in users |test:'fName': search"
Hope this will help you!!

Related

Limit angular ng-repeat to certain rows

For example if i had the json dataset here of all languages of books:
$scope.data = [{
"title": "Alice in wonderland",
"author": "Lewis Carroll",
"lang": ["en"]
}, {
"title": "Journey to the West",
"author": "Wu Cheng'en",
"lang": ["ch"]
}]
And I simply wanted to display exclusively english books, would I be able to do this purely using a filter in ng-repeat?
E.g.
<div ng-repeat="d in data | filter:d.lang='en'" style="margin-bottom: 2%">
{{d.title}}
</div>
I do not want to do it via any sort of form control (radio button etc). Would this be possible?
-EDIT- Thanks #GrizzlyMcBear for leading me down the right path! I got it to work with a slightly different filter function (which I'll paste below)
app.filter('MyFilter', function() {
var out = [];
angular.forEach(input, function(i) {
if (i.lang[0] === 'en') {
out.push(i);
}
})
return out;
}
});
and in the HTML
<div ng-repeat="d in data | MyFilter" style="margin-bottom: 2%">
{{d.title}}
</div>
Try like this
<div ng-repeat="d in data | filter: { lang : 'en'} " style="margin-bottom: 2%">
DEMO
You should use angular's filter,
I would also suggest that you use a function in the filter:
<div ng-repeat="item in collection | filter:filteringFunction" style="margin-bottom: 2%">
{{d.title}}
</div>
This way gives you more freeeeeedom (you're more than welcome to shout it Mel Gibson style ;-) )
in filtering your data by introducing more complex filtering logic.
var filteredLang = "en";
function filterByBookLanguage(collectionItem) {
var result = false;
if (collectionItem.lang[0] === filteredLang) {
result = true;
}
return result;
}
$scope.filteringFunction = filterByBookLanguage;
Now If you wish, you can also change the comperator function - filterByBookLanguage
(my terminology).
Say that your boss suddenly wants you to change the filtering logic from filtering books
into filtering by the author's name. Now all you have to do is add this condition:
if (bossWantsToChangeFilter) {
$scope.filteringFunction = filterByAuthorName;
} else {
$scope.filteringFunction = filterByBookLanguage;
}
All you have to remember is to write the comperator function with the current filtered item
as an argument and update the compared value of the language/author name
in the location you've found convenient ($scope, local variable, service etc.).

How to sort JSON data in ng-repeat?

I am getting a server response and binding these data to view using ng-repeat. Now I want to sort these data by priceList and name. I am able to sort name using orderBy, but not with priceList. I want to sort the products based on priceList. Sorting with name will change the order of list while sorting by priceList will effect only the order of products of each category. It will effect the order of displayed category. Please help me resolve this.
My code:
<div ng-controller="Ctrl">
<pre>Sorting predicate = {{predicate}};</pre>
<hr/>
<table class="friend">
<tr>
<th>Name
</th>
<th><a href="" ng-click="predicate = 'priceList'>price</a></th>
</tr>
</table>
<div ng-repeat="data in _JSON[0].categories | orderBy:predicate">
<div ng-repeat="vals in data.itemTypeResults |orderBy:'partTerminologyName'" id="{{vals.partTerminologyName}}">
`<h4 style="background-color: gray">{{vals.partTerminologyName}} : Position :{{vals.position}}</h4>`<br>
<div ng-repeat="val in vals.products">
<b> Quantity:{{val[0].perCarQty}}</b><br>
<b> part:{{val[0].partNo}}</b><br>
<b>sku:{{val[0].sku}}</b><br>
<b> qtyInStock:{{val[0].qtyInStock}}</b><br>
<b> priceList:{{val[0].priceList}}</b><br>
<b>priceSave:{{val[0].priceSave}}</b><br>
<b> qtyDC:{{val[0].qtyDC}}</b><br>
<b> qtyNetwork:{{val[0].qtyNetwork}}</b><br>
<b> priceCore:{{val[0].priceCore}}</b><br>
</div>
</div>
</div>
JS:
$scope._JSON = [
{"categories":
[
{"id":14061,"name":"Drive Belts",
"itemTypeResults":[
{"partTerminologyName":"Serp. Belt",
"position":"Main Drive",
"products":{
"5060635":[
{"perCarQty":2,"partNo":"5060635",
"sku":"20060904","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":19.15,"priceSave":3.29,
"priceCore":10.0}
],
"635K6":[
{"perCarQty":9,"partNo":"635K6",
"sku":"10062449","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0}
]
}
}
]
},
{"id":2610,"name":"Drive Belt Tensioners, Idlers, Pulleys & Components",
"itemTypeResults":[
{"partTerminologyName":"Drive Belt Tensioner Assembly",
"position":"N/A",
"products":{
"950489A":[
{"perCarQty":4,"partNo":"950489A",
"sku":"10150833","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0
}
]
}},
{"partTerminologyName":"Drive Belt Idler Pulley","position":"N/A",
"products":{
"89161":[
{"perCarQty":1,"partNo":"89161",
"sku":"99995959","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":17.15,"priceSave":3.21,"priceCore":10.0}
],
"951373A":[
{"perCarQty":2,"partNo":"951373A","pla":"LTN",
"plaName":"Litens",
"sku":"10150926","qtyInStock":2,"qtyNetwork":4,
"qtyDC":6,"priceList":18.15,"priceSave":3.21,"priceCore":10.0}
]
}
}
]
}
]
}
];
$scope.predicate = '';
Fiddle: Fiddle
You might need to define a very good sorter function, or sort your products before they are interpreted by ng-repeat. I've created sorter function using underscore.js (or lodash).
You can checkout the demo (or the updated demo). Products are first sorted by category and then sorted by price in every category.
<!-- in html -->
<button ng-click="sortFn=sortByPrice">Sort By Price</button>
<button ng-click="sortFn=doNotSort">Do not Sort</button>
...
<div ng-repeat="val in sortFn(vals.products)">
...
// in js
$scope.sortByPrice = function(products) {
return _.sortBy(products, function(product) {
return product.length > 0 ? product[0].priceList : 0;
});
};
$scope.doNotSort = function(products) {
return products;
};
$scope.sortFn = $scope.doNotSort; // do not sort by default
BTW: You are directly calling val[0], which is very dangerous, if the product does not contain any elements, your code will break. My code won't ;-)
Update 1
The author asks me for a more pure Angular way solution.
Here is my answer: I think my solution is exactly in Angular way. Usually you can implement a filter (similar to orderBy) which wraps my sortByPrice. Why I don't do that, because you have ng-click to switch your order filter. I'd rather put control logic into a controller, not as pieces into view. This will be harder to maintain, when your project keeps growing.
Update 2
Okay, to make the +50 worthy, here is the filter version you want, (typed with my brain compiler) Please check in fiddle
You need to organize the products in other estructure. For example:
$.each($scope._JSON[0].categories , function( i , e) {
$.each(e.itemTypeResults, function(sub_i,sub_e) {
$.each(sub_e.products, function(itemTypeResults_i,product) {
console.log(product);
var aProduct = new Object();
aProduct.priceList = product[0].priceList;
aProduct.name = e.name;
$scope.products.push(aProduct);
});
} )
});
The code is not very friendly but what i do is putt all the products in one array so they can be ordered by the price. You have the products inside categories so that's why angular is ordering by the price in each category.
Fiddle:
http://jsfiddle.net/7rL8fof6/1/
Hope it helps.
Your fiddle updated: http://jsfiddle.net/k5fkocby/2/
Basically:
1. Digested the complex json object into a flat list of objects:
var productsToShow = [];
for (var i=0; i < json[0].categories.length; i++){
var category = json[0].categories[i];
for (var j=0; j<category.itemTypeResults.length;j++){
var item = category.itemTypeResults[j];
var products = item.products;
for (var productIndex in products) {
var productItems = products[productIndex];
for (var k=0; k<productItems.length;k++){
var productItem = productItems[k];
// Additions:
productItem.categoryName = category.name;
productItem.partTerminologyName = item.partTerminologyName;
productItem.position = item.position;
productsToShow.push(productItem);
}
}
}
}
Show category title only when needed by:
ng-repeat="product in (sortedProducts = (productsToShow | orderBy:predicate))"
and
ng-show="sortedProducts[$index - 1].partTerminologyName != product.partTerminologyName"
you can sort from your database and get final JSON data..
db.categories.aggregate([{$group : {category : {your condition} }, price: {$sort : { price: 1 } },}}])

collect filtered ng-repeat data to use in controller

I am trying to build a picture library in a responsive grid with data coming down from a Mongo DB. Data from db is in this form. name, image, keyword, bio, reference", searchable,
I start with an ng-repeat to display category selection checkboxes based on the picture keywords. This seems to work fine.
p CATEGORIES:
span.formgroup(data-ng-repeat='category in mongoController.catData')
input.checks( type='checkbox', ng-model='mongoController.filter[category]')
| {{category}}
Here is the factory that sorts and identifies the keyword checkboxes:
getCategories: function (cat) {
return (cat || []).map(function (pic) {
return pic.keyword;
}).filter(function (pic, idx, array) {
return array.indexOf(pic) === idx;
});
}
From there I filter an ng-repeat to display pictures based on the checkbox selection, and/or a search field which works well also.
input.form-control.input-med(placeholder="Search" type='text', ng-model="search.searchable")
br
div.light.imageItem(data-ng-repeat="picture in filtered=( mongoController.allData | filter:search | filter:mongoController.filterByCategory)")
a(data-toggle="modal", data-target="#myModal", data-backdrop="static", ng-click='mongoController.selectPicture(picture)')
img( ng-src='../images/{{picture.image}}_tn.jpg', alt='Photo of {{picture.image}}')
p Number of results: {{filtered.length}}
Functions to display picture lists:
//Returns pictures of checkboxes || calls the noFilter function to show all
mongoController.filterByCategory = function (picture) {
return mongoController.filter[picture.keyword] || noFilter(mongoController.filter);
};
function noFilter(filterObj) {
for (var key in filterObj) {
if (filterObj[key]) {
return false;
}
}
return true;
}
If you click one of the pictures a modal box pops up with all of the input fields where you can edit image specific fields.
The part I am really struggling with is how to gather the usable data from the filtered ng-repeat to use in the controller so when the modal box is up I can scroll right or left through the other pictures that met the criteria of the search.
Any suggestions would help, even why the hell are you doing it this way?
When you declare the ng-repeat that filters the pictures, your filtered variable belongs to the current $scope (which I cannot infer from the question, as it stands). You could associate the filtered variable to a specific controller by using Controller As syntax: (i.e. using <elem ng-repeat="picture in ctrl.filtered = (ctrl.data | filter1 | filterN)"/>)
Example: (also in jsfiddle)
var mod = angular.module('myapp', []);
mod.controller('ctrl', function() {
var vm = this;
vm.data = ['alice', 'bob', 'carol', 'david', 'ema'];
vm.onbutton = function() {
// access here the filtered data that mets the criteria of the search.
console.log(vm.filtered);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="ctrl as vm">
<input ng-model="search" type="text" />
<p data-ng-repeat="item in vm.filtered = (vm.data | filter:search)">
{{item}}
</p>
<p>Items: {{vm.filtered.length}}</p>
<button ng-click="vm.onbutton()">Show me in console</button>
</div>

Selecting Select Box Option from Controller

I did a lot of searching and tried eleventy-billion different Google search combinations, but all I can find on this issue is how to set a default option in a select box.
I have a page where an admin can select a user from a list of users, and then Angular JS grabs the user_id of that user (using ng-change), sends it to the DB via POST, and then the goal is to change the value of the other inputs to the values from the DB. I have the values, but running into a hitch when using that value to get my state select box to change to the user's state.
The JS in question:
$scope.getUserInfo = function(user_id) {
this.user_id = user_id;
$http.post("lib/scripts/managing_user.php", {func: 'retrieve_user_info', user_id: this.user_id}).
success(function(data) {
console.log(data);
$scope.is_active = data['0']['active'];
//Interacts with the ng-checked directive. It takes a bool, and data returns 0 or 1 in active.
$scope.username = data['0']['username'];
//Assigns the data value to the ng-model directive with the value username
//Have to treat data as a 2D array because that is how it is returned
$scope.email = data['0']['email'];
$scope.fName = data['0']['first_name'];
$scope.lName = data['0']['last_name'];
$scope.schoolList = data['0']['school_id']; (<-Does not work)
}).
I accomplished the same thing using jQuery before. Here is the code if it helps you see what I want to do.
if ($("#school").children("option:selected"))
$("#school").children("option:selected").attr("selected", "false");
$("#school #" + this['school_id'] + "").attr("selected", "true");
Here is the Select Box that I want changed.
<div class="row-fluid">
<span class="adduser_heading">School:</span>
<select id="school" class="adduser_input" ng-model="user.schoolList" ng-options="name.school_name for (key, name) in schoolList" ng-disabled="is_disabled" name="school" style="width: 246px;"></select>
</div>
I get the list of schools from the DB, and that populates that list. After selecting a user, I want this select box to change to that user's school. The ultimate goal is for the admin to be able to change the selected school and submit it, changing the school in the DB.
Hope I described the problem adequately. Basically: I want to select an option in a select box from the JS using Angular JS.
Edit: As per the advice of oware, I created a function that gets just the school name from the object array and returns it to $scope.user.schoolList. Unfortunately, that did not work.
$scope.user.schoolList = $scope.findInSchoolList(data['0']['school_id']);
$scope.findInSchoolList = function(school_id) {
var school_id = school_id;
var school;
school = $scope.schoolList[school_id];
school = school['school_name'];
return school;
};
And here is the format of what is returned from the DB with regards to school. I don't really want to post "data" since that has the information of an actual person. Basically, the information with regards to school is what is below.
school_id: "106"
school_name: "Central Campus High School"
Your ng-model is set to user.schoolList, while you're assigning the default value to $scope.schoolList.
It should be $scope.user.schoolList instead.
If you want to use the find function, you still need to return the right object, not just the name; and you need to fix your function. So something like this should work:
$scope.findInSchoolList = function(school_id) {
for(var i = 0; i < $scope.schoolList.length; i++) {
if ($scope.schoolList[i].school_id == school_id) {
return $scope.schoolList[i];
}
}
};
Here's a working example:
angular.module('app', [])
.controller('Ctrl', function($scope) {
var findInSchoolList = function(school_id) {
for (var i = 0; i < $scope.schoolList.length; i++) {
if ($scope.schoolList[i].school_id == school_id) {
return $scope.schoolList[i];
}
}
};
$scope.schoolList = [{
school_id: "1",
school_name: "Central Campus High School"
}, {
school_id: "106",
school_name: "Another High School"
}, {
school_id: "231",
school_name: "Yet Another High School"
}, {
school_id: "23",
school_name: "The Correct High School"
}, {
school_id: "2",
school_name: "Last High School"
}]
$scope.user = {
schoolList: findInSchoolList(23)
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="row-fluid" ng-app="app" ng-controller="Ctrl">
<span class="adduser_heading">School:</span>
<select id="school" class="adduser_input" ng-model="user.schoolList" ng-options="name.school_name for (key, name) in schoolList" ng-disabled="is_disabled" name="school" style="width: 246px;"></select>
</div>
you have to select the item from the array that populated the list, for example, if you have:
$scope.school_list = [{id:1, name:'harvard'}, {id:2, name:'other'}]
and you want to select with:
$scope.user.schoolList = {id:1, name:'harvard'}
it won't work, you have to make a fucntion that finds the element in the array and then assign it to the $scope.user.schoolList variable (that is bound to the ng-model of your list)
you have to do something like this:
$scope.user.schoolList = findInSchoolList({id:1, name:'harvard'})
and it will select the item from the select list
hope it helps

AngularUI-Bootstrap Typeahead: Grouping results

I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a sample scenario
There are some users in the database, each user has a "Department". One user name can be available in multiple departments.
The end-user types in the names to search users from the database and retrieves the list in the typeahead list. Since one user name can belong to multiple departments, the requirement is to show the user names grouped by different departments. Something like this:
Then the user can select the desired user name and proceed.
As per the Typeahead documentation present here, I don't see any option to cater to my requirement.
I have tried the this workaround: Whenever the typeahead array is getting formed, I appended the user department to the array element:
$scope.fetchUsers = function(val) {
console.log("Entered fetchUsers function");
return $http.get("http://localhost:8080/TestWeb/users", {
params : {
username : val
}
}).then(function(res) {
console.log("Response:",res);
var users = [];
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
console.log("users=",users);
return users;
});
};
This way, at least the end user sees the department. But when I select the record, the selected value is the full content of the array element. Below is sample screenshot to elaborate:
HTML
Users from local service
<pre>Model: {{userList | json}}</pre>
<input type="text" ng-model="userList" placeholder="Users loaded from local database"
typeahead="username for username in fetchUsers($viewValue)"
typeahead-loading="loadingUsers" class="form-control">
<i ng-show="loadingUsers" class="glyphicon glyphicon-refresh"></i>
User types in the string
User selects one record
I want to avoid the department (in this case, string - Desc 4 ) when user selects a record.
Is there any way I can achieve this grouping without any workaround? Or is there any way I can enhance my workaround?
I used to have a similar requirement and here is how I did it that time.
Example Plunker: http://plnkr.co/edit/zujdouvB4bz7tFX8HaNu?p=preview
The trick is to set the typeahead-template-url to a custom item template:
<input type="text" class="form-control" placeholder="Users loaded from local database"
ng-model="selectedUser"
typeahead="user as user.name for user in getUsers($viewValue)"
typeahead-template-url="typeahead-item.html" />
The item template, this represent each item in a dropdown:
<div class="typeahead-group-header" ng-if="match.model.firstInGroup">Desc {{match.model.group}}</div>
<a>
<span ng-bind-html="match.label | typeaheadHighlight:query"></span>
</a>
As you can see, there is an ng-if to show a group header if that item has a property firstInGroup set to true.
The firstInGroup properties are populated like this using lodashjs:
$scope.getUsers = function (search) {
var filtered = filterFilter(users, search);
var results = _(filtered)
.groupBy('group')
.map(function (g) {
g[0].firstInGroup = true; // the first item in each group
return g;
})
.flatten()
.value();
return results;
}
Hope this fit to your requirement too.
please see here http://plnkr.co/edit/DmoEWzAUHGEXuHILLPBp?p=preview
instead of creating new objects here:
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
use create template :
<script type="text/ng-template" id="customTemplate.html">
<a> {{ match.model.name}} - department : {{match.model.dept}}</a>
</script>
and use it in your Typeahead directive
<input type="text" ng-model="selected"
typeahead="user.name as user for user in users | filter:$viewValue | limitTo:8" class="form-control"
typeahead-template-url="customTemplate.html">

Categories

Resources