AngularJS sorting by time difference - javascript

I want to enable sorting by each of my table fields. I have one column which shows how many minutes it took to work on project, but sorting by this field doesn't work properly.
<table>
<th >Company</th>
<th>Time difference<th />
<tr ng-repeat="task in tasks | orderBy:sortBy">
<td >{[{ task.company_name }]}</td>
<td >{[{ timediff(task.time_start,task.time_stop) }]}</td>
</tr>
</table>
timediff function:
$scope.timediff = function(start, end){
var start = moment(start);
var end = moment(end);
var diff = end.diff(start,'minutes',true);
return (diff/60).toPrecision(3);
};
Plunker: http://plnkr.co/edit/vdkfNkgpdLUp9RgZ1IvO?p=preview

Simple fix, just need to assign the function to a parameter
<tr>
<th>Company</th>
<th>Time difference</th>
</tr>
<tr ng-repeat="task in tasks | orderBy:sortBy">
<td>{{ task.company_name }}</td>
<td>{{ task.timediff = timediff(task.time_start,task.time_stop) }}</td>
</tr>
Here is an updated plunker showing this as well as adding a reverse sort.

There is a simpler way for the custom function to work. I edited the dataset a bit to make the switch between "Company" and "Time difference" a bit more clear.
Option 1 (DEMO):
If the property names don't change you can do the following :
$scope.timediff = function(task){
var start = moment(task.time_start);
var end = moment(task.time_stop);
var diff = end.diff(start,'minutes',true);
return (diff/60).toPrecision(3);
};
And in your html assign the function to your sortBy variable:
<th >Company</th>
<th>Time difference</th>
<tr ng-repeat="task in tasks | orderBy:sortBy">
<td >{{ task.company_name }}</td>
<td >{{ timediff(task)}}</td>
</tr>
Angular automatically passes the current item into the function defined in orderBy.
Option 2 (more flexible) (DEMO):
If you want to define the property names on the fly you can return another function:
$scope.timediff = function(name1, name2){
return function(item) {
var start = moment(item[name1]);
var end = moment(item[name2]);
var diff = end.diff(start,'minutes',true);
return (diff/60).toPrecision(3);
}
};
And give it the two property names:
<th >Company</th>
<th>Time difference</th>
<tr ng-repeat="task in tasks | orderBy:sortBy">
<td >{{ task.company_name }}</td>
<td >{{ timediff('time_start', 'time_stop')(task)}}</td>
</tr>

Related

how to get the result of two controller on ng-repeat

I want to get the values of the two object in thesame ng-repeat
$http.get('/api/PreviewPayroll').success(function (data){
//alert(data[0].empID);
$scope.allowance = data;
});
$http.get('/api/Deduction').success(function (data){
//alert(data[0].empID);
$scope.Deduction = data;
});
<tr ng-repeat="item in allowance && ng-repeat="value in Deduction">
<td>{{ item.empID }}</td>
<td>{{ value.empID }}</td>
</tr>
how can I get the two scope object on thesame ng-repeat
So you will want to combine the data.
You can use $q.all(promises):
var promise1 = $http.get('/api/PreviewPayroll');
var promise2 = $http.get('/api/Deduction');
$q.all([promise1, promise2]).then(function (results) {
var allowances = results[0];
var deductions = results[1];
var combinedList = /* some combination logic */;
});
By using $q.all() you are ensuring you have both lists of data before trying to combine anything. You can easily play around with this to get the desired effect. For example, if you don't care if the other list isn't available.
Then you can use the ng-repeat in order to iterate over that new combined list:
<tr ng-repeat="item in combinedList">
<td>{{ item.allowance.empID }}</td>
<td>{{ item.deduction.empID }}</td>
</tr>
The sub properties allowance and deduction are based on your combined list.
However
It is in my honest opinion that, the server side gives you the data in the format you need to display it in. (i.e. the business logic remains server side in a controlled environment). I believe the view should only deal with view logic, like button actions etc..
But this is my opinion, and is what I find easiest.
Another note
I prefer to also keep the view logic in the JavaScript, hence why I combine the data there. Rather than trying to do some overly complicated angular expression in the HTML.
You could either have a nested ng-repeat and also combine the two objects into one.
<table>
<tbody ng-repeat="row in mainCombinedObject">
<tr>
<th>{{row.empID}}</th>
</tr>
<tr ng-repeat="sub in row.subObject">
<td>{{sub.empID}}</td>
</tr>
</tbody>
</table>
Combine $scope.allowance and $scope.Deduction to one list of objects "combined" then do your ng-repeat:
<tr ng-repeat="c in combined">
<td>{{ c.someField }}</td>
<td>{{ c.someOtherField }}</td>
</tr>
You can't do that in such way! If your allowance and Deduction have the same size you have to mix them in the collection like this:
var array = [
{ allowance: value1, Deduction: value2},
{ allowance: value3, Deduction: value4},
...
];
and them use it in the view:
<tr ng-repeat="item in array">
<td>{{ item.allowance.empID }}</td>
<td>{{ item.Deduction.empID }}</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.

Formatting data before render it

I am displaying some data in the view, but I need to formatted first, I was doing something like
val.toFixed(2) and that is OK, it works but the problem is that val sometimes comes with letters, and toFixed(2) is not taking that into account so is not displaying the letters.
So I need something that takes into account letters and numbers, the letters don't have to change, only the numbers which comes like 234235.345345435, and obviously I need it like this 234235.34.
Here is some of the code I am using
<table>
<tr>
<th ng-repeat='header in headers'>{{header.th}}</th>
</tr>
<tr>
<td ng-repeat='data in headers'>
<div ng-repeat='inner in data.td'>
<span ng-repeat='(prop, val) in inner'>{{val.toFixed(2)}}</span>
</div>
</td>
</tr>
</table>
and in the controller
$scope.LoadMyJson = function() {
for (var s in myJson){
$scope.data.push(s);
if ($scope.headers.length < 1)
for (var prop in myJson[s]){
prop.data = [];
$scope.headers.push({th:prop, td: []});
}
}
for (var s in $scope.data){
for (var prop in $scope.headers){
var header = $scope.headers[prop].th;
var data = myJson[$scope.data[s]][header];
$scope.headers[prop].td.push(data);
console.log($scope.headers[prop].td);
}
}
};
and I prepared this Fiddle
the way it is right now, is displaying the table properly, but as you see, the table is missing the name, it is because of the toFixed method.
So, what can I do ?
Create a custom filter to use on your template.
<table>
<tr>
<th ng-repeat='header in headers'>{{header.th}}</th>
</tr>
<tr>
<td ng-repeat='data in headers'>
<div ng-repeat='inner in data.td'>
<span ng-repeat='(prop, val) in inner'>{{val|formatValue}}</span>
</div>
</td>
</tr>
</table>
angular.module('whatever').filter('formatValue', function () {
return function (value) {
if (isNaN(parseFloat(value))) {
return value;
}
return parseFloat(value).toFixed(2);
}
});
You can try this :
That is a clean way to render formated data in view using angularjs as MVC
frontend framework :
Create a filter in your angular application.
Include your filter in your index.html.
use your filter like this : {{somedata | filterName}}
That is a simple angular filter to solve your problem, hope it will help you :
angular.module('app')
.filter('formatHeader', function() {
return function(data) {
if(angular.isNumber(data)) {
return data.toFixed(2);
}
return data;
}
});
And us it like this :
<table>
<tr>
<th ng-repeat='header in headers'>{{header.th}}</th>
</tr>
<tr>
<td ng-repeat='data in headers'>
<div ng-repeat='inner in data.td'>
<span ng-repeat='(prop, val) in inner'>{{val | formatHeader}}</span>
</div>
</td>
</tr>
You can take a look about these references :
angular functions
filter doc.
angular tutorials

Changing or editings Keys in objects, in order to show in view. Using Angular

Example JSON:
[{"name":"John", "date_of_birth":"01/12/1987","marital_status": "Open Relationship"}]
Example view:
<table>
<tr>
<th> Name </th>
<th>Date of Birth</th>
<th>Martial Status</th>
</tr>
<tr ng-repeat= "profile in profiles">
<td>{{profile.name}}</td>
<td>{{profile.date_of_birth}}</td>
<td>{{profile.marital_status}}</td>
</tr>
</table>
What I want is to ng-repeat the keys or table heading too. I know how to put ng-repeat and get keys and values. But what I will like is to change the keys and make them look nice, for e.g : date_of_birth should be Date of Birth, but with ng-repeat on it.
I do agree with the comments in under your question that it is not the best approach. Also, remember that in reality you shouldn't count on a particular order when iterating over object's params (see https://github.com/angular/angular.js/issues/6210).
However ;) in the spirit of solving interesting question…
If you really want to do this, you can do it this way:
<table>
<tr ng-repeat="(key, value) in profiles[0]">
<th>{{ key | snailToHuman }}</th>
</tr>
<tr ng-repeat= "profile in profiles">
<td>{{profile.name}}</td>
<td>{{profile.date_of_birth}}</td>
<td>{{profile.martial_status}}</td>
</tr>
</table>
And create a filter snailToHuman, e.g.:
app.filter('snailToHuman', function () {
return function (snail) {
return snail.split('_').map(function (word) {
word.charAt(0).toUpperCase() + word.slice(1);
}).join(' ');
};
})

Angular paging generates wrong number of pages

I am having a hard time getting the Angular paging to work correctly. The number of pages seems to be off. For example, for one of my searches, the number of returned results is 1005. Displaying 16 results per page, should have 63 pages total. Instead it generates 101. I appreciate any suggestions on why this is happening and how to resolve.
Thanks in advance!
<table class="table table-striped results">
<thead>
<tr>
<th ng-repeat="x in json.headers">{{ x }}</th>
</tr>
<thead>
<tbody>
<tr ng-repeat="x in filteredResults">
<td>{{ x.name }}</td>
<td>{{ x.city }}</td>
<td>{{ x.state }}</td>
<td>{{ x.zip }}</td>
<td>{{ x.phone }}</td>
</tr>
</tbody>
</ul>
</table>
<pagination
style="position:absolute; bottom:10px;"
ng-show="json.results.length"
ng-model="currentPage"
total-items="json.results.length"
max-size="maxSize"
boundary-links="true"
next-text=">"
last-text=">>"
previous-text="<"
first-text="<<">
</pagination>
JavaScript
var app = angular.module('myApp', ['ui.bootstrap']);
app.controller('formCtrl', function($scope, $http){
$(document).ready(function(){
$('.submit-search').click(function(){
$http.get('model/search_url.php', {
params: { searchBy: $scope.user.searchBy, search: $scope.user.search }
}).success(function (response){
$scope.json = response;
$scope.filteredResults = [];
$scope.currentPage = 1
$scope.numPerPage = 16
$scope.maxSize = 5;
$scope.$watch("currentPage + numPerPage", function() {
var begin = (($scope.currentPage - 1) * $scope.numPerPage);
var end = begin + $scope.numPerPage;
$scope.filteredResults = $scope.json.results.slice(begin, end);
});
});
});
});
You're missing the items-per-page, in your case it should be 16, but since you don't provide it it set to the default of 10 items per page.

Categories

Resources