Binding select on filtered model - angularjs

Please forgive the title. I couldn't figure out how best to describe what I'm looking for.
My JSON looks something like this:
AvailableColumns: [
{ ColumnName: "CustomerNumber", ... },
{ ColumnName: "CustomerFirstName", ... }
],
AvailableSummaryColumns: [
{
ColumnName: "CustomerNumber",
CalculationTypes: [
{ label: "Count", id: 1 },
{ label: "Count Distinct", id: 2 }
]
},
{
ColumnName: "CustomerFirstName",
CalculationTypes: [
{ label: "Count", id: 1 },
{ label: "Count Distinct", id: 2 }
]
}
]
I have a directive that walks through the parent of this object and binds to the data inside, no problem. The issue I'm having, though, is that I need to bind ng-options of a select to a filtered object by ColumnName.
Within my ng-repeat, I'm rendering a select housing the CalculationTypes for each column. I need my ng-options to be set to each of the CalculationTypes where AvailableSummaryColumns.ColumnName is equal to ColumnName of the current AvailableColumns object in my ng-repeat.
I've tried everything I know to try, but just can't get this bound properly. What's the best way to achieve this?
ng-repeat:
<tr ng-repeat="column in AvailableColumns track by $index | orderBy: 'ColumnOrder'" data-column-name="{{column.ColumnName}}" data-ref="{{$index}}">
Within each row, I need the ng-model or ng-options of a selectto be bound to AvailableSummaryColumns.CalculationTypes where AvailableSummaryColumns.ColumnName is equal to AvailableColumns.ColumnName.

Try this:
<tr ng-repeat="column in AvailableColumns track by $index | orderBy: 'ColumnOrder'" data-column-name="{{column.ColumnName}}" data-ref="{{$index}}">
<td><div ng-repeat="sumary in AvailableSummaryColumns track by $index">
<div ng-if="sumary.ColumnName === column.ColumnName">
<select ng-options="type.label for type in AvailableSummaryColumns. CalculationTypes"></select>
</div>
</div>
</td>
</tr>

Related

Using ng-repeat print table in AngularJS with Filter

I want to use ng-repeat directive in AngularJS with customized filter to print a table.
Assume that I have data as following
[
{
"Key":{
"Name":"Paul",
"Age":"18"
},
"Info":{
"Gender":"M"
}
},
{
"Key":{
"Name":"John",
"Age":"19"
},
"Info":{
"Gender":"M"
}
},
{
"Key":{
"Name":"Jane",
"Age":"17"
},
"Info":{
"Gender":"F"
}
}
]
By using ng-repeat and filter, I hope that I could filtred by Name or other options.
So I tried:
<div>
<tr ng-repeat="x in data | myFilter: filterText">
<td>{{x.Key.Name}}</td>
<td>{{x.Key.Age}}</td>
</tr>
</div>
My script for customizing filter here:
myApp.filter("myFilter",function(){
return function(input, filterText){
if(input.Key.Name == filterText){
return input;
}
}
})
I keep receiving error from console that Name is undefined. I same have problem that accessing Javascript of Array of Object. I have set about JSON file input $scope.data.
filterText would be filter keywords for Name.
First of all, table tags are missing in your template. You can filter with nested property 'Name' without using custom filter like this:
<table>
<tr ng-repeat="x in data | filter: { Key: {Name: filterText} }">
<td>{{x.Key.Name}}</td>
<td>{{x.Key.Age}}</td>
</tr>
</table>
Check the working example : jsfiddle

Filter ng-repeat table with multiple filter values for one column

I've generated a dynamic table from array of objects and paginated the rows with angular-ui-bootstrap. I also need to implement row filter functionality in table based on user selections. For example, I would select one or more Cities from check-boxes and only the rows with matching Cities should be shown, or user will select certain names, or a combination of both. Below is sample data:
ctrl.data = [{
id: 1,
name: 'Ram Kumar',
city: 'Delhi',
date: new Date("October 13, 2014 11:13:00")
}, {
id: 2,
name: 'Raman Kumar',
city: 'Mumbai',
date: new Date("November 13, 2014 11:13:00")
}, {
id: 3,
name: 'Raghav Kumar',
city: 'Chennai',
date: new Date("December 13, 2014 11:13:00")
}];
I've also created a metadata which will contain information about all properties of data such as property name, it's unique values, the kind of filter which this property would require i.e. check-box or a slider. Below is the metadata:
ctrl.metadata = [{
name: "ID",
values: [1, 2, 3],
type: "checkbox",
showFilter: false
}, {
name: "Name",
type: "checkbox",
values: ["Ram Kumar", "Raman Kumar", "Raghav Kumar"],
showFilter: false
}, {
name: "City",
type: "checkbox",
values: ["Delhi", "Mumbai", "Chennai"],
showFilter: true
}, {
name: "Birth Date",
type: "date",
showFilter: false
}, {
name: "Age",
type: "slider",
values: [18,26],
showFilter: false
}];
I've currently made a one-value static filter query variable for some attributes such as ID, Name and City.
<div ng-repeat="attr in metadata">
<div ng-if="attr.name === 'ID'">
Enter Filter:
<input type="textbox" ng-model="search.id" />
</div>
<div ng-if="attr.name === 'City'">
Enter Filter:
<input type="textbox" ng-model="search.city" />
</div>
<div>
And I am applying this filter to table rows in this way:
<tr ng-repeat="row in data | filter: search | pages: currentPage : itemsPerPage">
<td ng-repeat="item in row">{{ item }}</td>
</tr>
But firstly, I want the filter query variable (search) to be dynamically generated, as the data and metadata might change. Something like below if possible:
<div ng-repeat="attr in metadata">
<div ng-if="attr.name === 'ID'">
Enter Filter:
<input type="textbox" ng-model="attr.searchQuery" />
</div>
</div>
I attached the search query to metadata object property called searchQuery. But when I am trying to use it in filter, I see no result:
<tr ng-repeat="row in data | filter: metadata[$index].searchQuery | pages: vm.currentPage : vm.itemsPerPage">
<td ng-repeat="item in row">{{ item }}</td>
</tr>
Secondly, the search query can take only one string, but as I want the filter to be based on more than one value, how can I achieve it? I've researched about custom filter but the problem is, how do I 'record' the selected values, and then send it to the filter (for example, get all the selected cities from check-boxes) ? I have to make every variable dynamic or part of metadata so that it does not break when either number of properties, or name of properties or values are changed. There might be no Cities but States when data is changed, so can't make anything static.
Please suggest some ideas for implementation.
Here is a working plunker.
First you should change your <input> to:
<input type="checkbox" ng-model="vm.selected[$index]" ng-true-value="'{{value}}'" ng-false-value="" /> {{value}}
Then your ng-repeat:
<tr ng-repeat="row in vm.data | filter: vm.search | customFilter: vm.selected | pages: vm.currentPage : vm.itemsPerPage">
And finally you can implement a custom filter to achieve what you want, like this:
.filter('customFilter', function() {
return function(items, search) {
if (!search || (Object.keys(JSON.parse(JSON.stringify(search))).length === 0 && search.constructor === Object)) {
return items;
}
var selected = Object.keys(search).map(function(key) {
return search[key]
});
items = items.filter(function(value) {
return selected.indexOf(value.city) != -1;
});
return items;
}
});
Check the complete code
I hope it helps.
Tip: I don't know if you're using these things of ui-bootstrap in your real project, but they're deprecated, as you can see in console:
PaginationController is now deprecated. Use UibPaginationController
instead. pagination is now deprecated. Use uib-pagination instead.

Ng-repeat inside ng-repeat

<ul ng-repeat="cate in restaurant.categories"><li>{{cate}}</li>
<li ng-repeat="menuItem in restaurant.menuItems" ng-show="menuItem.category == cate">{{menuItem.name}}</li></ul>
I want one ng-repeat loop inside another and to show the menu only if the menuItem is in the category. I only have items in the first category loop, and empty for all the other categories.
Categories and menuItem are 2 different arrays. If the menuItem's category is under the current category it should be added to the page.
menuItems = {{name: dish1, category:soup},
{name: dish2, category:beef}}
categories = {beef, soup}
#show-me-the-code : Bill Bi has two different array. So the best option to achieve this is by filter in inside loop as stated in my comment.
Here is the final code with filter for inside loop. I am including fiddler for quick reference.
<div ng-app ng-controller="testCtrl">
<ul ng-repeat="cate in categories">
<li>{{cate}}</li>
<li ng-repeat="menuItem in menuItems | filter:{category: cate}">{{menuItem.name}}</li>
</ul>
</div>
function testCtrl($scope) {
$scope.menuItems = [{name: 'dish1', category:'soup'},
{name: 'dish2', category:'beef'}];
$scope.categories = ['beef', 'soup']
}
Fiddle : JSFiddle
I would change my data representation to match what you are actually trying to display like so:
$scope.restaurant = {
categories: [{
name: "beef",
menuItems: [{
name: "dish1",
"price": "$10"
}, {
name: "dish2",
"price": "$15"
}]
}, {
name: "soup",
menuItems: [{
name: "dish1",
"price": "$20"
}, {
name: "dish2",
"price": "$25"
}]
}]
};
This way you could easily match your two nested loops like this:
<div ng-app ng-controller="testCtrl">
<ul ng-repeat="cate in restaurant.categories">
<li>{{cate.name}}</li>
<li ng-repeat="menuItem in cate.menuItems">{{menuItem.name}} - {{menuItem.price}}</li>
</ul>
</div>
Check out this fiddle if you would like to see it in action.
If you need to stick to your JSON data, you will have to do filtering to pull the contents you want to display.

deleting an element in ng-repeat with orderBy and track by is failing

Here is the code
<tr ng-repeat="collection in collections | orderBy:'-modifiedDate' track by $index" ng-init="listIndex = $index">
If I remove orderBy:'-modifiedDate', the deletion on a specific element is working great. However, I need the collection/array to be rendered in sorted way that's why I have orderBy.
If I don't delete the orderBy:'-modifiedDate' from the code and I delete a random element say on position 7, the element that gets deleted is the very last always.
I had to use ng-init since I have another ng-repeat inside the ng-repeat shown above. I call the delete function like this, ng-click="deleteRow(listIndex)"
alert listIndex in the deleteRow method, if it is coming correct as 7, use splice(listIndex,1) to do the deletion.
This is how I got it to work.
in the template
<tr ng-repeat="collection in collections| orderBy:'-modifiedDate'">
........
... there is another ng-repeat here
<a ng-click="deleteItem(collection)">Delete this item</a>
in the controller
$scope.deleteItem = function(item) {
$scope.collections.splice($scope.collections.indexOf(item), 1);
}
Or, even more simple:
$scope.deleteItem = function(array, index){
array.splice(index, 1);
};
And in your html:
<tr ng-repeat="collection in collections| orderBy:'-modifiedDate'">
..........
..........
<a ng-click="deleteItem(collections, $index)">Delete this item</a>
When you use orderBy, angular creates another "collections" in the scope, and changes the order of its elements, but the original "collections" stays the same, for example if :
$scope.collections = [
{ name: '1st element', id: '1' },
{ name: '2nd element', id: '2' },
{ name: '3rd element', id: '3' }
];
<tr ng-repeat="collection in collections | orderBy:'id':true track by $index">
$scope.collections[2] would still be { name: '3rd element', id: '3' }
Here's what you can do, you can give a name to that sorted collection created by angular :
<tr ng-repeat="collection in sortedCollections = (collections | orderBy:'id':true) track by $index">
And then you could use $scope.sortedCollections[2] instead

Filtering on object properties not working

I'm trying to filter within an ng-repeat like this:
<li ng-repeat="file in files | filter: { values.filetype: 'Form' }">
If I change values.filetype: 'Form' to, say, id: 1, the filter works correctly. So how do I get it to work with the first property?
Edit: the structure of the data is like this:
{
"id": "3",
"values":
"title: "sldkfjsd",
"filetype": "Form"
}
This is solved with the following syntax:
filter: { values: { filetype: 'Form'} }

Resources