Sorting only certain arrays in nested ngRepeat - angularjs

I currently have a nested ngRepeat, where the inner loop iterates over a collection of items from its parent. An excerpt:
<div ng-repeat="person in persons">
(Irrelevant code here.)
<table>
<tr>
<th ng-click="setItemOrder('name')">Item name</th>
<th ng-click="setItemOrder('number')">Item number</th>
</tr>
<tr ng-repeat="item in person.items | orderBy:itemOrder">
<td>{{item.name}}
<td>{{item.number}}
</tr>
</table>
</div>
By clicking the table headers, I set the itemOrder-property in my controller to the name of the property I want orderBy to use:
$scope.setItemOrder = function(order){
$scope.itemOrder = order;
}
This all works fine, except that if I click the headers in one person-div, the item-tables in all person-divs get sorted on that property.
Is there a way to make ngRepeat only apply orderBy to entries that match a certain criteria - for instance a certain index? Or should I use a different approach?

Try setting the property to respective person instance as follows:
angular.module('test', []).controller('testController', function($scope) {
$scope.persons = [{
items: [{
name: 'test',
number: 2
}, {
name: 'test1',
number: 1
}]
}, {
items: [{
name: 'test3',
number: 5
}, {
name: 'test4',
number: 4
}]
}];
$scope.setItemOrder = function(person, order) {
person.itemOrder = order;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="test" ng-controller="testController">
<div ng-repeat="person in persons">
<table>
<tr>
<th ng-click="setItemOrder(person,'name')">Item name</th>
<th ng-click="setItemOrder(person,'number')">Item number</th>
</tr>
<tr ng-repeat="item in person.items | orderBy:person.itemOrder">
<td>{{item.name}}
<td>{{item.number}}
</tr>
</table>
</div>

You could add a ordering variable for each person and extend setItemOrder with the person object. Then you can call:
setItemOrder(person, 'name');
and then use it in the ngRepeat:
orderBy:person.itemOrder

angular.module('test', []).controller('testController', function($scope) {
$scope.ordersort=true;
$scope.orderfield='number';
$scope.persons = {
"items": [{
"name": 'test',
"number": 2
}, {
"name": 'test1',
"number": 1
}],
"item1": [{
"name": 'test3',
"number": 5
}, {
"name": 'test4',
"number": 4
}]
};
$scope.setItemOrder = function(person, order) {
$scope.orderfield=order;
person.itemOrder = order;
$scope.ordersort= !$scope.ordersort;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="test" ng-controller="testController">
<div ng-repeat="person in persons">
<table>
<tr>
<th ng-click="setItemOrder(person,'name')">Item name</th>
<th ng-click="setItemOrder(person,'number')">Item number</th>
</tr>
<tr ng-repeat="item in person | orderBy:orderfield:ordersort">
<td>{{item.name}}
<td>{{item.number}}
</tr>
</table>
</div>
I have modified your example. In this example table sorting is working perfectly. But It is not sorted the particular table when I click on that table header. Anyway to sort columns by specific table?
angular.module('test', []).controller('testController', function($scope) {
$scope.persons = [{
items: [{
name: 'test',
number: 2
}, {
name: 'test1',
number: 1
}]
}, {
items: [{
name: 'test3',
number: 5
}, {
name: 'test4',
number: 4
}]
}];
$scope.setItemOrder = function(person, order) {
person.itemOrder = order;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="test" ng-controller="testController">
<div ng-repeat="person in persons">
<table>
<tr>
<th ng-click="setItemOrder(person,'name')">Item name</th>
<th ng-click="setItemOrder(person,'number')">Item number</th>
</tr>
<tr ng-repeat="item in person.items | orderBy:person.itemOrder">
<td>{{item.name}}
<td>{{item.number}}
</tr>
</table>
</div>

Related

AngularJS recursive templates using tables

I am trying to work out how to do recursion using table tags with angularjs. I have the following data structure
$scope.report = [{
'Name': 'Moo',
'Value': 'MooV',
'Children': [{
'Name': 'Moo2',
'Value': 'Moo2V',
'Children': [{
'Name': 'Moo3',
'Value': 'Moo3V'
}]
}]
}];
The recursion can have no limit. I am looking to put in a simple table with the format
<table>
<tr>
<td>Moo</td>
<td>MooV</td>
</tr>
<tr>
<td>Moo2</td>
<td>Moo2V</td>
</tr>
<tr>
<td>Moo3</td>
<td>Moo3v</td>
</tr>
<table>
but I am unable to get it working just right. This will allow me to do certain things to the sections while looking flat to the user. Currently I have the code
<div ng-app="app" ng-controller='AppCtrl'>
<script type="text/ng-template" id="rloop">
<td>{{data.Name}}</td>
<td>{{data.Value}}</td>
<tr ng-repeat="data in data.Children" ng-include src="'rloop'"></tr>
</script>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat-start="data in report" ng-include="'rloop'"></tr>
<tr ng-repeat-end></tr>
</tbody>
</table>
</div>
but this is not going to work due to in repeating it creates a tr tag within a tr tag. I have tried various different methods of moving the repeat to tbody etc etc but I can't seems to get it working due to the restrictions of tables in HTML. Example, is not allowed within tr.
JSFiddle showing issue here: JSfiddle
As you say, this is not going to work within the table. Maybe it would be better to recursively convert the data to an array of Name/Value pairs, then use those in the normal way?
var app = angular.module('app', []);
function includeChildren([first, ...rest]) {
if (!first) return [];
const {Name, Value, Children = []} = first;
return [{Name, Value}, ...includeChildren(Children), ...includeChildren(rest)];
}
app.controller('AppCtrl', function ($scope) {
$scope.report = [{
'Name': 'Moo',
'Value': 'MooV',
'Children': [{
'Name': 'Moo2',
'Value': 'Moo2V',
'Children': [{
'Name': 'Moo3',
'Value': 'Moo3V'
}]
}]
}];
$scope.reportTable = includeChildren($scope.report);
// $scope.reportTable now contains:
// [{Name: 'Moo', Value: 'MooV'}, {Name: 'Moo2', Value: 'Moo2V'}, {Name: 'Moo3', Value: 'Moo3V'}]
});
Now you can use a more unsurprising template:
<div ng-app="app" ng-controller='AppCtrl'>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="data in reportTable">
<td>{{data.Name}}</td>
<td>{{data.Value}}</td>
</tr>
</tbody>
</table>
</div>
See it working here: https://jsfiddle.net/k6rf9g1p/1/

I want to calculate a total of ng-repeat

I want to calculate a total of amount of ng-repeat with the condition.
My table looks like this
<tr ng-repeat="Company in Companies track by $index">
<td>Departmend</td>
<td>Amount</td>
</tr>
<tr>
<td>HR</td>
<td>500</td>
</tr>
<tr>
<td>IT</td>
<td>200</td>
</tr>
<tr>
<td>HR</td>
<td>600</td>
</tr>
<tr>
<td colspan="2">Total:</td>
</tr>
My script like this.
<script>
if ($scope.Department == "HR") {
var Total = 0;
angular.forEach($scope.Companies, function (item) {
Total = Math.abs(Total) + Math.abs(item.Amount);
});
}
</script>
Are you looking for a solution like this? Finding the total of the array depending on its type?
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.Companies = [
{ Amount: 200, Department: "HR" },
{ Amount: 300, Department: "IT" },
{ Amount: 400, Department: "HR" },
{ Amount: 500, Department: "IT" },
{ Amount: 600, Department: "HR" }
];
$scope.totalValue = 0;
angular.forEach($scope.Companies, function(item) {
if (item.Department === "HR") {
$scope.totalValue += Math.abs(item.Amount);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<table>
<tr>
<td>Departmend</td>
<td>Amount</td>
</tr>
<tr ng-repeat="Company in Companies track by $index">
<td>{{Company.Department}}</td>
<td>{{Company.Amount}}</td>
</tr>
<tr>
<td>Total</td>
<td>{{totalValue}}</td>
</tr>
</table>
</body>

AngularJS access another array with $index under ng-repeat

I new to AngularJS and wish to access two arrays under ng-repeat. This is my code:
<tr ng-repreat="find in findOptions">
<td>find.first</td>
<td>find.second</td>
<td>{{ search[{{$index}}].third }}</td>
</tr>
search is another array that I want to access here
You can't have iterpolation inside a intepolation again, basically you could access scope variable directly inside it.
You could simply directly use $index to access array.
search[$index].third
Created JSFiddle Demonstration - https://jsfiddle.net/qtksp1kj/
Hope this will help!
<body ng-app="SampleApp">
<table ng-controller="fcController" border="1px">
<thead>
<tr>
<td>Name</td>
<td>Address</td>
<td>LandMark</td>
</tr>
</thead>
<tbody ng-repeat="element in myArray">
<tr>
<td>{{element.name}}</td>
<td>{{element.address}}</td>
<td>{{myArray1[$index].landMark}}</td>
</tr>
</tbody>
</table>
</body>
var sampleApp = angular.module("SampleApp", []);
sampleApp.controller('fcController', function($scope) {
$scope.myArray = [{
'name': 'Name1',
'address': 'H-1 China Town'
}, {
'name': 'Name2',
'address': 'H-2 China Town'
}];
$scope.myArray1 = [{
'landMark': 'Near Dominos'
}, {
'landMark': 'Near Airport'
}];
});

Angular select element with complex object

Need some help with a select element in a Angular app I'm building.
Supposing I have the code below, what's the best way to change the property 'childId' of each item when selecting an option in the select element?
With the below code, when I select an element it will only set the 'child' property with the selected object and I can understand why. My only issue is that I also need to set the 'childId' property, so what's the right way to accomplish that?
<div ng-app="CustomApp" ng-controller="CustomCtrl">
<table class="table">
<thead>
<tr>
<th>Description</th>
<th>Child</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in dataItems">
<td>
<input name="Item[{{$index}}].Description"
value="{{item.description}}"
type="text"
class="form-control" />
</td>
<td>
<select name="Item[{{$index}}].Child"
ng-model="item.child"
ng-options="ichild as ichild.description for ichild in
$parent.childItems track by ichild.id">
<option value="">Select one option...</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
(function () {
"use strict";
var app = angular.module('CustomApp', []);
app.controller('CustomCtrl', ['$scope',
function($scope) {
$scope.dataItems = [
{ id: 1, description: 'foo one', childId: 1, child: { id: 1, description: 'bar01' }},
{ id: 2, description: 'foo two', childId: 0 },
{ id: 3, description: 'foo three, childId: 2, child: { id: 2, description: 'bar02' }}
];
$scope.childItems = [
{ id: 1, description: 'bar01' },
{ id: 2, description: 'bar02' }
];
}]);
})();
i think, this is what you want to do [actually i hope]:
<!doctype html>
<html>
<head>
</head>
<body>
<div ng-app="CustomApp" ng-controller="CustomCtrl">
<table class="table">
<thead>
<tr>
<th>Description</th>
<th>Child</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in dataItems">
<td>
<input name="Item[{{$index}}].description" ng-model="item.description" type="text" class="form-control" />{{item.description}}
</td>
<td>
<select name="Item[{{$index}}].child" ng-model="item.child" ng-options="ichild as ichild.description for ichild in $parent.childItems track by ichild.id"></select>
{{item.child}}
</td>
</tr>
</tbody>
</table>
</div>
<script src="/Scripts/angular.js"></script>
<script>
(function() {
"use strict";
var app = angular.module("CustomApp", []);
app.controller("CustomCtrl", ["$scope",
function($scope) {
$scope.dataItems = [{
id: 1,
description: "foo one",
childId: 1,
child: [{
id: 1,
description: "bar01"
}]
}, {
id: 2,
description: "foo two",
childId: 0
}, {
id: 1,
description: "foo three",
childId: 2,
child: [{
id: 2,
description: "bar02"
}]
}];
$scope.childItems = [{
id: 1,
description: "bar01"
}, {
id: 2,
description: "bar02"
}];
}
]);
})();
</script>
</body>
</html>

Can't generate <td> structure with recursive ng-repeat

I'm having some difficulties generating the desired table structure with a nest ng-repeat. I have a table header for each day of the week and I want to display three shifts in the <td> beneath that. Currently I solve this with three <span>'s in one <td>, but I would like to generate three <td>'s for each shift in on day. My html code with the repeats is:
<div>
<table>
<caption>Week: {{ week }}</caption>
<thead>
<tr>
<th ng-repeat="day in days">
<span>{{day.Date}}/{{ month }}/{{ year }}</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-repeat="day in days">
<span ng-repeat="shift in shifts">{{shift.Name}}</span>
</td>
</tr>
</tbody>
</table>
</div>
I have tried <th colspan="3"> with different placement of "day in days" repeats, but I can't seem to get the wanted structure for the table body:
<tr>
<td>shift1</td>
<td>shift2</td>
<td>shift3</td>
<td>shift1</td>
<td>shift2</td>
<td>shift3</td>
<td>shift1</td>
<td>shift2</td>
<td>shift3</td>
<td>shift1</td>
<td>shift2</td>
<td>shift3</td>
<td>shift1</td>
<td>shift2</td>
<td>shift3</td>
<td>shift1</td>
<td>shift2</td>
<td>shift3</td>
</tr>
My question is: how can I rewrite my current code with the <span>'s so that it generates the wanted structure above.
If I understood your problem correctly then I think you are looking for ng-repeat-start & ng-repeat-stop.
HTML:
<div ng-app>
<div ng-controller="ExampleCtrl">
<table border="1">
<tr>
<td ng-repeat-start="day in days" ng-show="false"></td>
<td ng-repeat="shift in day.shifts" ng-repeat-end>shift{{shift}}</td>
</tr>
</table>
</div>
</div>
JS Controller:
function ExampleCtrl($scope) {
$scope.days = [
{
shifts: ["1","2","3"]
},
{
shifts: ["1","2","3"]
},
{
shifts: ["1","2","3"]
},
{
shifts: ["1","2","3"]
},
{
shifts: ["1","2","3"]
}
];
}
and the Fiddle
shifts outside days object: Fiddle update
You can create new plain list in your model to iterate over and use it instead of using nested loops. If data is to be changed then you can watch changes of dependent properties and reassemble plain list for each change. Something like this:
JavaScript
angular.module('app',[]).
controller('appController', function($scope) {
var i, j;
$scope.week = 1;
$scope.days = [{
day: 1,
month: 2,
year: 2014
}, {
day: 2,
month: 2,
year: 2014
}, {
day: 3,
month: 2,
year: 2014
}];
$scope.shifts = ['Shift1', 'Shift2', 'Shift3'];
// Create new plain list each time shifts is changed
$scope.$watchCollection('shifts', function() {
$scope.daysShifts = [];
for(i=0; i<$scope.days.length; i++) {
for(j=0; j<$scope.shifts.length; j++) {
$scope.daysShifts.push({
name: $scope.shifts[j]
});
}
}
});
$scope.addShift = function() {
$scope.shifts.push('Shift'+($scope.shifts.length+1));
}
});
HTML
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#*" data-semver="1.2.11" src="http://code.angularjs.org/1.2.11/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="appController">
<div>
<table>
<caption>Week: {{week}}</caption>
<thead>
<tr>
<th ng-repeat="day in days" colspan="{{shifts.length}}">
<span>{{day.day}}/{{day.month}}/{{day.year}}</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-repeat="dayShift in daysShifts">
<span>{{dayShift.name}}</span>
</td>
</tr>
</tbody>
</table>
</div>
<button ng-click="addShift()">Add Shift</button>
</body>
</html>
Plunker: http://plnkr.co/edit/zZYrurhp3HQA9Jp9QHDo?p=preview

Resources