Ranking table with sorting & dynamic columns - angularjs

I'm trying to do a ranking table :
I have an object scores with the total of points, and each "challenge" with the points associated to this challenge. My principal problem is to run through scores to create a th tag for each challenge & to sort these after.
The aims :
The number of challenge must be dynamic
Sort by total (done) & by challenge
My problems :
How use ng-repeat for the array of challenge
How to sort these
My advancement :
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.sortType = 'points';
$scope.sortReverse = false;
$scope.scores = {
"6":{
"total":5,
"challenges":{
"1":{
"challengeId":1},
"2":{
"point":2,
"challengeId":2}
}
},
"21":{
"total":2,
"challenges":{
"1":{
"point":1,
"challengeId":1},
"2":{
"point":1,
"challengeId":2}
}
},
}
}
myApp.filter('orderObjectBy', function() {
return function(items, field, reverse) {
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (a[field] > b[field] ? 1 : -1);
});
if(reverse) filtered.reverse();
return filtered;
};
});
<div ng-controller="MyCtrl">
<table>
<tr>
<th>Rank</th>
<th>
<span ng-click="toggleSort($index);sortReverse=false">+</span>
Total
<span ng-click="toggleSort($index);sortReverse=true">-</span>
</th>
<th ng-repeat="challenge in scores.challenges">
<span ng-click="toggleSort($index);sortReverse=false">X</span>
Challenge n°
<span ng-click="toggleSort($index);sortReverse=true">Y</span>
</th>
</tr>
<tbody ng-repeat="score in scores | orderObjectBy:sortType:sortReverse">
<tr>
<td>{{ $index+1 }}</td>
<td>{{ score.total }}</td>
<td ng-repeat="challenge in score.challenges">{{ challenge.point }}</td>
</tr>
</tbody>
</table>
</div>
JSFiddle
Thank you!

I updated the fiddle. When you want to order by a nested value this is how you specify the orderString (In your case it will be by a particular challenge ID):
$scope.orderType = "challenges."+challengeId+'.point';
Also I added a filter which convert the object in an array:
.filter('orderObjectBy', function() {
return function (items, field, reverse) {
var filtered = [];
angular.forEach(items, function(item, key) {
item.key = key;
filtered.push(item);
});
return filtered;
};
});
Then your html should look like this:
<tbody ng-repeat="score in scores| orderObjectBy | orderBy: orderType:sortReverse">
<tr>
<td>{{ $index+1 }}</td>
<td>{{ score.total }}</td>
<td ng-repeat="challenge in score.challenges">{{ challenge.point }}</td>
</tr>
</tbody>
Check this working sample: http://jsfiddle.net/Lvc0u55v/13564/

Related

Pass value to another controller in angularjs

I am new to AngularJS.
I have a div assigned controller to controller1. In this div, I am showing EmployeeList with three links for View, Edit,Delete for each employee.
I have another div assigned controller to controller2. By clicking the view link on any employee, I want to show full details in second div. Both divs are in same page.
How do I do that? Below is what I tried.
<div ng-controller="employeeController" style="border:solid black 3px;float:left;padding:5px">
<h3 style="color:#ff6a00">List Of Employees</h3>
<table>
<tr>
<th hidden>ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Full Name</th>
<th>Actions</th>
</tr>
<tr ng-repeat="employee in employees">
<td hidden><strong>{{ employee.ID }}</strong></td>
<td>{{ employee.FName }}</td>
<td>{{ employee.LName }}</td>
<td><strong>{{ employee.FName + ' ' + employee.LName }}</strong></td>
<td>
<a data-ng-click="GetEmployeeByID({{employee.ID}})" href="javascript:;">View</a>
</td>
</tr>
</table>
</div>
<div ng-controller="employeeByIDController" style="border:solid black 3px;float:right;padding:5px">
<h3 style="color:#ff6a00">Employee Details</h3>
<table>
<tr>
<th hidden>ID</th>
<th>Full Name</th>
<th>Qualification</th>
<th>Gender</th>
<th>Phone</th>
<th>Email</th>
<th>Address</th>
<th>City</th>
</tr>
<tr>
<td hidden><strong>{{ employeeByID.ID }}</strong></td>
<td>{{ employeeByID.FName + ' ' + employeeByID.LName }}</td>
<td>{{ employeeByID.Qualification }}</td>
<td>{{ employeeByID.Gender }}</td>
<td>{{ employeeByID.Phone }}</td>
<td>{{ employeeByID.Email }}</td>
<td>{{ employeeByID.Address }}</td>
<td>{{ employeeByID.City }}</td>
</tr>
</table>
</div>
The code in factory is as below.
mainApp.factory('EmployeeFactory', function ($http) {
return {
GetEmployeeList: function () {
return $http({
method: 'GET',
url: '/AngularEmployee/EmployeeList'
});
}
}
return {
GetEmployeeByID: function ($id) {
return $http({
method: 'GET',
url: '/AngularEmployee/EmployeeByID',
params: { empID : id }
});
}
}
});
I have added controller as follows.
mainApp.controller("employeeController", ['$scope', 'EmployeeFactory', function ($scope, EmployeeFactory) {
$scope.GetEmployeeList = function () {
EmployeeFactory.GetEmployeeList().success(function (data) {
$scope.employees = data;
}).error(function (data) {
$scope.error = "An Error has occured while Loading users! " + data.ExceptionMessage;
});
};$scope.GetEmployeeList();
}]);
Angular has a pubsub mechanism for this. It's well documented. E.g.:
https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
if you want to share data between two controllers in angularJs , using a service or factory is a better option as service/factory in angularJs are injectable.But service/factory is a singleton object so be aware of the fact that same object of the service/factory will be shared by all the controllers.
Taking your example ,
you can create another service :
mainApp.service('sharedService',function(){
this.employee={};
this.getter=function(){
return this.employee;
};
this.setter=function(emp){
this.employee=emp;
};
});
Then you can share this service across your controllers to get or set employee.
mainApp.controller("employeeController", ['$scope', 'EmployeeFactory', 'sharedService', function($scope, EmployeeFactory, sharedService) {
$scope.GetEmployeeList = function() {
EmployeeFactory.GetEmployeeList().success(function(data) {
$scope.employees = data;
sharedService.setter($scope.employees);//set in shared service
}).error(function(data) {
$scope.error = "An Error has occured while Loading users! " + data.ExceptionMessage;
});
};
$scope.GetEmployeeList();
}]);
And then in employeeByIDControlle ,
mainApp.controller("employeeByIDController", ['$scope', 'EmployeeFactory', 'sharedService', function($scope, EmployeeFactory, sharedService) {
$scope.employees = sharedService.getter();//get from shared service
}]);

Angular add filter based on Object value

I'm starting AngularJS. I'm dynamically building a table with angular and I'm trying to add a filter to one of my columns.
Here the code I have in my controller :
$scope.columns = [
{
"alias":"alias1",
"name":"name1",
"type":"string"
},
{
"alias":"alias2",
"name":"name2",
"type":"currency"
}
];
$scope.data = [
{
"alias1": "value1",
"alias2": "22489"
},
{
"alias1": "value2",
"alias2": "22489"
},
{
"alias1": "value3",
"alias2": "22489"
},
];
And my table looks like this :
<table class="table table-hover">
<thead>
<tr>
<th ng-repeat="c in columns" ng-click="sort(c.alias)">{{ c.name }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="result in results | filter:search | orderBy:sortKey:reverse">
<td ng-repeat="c in columns">{{ result[c.alias] }}</td>
</tr>
</tbody>
</table>
What I'd like to do is to apply a filter to the <td> value based on the type of the column. Something like {{ result[c.alias] | c.type }} doesn't work...
Is it something that can be achieved by some formatting directive for example ?
Thanks for you help.
You mean this?:
app.filter('myfilter', function() {
return function(input, param) {
//do whatever
}
})
{{ result[c.alias] | myfilter:c.type }}

Is it possible to have 2 datasources for ng-repeat?

Is it possible to have
<tbody ng-repeat="Emp in Employees" ng-repeat="Dept in Department">
<td> <span>{{Emp.id}}</span></td>
<td> <span>{{Emp.name}}</span></td>
<td> <span>{{Dept.Deptid}}</span></td>
</tbody>
That means two different sources.... It is for a composite UI requirement. If not available, what is the alternate way?
I am not looking for
<tbody ng-repeat="Emp in Employees" >
<td> <span>{{Emp.id}}</span></td>
<td> <span>{{Emp.name}}</span></td>
</tbody>
<tbody ng-repeat="Dept in Department">
<td> <span>{{Dept.Deptid}}</span></td>
<td> <span>{{Dept.Deptname}}</span></td>
</tbody>
Something similar to LINQ in C# like
var x = (from a in AList
join b in BList on a.item = b.item
select new{....});
You can do it by merging two arrays
app.controller('MainCtrl', function($scope) {
$scope.Employees = [{
'id':1,
'name':'rachit1'
},
{
'id':2,
'name':'rachit2'
}];
$scope.Department=[{
'Deptid':'1D',
'Deptname':'dep1'
},
{
'Deptid':'2D',
'Deptname':'dep2'
}];
$scope.result=merge($scope.Employees,$scope.Department);
function merge(obj1,obj2){ // Our merge function
var result = {}; // return result
for(var i in obj1){ // for every property in obj1
if((i in obj2) && (typeof obj1[i] === "object") && (i !== null)){
result[i] = merge(obj1[i],obj2[i]); // if it's an object, merge
}else{
result[i] = obj1[i]; // add it to result
}
}
for(i in obj2){ // add the remaining properties from object 2
if(i in result){ //conflict
continue;
}
result[i] = obj2[i];
}
return result;
}
});
HTML:-
<body ng-controller="MainCtrl">
<table>
<tbody ng-repeat="Emp in result">
<td> <span>{{Emp.id}}</span></td>
<td> <span>{{Emp.name}}</span></td>
<td> <span>{{Emp.Deptid}}</span></td>
<td> <span>{{Emp.Deptname}}</span></td>
</tbody>
</table>
</body>
Plunker
The answer is :Can't. Because may the looping count is difference.
can you write a looping statement on a another one looping statement?
for (var i=0;i<Empcount;i<DeptCount;i++)
{
// code
}
Is it impossible, Same as what the answer for your question. You can think the result.
I know that it is not really an answer, but I must to say this: your data organised incorrectly. Data integrity is violated.
But if you can't change the source structure for some reason try to combine that data in the controller:
$scope.employees = employees.map(function (employee, index) {
employee.department = departments[index];
return employee;
})
Then use it in the ng-repeat:
<tbody>
<tr ng-repeat="employee in employees">
<td>{{ employee.id }}</td>
<td>{{ employee.name }}</td>
<td>{{ employee.department.deptId }}</td>
</tr>
</tbody>
NOTES:
Do not repeat <tbody> tag.
Apply the technique if Departments.length === Employees.lengh and the order is correct.
Do not capitalize not constructors. Rename Departments to departments, Employees to employees.
If you don't have any styles on td > span then remove unnecessary span's

angularjs change element based on dropdown select

Using angularjs
I have 3 select dropdowns and a button to get a json list
Focus select dropdown looks like this:
<select id='sort' ng-model='sort'>
<option value='1'>ID</option>
<option value='2'>Departmentname</option>
<option value='3'>Number of employees</option>
</select>
<button ng-click="getinfo()">GET INFO</button>
The table looks like this
<table>
<tr> <td>{{ depid }}</td> <td>{{ depname }}</span></td> <td>{{ depemp }}</td></tr>
In the controller I have:
$scope.depid = "Department id";
$scope.depname = "Departmentname";
$scope.depemp = "Number of employees";
$scope.getinfo = function() {
var url = "";
...
}
Based on selection "sort" I want the sort column to be bold/strong or uppercase.
How do I do thIS?
You can use ng-class on the td elements:
<tr>
<td ng-class="{bold : sort == 1}">{{ depid }}</td>
<td ng-class="{bold : sort == 2}">{{ depname }}</span></td>
<td ng-class="{bold : sort == 3}">{{ depemp }}</td>
</tr>
And add a CSS rule:
.bold {
font-weight: bold;
}
Here's a Fiddle demonstration.
Following Omri Aharon I changed
ng-class="{bold : sort == 1}"
to
ng-class="{bold : sortby == 1}"
and followed Nayish I put following inside controller
$scope.getinfo = function() {
var url = "";
...
$http.get(url)
.success(function(response) {
$scope.names = response;
$scope.sortby = $scope.sort;});
}
Thanks a lot to the two of you!

angularjs ng-repeat with Filter or ng-if condition

I want to render text if it is Name or Value. I don't want to loop if it is createdate or enventId. I'm trying with Filter and ng-if condition. It is not working.
HTML CODE
<tr ng-repeat="event in events | filter:Name ">
<td >{{event.Name}} : </td>
<td>{{ event.Value }}</td>
</tr>
JS CODE
Events: [
{
CreatedTime: "/Date(1420757322810-0800)/",
EventId: 13,
Name: "AdGroupId",
Value: "2367898"
},
{
CreatedTime: "/Date(1420757322810-0800)/",
EventId: 13,
Name: "AdGroupId",
Value: "2367898"
}
]
I expect the result should be
AdGroupId :2367898
Assuming that what you are trying to ask is how to exclude items with neither a name nor a value, you'll need a custom function for this. This should suffice:
$scope.filterEvents = function(e){
return e.Name || e.Value;
};
And then:
<tr ng-repeat="event in events | filter:filterEvents">
<td >{{event.Name}} : </td>
<td>{{ event.Value }}</td>
</tr>
Example
Alternatively, if you are saying you want to filter the values based on a specified name and/or value, you can do so like this:
$scope.filterEvents = function (e) {
return ($scope.Name || $scope.Value) &&
(!$scope.Name || $scope.Name === e.Name) &&
(!$scope.Value || $scope.Value === e.Value);
};
(HTML is same as above)
Example
Assuming you are looking for a filter to cut out the Events that has no Name and Value properties in it.
Here is a sample for you:
Controller:
$scope.filterNameValue = function(e){
if(angular.isDefined(e.Name) && angular.isDefined(e.Value))
return e;
};
Html
<table>
<tbody>
<tr ng-repeat="event in events | filter: filterNameValue">
<td >{{event.Name}} : </td>
<td>{{ event.Value }}</td>
</tr>
</tbody>
</table>
Working Sample here

Resources