Some troubleshoot to writing custom filter - javascript

I use angularjs in my project.
I want to filter my array in ng-repeat.
Here is HTML:
<tbody>
<tr ng-repeat="sensorData in list.sensorsData |
filterByAlert:list.alertTags">
<td>
<div class="container-fluid">
<div class="row">
<div class="text-center">{{ sensorData.Area }}</div>
</div>
</div>
</td>
</tr>
</tbody>
Here is how looks list.sensorsData array of objects:
Here is how looks list.alertTags array of objects:
I need to show in ng-repeat elements only list.sensorsData rows that has alertType properties value equal to Id proiperty in list.alertTags.
for this purpose I wrote this filter:
(function () {
"use strict";
angular.module("sensorsData").filter('filterByAlert', filterByAlert);
function filterByAlert() {
var result = [];
return function (sensorRecords, alertTypes) {
if (!sensorRecords)
return;
if (!alertTypes)
return;
angular.forEach(sensorRecords, function (sensorRecord, key) {
angular.forEach(alertTypes, function (alertType, key2) {
if (sensorRecord.AlertType == alertType.Id ) {
result.push(sensorRecord);
}
})
});
return result;
}
};
})();
but when filter is fired I get this error:
angular.js:13424 Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: sensorData in list.sensorsData |
filter:filterByAddress |
orderBy:sortType:sortReverse |
filterByAlert:list.alertTags, Duplicate key: object:8, Duplicate value: {"Id":8,"Address":"125 king g.str","AlertType":3,"Area":"North","MeasureDate":"2012-10-12T16:10:00","MeasureValue":-1}
It seems I have problem with logic in my custom filter.
Any idea why I get the error?And how to fix it?

Add track by $index to your ng-repeat.
<tr ng-repeat="sensorData in list.sensorsData | filterByAlert:list.alertTags track by $index">

Related

ngFor showing the last element only from array

Trying to loop over an array and display the results, but only the last element showing multiple times.
Here is my code.
Making a get request.
showItems(id: any): Observable<any> {
return this.http.get(`${this.url}${id}`)
}
Console logging works fine here, I can see the expected results.
ngAfterViewInit(): void {
this.showItems()
}
showItems(): void {
const id: any = []
this.idFromList.forEach((val: any) => id.push(val.nativeElement.innerText))
for(let i = 0; id.length > i; i++) {
this.ItemService.showItems(id[i]).subscribe(data => {this.api_items = data.item.current
console.log(this.api_items)});
}
}
HTML
<table>
<thead>
<tr>
<td>Name</td>
<td>Price</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items | filter: searchQuery" >
<td >
<a href="" #btn>{{item.id}}</a>
{{ item.name }}
</td>
<td *ngFor="let api_item of api_items | keyvalue">
{{ api_item.value }}
</td>
</tr>
</tbody>
</table>
Tried using fake JSON server, same results.
1) Quick answer
Push items in an array instead of replacing its value :
api_items = [];
this.ItemService.showItems(id[i]).subscribe(data => {
this.api_items.push(data.item.current);
});
2) Complete answer
As #MoxxiManagarm said, you're replacing api_items value at each iteration of your for loop.
You might want to check & use RxJS to handle multiple observables in a cleaner way: for example, by generating an array of Observable to resolve them together.
3) Ressources & useful links you might want to check for more information
https://www.learnrxjs.io/

How to sort by date in Javascript/Angular {{}}

Say I have this piece of code:
<tr ng-repeat-start="caseHistory in vm.caseHistory">
<td >{{caseHistory.date | date:"dd-MMM-yyyy h:mm a"}}</td>
<td >{{caseHistory.description}}</td>
<td >{{caseHistory.performedByUser.firstName}} {{caseHistory.performedByUser.lastName}} ({{caseHistory.performedByUser.externalAssociateId}})</td>
<td>
<span ng-show="vm.showCreateCaseComments(caseHistory.eventType,caseHistory.comments)">
<a ng-if="caseHistory.expanded" class="removeTextDecoration headerPointer" ng-click="caseHistory.expanded = false"> -Collapse</a>
<a ng-if="!caseHistory.expanded" class="removeTextDecoration headerPointer" ng-click="caseHistory.expanded = true"> +Expand</a>
</span>
</td>
</tr>
I would like it so that everything that is shown to be sorted by ascending chronological date.
What would I need to add in {{caseHistory.date | date:"dd-MMM-yyyy h:mm a"}} in order to sort all the results in by the date?
You need to add a pipe here:
<tr ng-repeat-start="caseHistory in vm.caseHistory | orderBy:'caseHistory.date'">
You could also add the '-' symbol to specify ascending vs descending.
Documentation can be found here:
https://docs.angularjs.org/api/ng/filter/orderBy
Use orderBy filter and add the reverse option for descending
<tr ng-repeat-start="caseHistory in vm.caseHistory | orderBy: 'date':true">
Or add a - in front of field name
<tr ng-repeat-start="caseHistory in vm.caseHistory | orderBy: '-date'">
According to Angular Official Tutorial pipe or filter should not be used for sorting purpose.
You can write your custom method to perform this task like :
Main.prototype = {
sort: function(item) {
if (this.predicate == 'date') {
return new Date(item.date);
}
return item[this.predicate];
},
sortBy: function(field) {
if (this.predicate != field) {
this.predicate = field;
this.reverse = false;
} else {
this.reverse = !this.reverse;
}
},
reverse: false};
Please refer following jsfiddle for reference.

Angularjs dynamic looping to access dynamic values of array keys

I am generating following json data from my database where keys like sid and name is static but task1,task2,task3 .... are dynamic and they can be upto value n(where n=1,2,3......)
My data:
[
{"sid":"10","name":"nam1","task1" :"1","task2" :"0","task3" :"1","task4" :"0","task5" :"0"},
{"sid":"20","name":"nam2","task1" :"0","task2" :"1","task3" :"0","task4" :"1","task5" :"1"},
{"sid":"30","name":"nam3","task1" :"1","task2" :"1","task3" :"0","task4" :"0","task5" :"1"},
{"sid":"40","name":"nam4","task1" :"0","task2" :"0","task3" :"0","task4" :"0","task5" :"1"}
]
app.js
app.controller('taskCrtl', function ($scope, $http, $timeout) {
$http.get('get.php').success(function(data){
$scope.list = data;
$scope.currentPage = 1; //current page
$scope.entryLimit = 5; //max no of items to display in a page
$scope.filteredItems = $scope.list.length; //Initially for no filter
$scope.totalItems = $scope.list.length;
});
Now i am trying to access key values using ng-repeat like below:
<tr ng-repeat="data in list| unique: 'sid'">
<td>{{data.sid }}</td>
<td>{{data.name}}</td>
<td>{{data.task1}}</td>
<td>{{data.task2}}</td>
<td>{{data.task3}}</td>
<td>{{data.task4}}</td>
<td>{{data.task5}}</td>
<tr/>
The above code works well for fixed keys like task1,task2,task3... but i am generating "task" key dynamically so it will be always unknown to me that how many task keys are there.There may be a case when there are keys from task1 to task100 so the above code will fail on that case.
I am trying to dynamically access the task keys to generate associate data.To achieve this i am concatenating a number( produced by an incremental loop) with the task key so that i becomes like task1,task2 .... value n
<tr ng-repeat="data in list| unique: 'sid'">
<td> {{ data.sid }} </td>
<td> {{ data.name }} </td>
<td ng-repeat="data in list| limitTo:n">
{{ id=$index+1; 'data.task'+id}}
</td>
</tr>
Id starts with 1 and incremented in every loop so that we can dynamically access data.task1,data.task2 and so on
I am facing two problems here.After concatenation i was supposed to get the value of a corresponding task key but i am getting like this data.task1,data.task2,data.task3 that is i am getting keys itself instead of key values.
The second problem is i want to loop up to n number of times so i am limiting the ng-repeat by n like this way ng-repeat="data in list| limitTo:n" but the value of n is unknown to me.Here n should be equal to the highest number of dynamic task key(or total task keys).
For example if in the data list there exist task1,task2,task3,task4,task5 then n will be 5 since there are total 5 task keys.If there exist task1 to task100 then n will be 100 as there are total 100 task keys.
One idea to achieve this could be counting total unique keys from the array and then subtract 2 static keys from it to get the number of dynamic keys which will be used as value of n to limit the loop.
For example in above data set the unique keys are sid,name,task1,task2,task3,task4,task5 if somehow we can manage to calculate total number of unique keys from the array then the total number of unique keys will be 7 in this case, and if we subtract 2 from the number 7 then we have 5 which is equal to the number of dynamic keys in the array, that we want to use as n to limit the loop.
Any idea how to solve my above problems using angularjs ?
I assume that the total number of task attributes on an Item may be varied.
By returning the list of Keys which start with Task you can iterate over that list instead and making it a function, you can call that during each iteration of ngRepeat:
$scope.getTaskKeys = function(row) {
return Object.keys(row).filter(function(keyname) {
return keyname.startsWith('task');
});
Leaving you free to call it as the collection for the second iteration:
<tr ng-repeat="data in list| unique: 'sid'">
<td> {{ data.sid }} </td>
<td> {{ data.name }} </td>
<td ng-repeat="taskKey in getTaskKeys(data)">
{{data[taskKey]}}
</td>
</tr>
Created a working example here: https://jsfiddle.net/zd1yjc0m/
Try this it will work as per your expectation..
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope) {
$scope.list = [
{"sid":"10","name":"nam1","task1" :"1","task2" :"0","task3" :"1","task4" :"0","task5" :"0"},
{"sid":"20","name":"nam2","task1" :"0","task2" :"1","task3" :"0","task4" :"1","task5" :"1"},
{"sid":"30","name":"nam3","task1" :"1","task2" :"1","task3" :"0","task4" :"0","task5" :"1"},
{"sid":"40","name":"nam4","task1" :"0","task2" :"0","task3" :"0","task4" :"0","task5" :"1"}
];
for (var i in $scope.list) {
var res = Object.keys($scope.list[i]).filter(item => { return item.indexOf("task") > -1; });
$scope.tasksList = res;
}
});
table,td {
border: 1px solid black;
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<table>
<tr ng-repeat="data in list">
<td> {{ data.sid }} </td>
<td> {{ data.name }} </td>
<td ng-repeat="item in tasksList"> {{ data[item] }} </td>
</tr>
</table>
</div>
data[0] contain the JSON data and data[1] contain dynamic key
<tr ng-repeat="x in data[0] ">
<td ng-repeat="j in data[1]">
[[ x[j] ]]
</td>
</tr>

orderBy not working with pagination and filters

<table>
<thead>
<tr>
<th class="col-md-3" ng-click="sortDirection = !sortDirection">Created At</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="food in foods | filter:foodFilter | itemsPerPage:pageSize | orderBy:'created_at_date'">
<td class="col-md-"> {{food.created_at_date}} </td>
</tbody>
</table>
<dir-pagination-controls
max-size= 7
boundary-links="true">
</dir-pagination-controls>
This is only a snippet of my code but its too large to put up. Everything is working except only some of the created_at_date is in order. When I click on a different filter to add in or remove data depending on that filter, only some of it is entered into the correct place. My main question is: is there someway to sort all of the dates properly while still allowing the everything else function as well? All help is welcome, Thanks
(function () {
"use strict";
App.controller('foodsController', ['$scope'],
function($scope) {
$scope.sortDirection = true;
In your controller you can add the method to order the array before you loop over them.
Assuming your foods array has an array of objects, each with a key of created_at_date and a value:
App.controller('foodsController', function($scope) {
$scope.foods = [{
created_at_date: 6791234
}, {
created_at_date: 9837245
}, {
created_at_date: 1234755
}];
// create a method exposed to the scope for your template.
$scope.orderBy = function(key, array) {
// now you've received the array, you can sort it on the key in question.
var sorted = array.sort(function(a, b) {
return a[key] - b[key];
});
return sorted;
}
});
Now on your template, you have a method available to sort your values for you:
<table>
<thead>
<tr>
<th class="col-md-3" ng-click="sortDirection = !sortDirection">Created At</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="food in orderBy('created_at_date', foods) | filter:foodFilter | itemsPerPage:pageSize">
<td class="col-md-"> {{food.created_at_date}} </td>
</tr>
</tbody>
</table>
The orderBy method which we've created on your controller returns an array, but it's just sorted by the key that's sent in as the first argument of the function. The second argument is the original array you're trying to sort.
At least this way you can check if you remove all your other filters to see if it's ordered correctly, if then after you add them back in it changes it's because those filters are also changing the order.

ng-repeat run as many times as integer parameter doesn't work in some cases

I want to run angular as many times as integer value passed to it.
EDIT: I simplified this example because originally I used function to return array based on number passed to it.
HTML
<body ng-app="userFilterModule">
<div class="container-fluid" ng-controller="UserfilterController as Ctrl">
<div class="row">
<div class="filter_tableTbody col-xs-12">
<div class="row filter_tableRow" ng-repeat="user in Ctrl.obj_users">
<!-- ... -->
<div class="col-xs-2">
<span class="filter_rateStars" ng-repeat="a in Ctrl.ratingArr| limitTo: user.rate">
★
</span>
<span class="filter_rateStars notActive" ng-repeat="a in Ctrl.ratingArr| limitTo: 5 - user.rate">
☆
</span>
</div>
</div>
</div>
</div>
</div>
</body>
And everything works fine if ratingArr contains numbers e.g.
app.controller('UserfilterController', function ($scope) {
this.int_male_counter = this.int_female_counter = 5;
this.str_sort_by = {
prop_name: 'f_name',
order: 'asc'
};
//problem starts here
this.ratingArr = [1,2,3,4,5];
this.obj_users = new Users(this.int_male_counter, this.int_female_counter).list;
this.fn_set_sorting = function (str) {
if (this.str_sort_by.prop_name === str) {
this.str_sort_by.order = this.str_sort_by.order === 'des' ? 'asc' : 'des';
} else {
this.str_sort_by.order = 'asc';
this.str_sort_by.prop_name = str;
}
this.obj_users.sortByObjKeyVal(this.str_sort_by.prop_name, this.str_sort_by.order);
};
this.fn_setlected_filter = function (str) {
return str === this.str_sort_by.prop_name;
};
this.fn_is_descending = function(){
return this.str_sort_by.order === 'des';
};
});
But when I change it to new Array(5) or ['','','','',''] or ['a','a','a','a','a']
I get error in console: Error: [ngRepeat:dupes] why?
Thanks!
By default, you cannot use identical values in the array processed by ng-repeat. Quoting the docs:
error:dupes
Duplicate Key in Repeater
Occurs if there are duplicate keys in an ngRepeat expression.
Duplicate keys are banned because AngularJS uses keys to associate DOM
nodes with items.
By default, collections are keyed by reference which is desirable for
most common models but can be problematic for primitive types that are
interned (share references).
As advised in the same docs, just use track by $index suffix (so that items will be keyed by their position in the array instead of their value) to resolve the issue:
<span class="filter_rateStars"
ng-repeat="a in Ctrl.ratingArr | limitTo: user.rate track by $index">

Categories

Resources