nested table using ng-repeat - angularjs

What I'm trying to do is repeat three levels.
Demo: http://plnkr.co/edit/qXLcPHXDlOKZYI5jnCIp?p=preview
<table>
<thead>
<tr>
<td>Block</td>
<td>Column</td>
<td>Unit</td>
<td>Action</td>
</tr>
</thead>
<tbody ng-repeat="block in building.ownBlock">
<tr ng-repeat="column in block.ownColumn">
<td>{{block.name}}</td>
<td>{{column.number}}</td>
<td>{{unit.name}} - ? empty</td>
<td><button ng-click="edit(unit)">Edit</button></td>
</tr>
</tbody>
</table>
but I have failed to do so.
Collection
$scope.building =
{
id: 1,
name: 'first',
ownBlock: [
{
id: 1,
name: 'Block 1',
ownColumn: [
{
id: 1,
number: 'Column 1-1',
ownUnit: [
{
id: 1,
number: 'Unit 1-1-1'
},
{
id: 2,
number: 'Unit 1-1-2'
}
]
},
{
id: 2,
number: 'Column 1-2',
ownUnit: [
{
id: 3,
number: 'Unit 1-2-3'
},
{
id: 4,
number: 'Unit 1-2-4'
}
]
}
]
},
{
id: 2,
name: 'Block 2',
ownColumn: [
{
id: 3,
number: 'Column 2-3',
ownUnit: [
{
id: 5,
number: 'Unit 2-3-5'
},
{
id: 6,
number: 'Unit 2-3-6'
}
]
},
{
id: 4,
number: 'Column 2-4',
ownUnit: [
{
id: 7,
number: 'Unit 2-4-7'
},
{
id: 8,
number: 'Unit 2-4-8'
}
]
}
]
}
]
};
Using KnockoutJS I could use virtual repeaters eg.
<!-- ko foreach: items -->
<li data-bind="text: $data"></li>
<!-- /ko -->
I coded a directive, but 'ng-click="edit(unit)"' just doesn't work.
Maybe because I'm using element.replaceWith(html); to replace the directive HTML.
Any help is much appreciated.
Thank you

You could try something like this, depending on the steady state of your models.
<body>
<table>
<thead>
<tr>
<td>Block</td>
<td>Column</td>
<td ng-repeat="unit in building.ownBlock[0].ownColumn[0].ownUnit[0]">Unit</td>
<td>Action</td>
</tr>
</thead>
<tbody ng-repeat="block in building.ownBlock">
<tr ng-repeat="column in block.ownColumn">
<td>{{block.name}}</td>
<td>{{column.number}}</td>
<td ng-repeat="unit in column.ownUnit">{{unit.number}} - ? empty</td>
<td><button ng-click="edit(unit)">Edit</button></td>
</tr>
</tbody>
</table>
<pre>
{{toedit|json}}
</pre>

you will want to use the new ng-repeat-start and ng-repeat-end directives which were added in Angular 1.2. see the doco for examples.
http://docs.angularjs.org/api/ng.directive:ngRepeat

Related

Trying to populate html table with ng-repeat using two different arrays

I am having some issues trying to populate a table with two arrays. I am trying to achieve the following output:
I have an array called students and within each student object, I have a courses array. I am trying to populate my table using ng-repeat to show all courses against each student.
Not all data is given within each course, meaning that if a student hasn't started a course then it won't appear in the courses array. Also, if a student hasn't finished a course there will be no date completed given.
So my array looks like so:
$scope.students = [
{
Id: 1,
Name: 'Joe Blogs',
Courses: [
{
Title: 'Course 1',
Grade: 87,
Completed: true,
DateCompleted: '2018-01-01'
},
{
Title: 'Course 2',
Grade: 2,
Completed: false
}
]
},
{
Id: 2,
Name: 'Peter Smith',
Courses: [
{
Title: 'Course 1',
Grade: 100,
Completed: true,
DateCompleted: '2018-01-01'
},
{
Title: 'Course 2',
Grade: 95,
Completed: true,
DateCompleted: '2018-01-01'
},
{
Title: 'Course 3',
Grade: 10,
Completed: false
}
]
},
{
Id: 3,
Name: 'Joanne Someone',
Courses: [
{
Title: 'Course 3',
Grade: 55,
Completed: false,
}
]
}
]
So far my code snippet looks like this:
<table class="table table-hover" width="100%">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Course</th>
<th>Grade</th>
<th>Completed</th>
<th>Date Completed</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in students">
<td>{{ student.Id }}</td>
<td>{{ student.Name }}</td>
<td>
<!-- Can't figure this bit out -->
</td>
</tr>
</tbody>
</table>
But I am stuck on how to get multiple courses to show against each student. Also I have found that by using ng-repeat, if a value isn't in the object (e.g. Date Completed) then that field will not show and pushes everything up and out of alignment.
Lastly, I will mention that I am using angular-datatables however, I am just trying to work out the concept, then I can apply this to datatables later.
You'll have to use rowspan for that purpose. And the value of that for particular Id should be of length of Courses. Then, you'll need to have ng-repeat on tbody to repeat tbody section & have ng-repeat inside on tr for repeating Courses arrays of each student. So, your table view code can be:
<table>
<thead>
<tr>
<td>Id</td>
<td>Name</td>
<td>Course</td>
<td>Grade</td>
<td>Completed</td>
<td>Date Completed</td>
</tr>
</thead>
<tbody ng-repeat="x in students">
<tr>
<td rowspan="{{x.Courses.length}}"><span>{{ x.Id }}</span></td>
<td rowspan="{{x.Courses.length}}"><span>{{ x.Name }}</span></td>
<td><span>{{x.Courses[0].Title}}</span></td>
<td><span>{{x.Courses[0].Grade}}</span></td>
<td><span>{{x.Courses[0].Completed}}</span></td>
<td><span>{{x.Courses[0].DateCompleted}}</span></td>
</tr>
<tr ng-repeat="item in x.Courses" ng-if="$index > 0">
<td><span>{{item.Title}}</span></td>
<td><span>{{item.Grade}}</span></td>
<td><span>{{item.Completed}}</span></td>
<td><span>{{item.DateCompleted}}</span></td>
</tr>
</tbody>
</table>
To avoid breaking of table code if some values are not available, just have it inside span element so it'll still load td with no value inside it.
Plunker Example

How to fetch data from objects in angularjs?

I am doing a simple cart system.
I created a table to display the products with add to cart button on the side of every product. I was able to get the index of every product when I click the button but I can't fetch the data inside the object(the name, price, and qty). Please help! How can I get them? Thanks.
< script >
var app = angular.module("shoppingCart", []);
app.controller("cartCtrl", function($scope) {
$scope.products = [{
id: 1,
name: 'Baby Mix Lobster (300g - down)',
unit: 'per kg',
price: 2500,
qty: 1
},
{
id: 2,
name: 'Tiger Lobster (1kg - up)',
unit: 'per kg',
price: 5800,
qty: 1
},
{
id: 3,
name: 'Tiger Lobster (700g - 999g)',
unit: 'per kg',
price: 4500,
qty: 1
},
{
id: 4,
name: 'Tiger Lobster (500g-699g)',
unit: 'per kg',
price: 4200,
qty: 1
},
{
id: 5,
name: 'Tiger Lobster (300g-499g)',
unit: 'per kg',
price: 3900,
qty: 1
},
];
$scope.addToCart = function(item) {
alert(item);
}
});
<
/script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<table style="border: 1px solid black">
<tr>
<th></th>
<th>name</th>
<th>unit</th>
<th>price</th>
<th>Qty</th>
<th></th>
</tr>
<tr ng-repeat="x in products">
<td>{{$index + 1}}</td>
<td>{{x.name}}</td>
<td>{{x.unit}}</td>
<td>{{x.price | currency: "Php " : 2}}</td>
<td><input type="number" min="1" placeholder="{{x.qty}}" /></td>
<td><button ng-click="addToCart($index)">Add to Cart</button></td>
</tr>
</table>
</div>
need to pass the item x instead of the index
ng-click="addToCart(x)"
If you are able to get the index, then why not fetch the object from the array?
$scope.addToCart = function(index) {
var item = $scope.products[index];
// access the properties of `item` here.
}
Also, it is recommended to track the items of ng-repeat:
ng-repeat="x in products track by $index"

Sorting only certain arrays in nested ngRepeat

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>

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>

ng-repeat in a table with colspan and rowspan

Here's a fiddle with the desired table and the Javascript function containing the array from where I want to populate the table, what I don't figure out is how, because if I use rowspan and colspan I have to create different <tr> for each product...
If there's another way to get the desired table I'd love to know about it... The main question here is: How could I use ng-repeat in a table that uses rowspan and colspan?
Also, the colspan and rowspan values at the <thead> can't be static as in the jsfiddle since each row may contain different amount of products... So the second question is: How could I dynamically set the rowspan value? they should be specified in each table row..
This is possible if you let the rowspan depend on inventory.length for the current transaction, and then you use nested ng-repeats.
Here we go:
var myApp = angular.module('myApp', []);
function MainCtrl($scope) {
var vm = $scope;
vm.hello = 123;
vm.transactions = [{
id: 1,
cost: 100,
transaction_type: { id: 1, name: 'Sell' },
client: { id: 2, name: 'XCLIENT' },
inventory: [
{ id: 1, quantity: 4, product: { id: 1, name: 'Cup' }, product_condition: { id: 2, name: 'New' } },
{ id: 2, quantity: 10, product: { id: 2, name: 'Shirt' }, product_condition: { id: 2, name: 'New' } }
]
}, {
id: 2,
cost: 40,
transaction_type: { id: 2, name: 'Buy' },
supplier: { id: 3, name: 'XSUPPLIER' },
inventory: [
{ id: 1, quantity: 2, product: { id: 1, name: 'Cup' }, product_condition: { id: 2, name: 'New' } },
{ id: 2, quantity: 5, product: { id: 6, name: 'Pants' }, product_condition: { id: 2, name: 'New' } }
]
}];
}
table,
th,
td {
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<table ng-controller="MainCtrl">
<thead>
<div>
<tr>
<td rowspan=2>Movement</td>
<td colspan=3>Products</td>
<td rowspan=2>Supplier</td>
<td rowspan=2>Cost</td>
</tr>
<tr>
<td>Name</td>
<td>Quantity</td>
<td>Condition</td>
</tr>
</div>
</thead>
<tbody ng-repeat="t in transactions">
<tr>
<td rowspan="{{t.inventory.length}}">Sell</td>
<td>{{t.inventory[0].product.name}}</td>
<td>{{t.inventory[0].quantity}}</td>
<td>{{t.inventory[0].product_condition.name}}</td>
<td rowspan="{{t.inventory.length}}">XCLIENT</td>
<td rowspan="{{t.inventory.length}}">$100</td>
</tr>
<tr ng-repeat="item in t.inventory" ng-if="$index > 0">
<td>{{item.product.name}}</td>
<td>{{item.quantity}}</td>
<td>{{item.product_condition.name}}</td>
</tr>
</tbody>
</table>
</div>
(Fiddle)
Here it is the totally dynamic way including dynamic rowspan :
HTML:
<div ng-app>
<h3>Here's what I want it to be</h3>
<div ng-controller="MainCtrl">
<table >
<thead>
<tr>
<td rowspan='{{rowspan}}'>Movement</td>
<td colspan='{{rowspan}}'>Products</td>
<td rowspan='{{rowspan}}'>Supplier</td>
<td rowspan='{{rowspan}}'>Cost</td>
</tr>
<tr>
<td>Name</td>
<td>Quantity</td>
<td>Condition</td>
</tr>
</thead>
<tbody ng-repeat='t in transactions'>
<tr ng-init='invCustom=(t.invetory.splice(1))'>
<td rowspan='{{rowspan}}'>{{t.transaction_type.name}}</td>
<td>{{t.invetory[0].product.name}}</td>
<td>{{t.invetory[0].quantity}}</td>
<td>{{t.invetory[0].product_condition.name}}</td>
<td ng-if='$index==0' rowspan='{{rowspan}}'>{{t.client.name}}</td>
<td ng-if='$index==1' rowspan='{{rowspan}}'>{{t.supplier.name}}</td>
<td rowspan='{{rowspan}}'>{{ t.cost | currency }}</td>
</tr>
<tr ng-repeat='tsub in invCustom'>
<td>{{tsub.product.name}}</td>
<td>{{tsub.quantity}}</td>
<td>{{tsub.product_condition.name}}</td>
</tr>
</tbody>
</table>
</div>
<h4>This data isn't loaded from the controller, I'd like to know how to use ng-repead in this case</h4>
</div>
JS:
function MainCtrl($scope) {
//var vm = this;
$scope.rowspan = 3;
$scope.transactions = [{
id: 1,
cost: 100,
transaction_type: {
id: 1,
name: 'Sell'
},
client: {
id: 2,
name: 'XCLIENT'
},
invetory: [{
id: 1,
quantity: 4,
product: {
id: 1,
name: 'Cup'
},
product_condition: {
id: 2,
name: 'New'
}
}, {
id: 2,
quantity: 10,
product: {
id: 2,
name: 'Shirt'
},
product_condition: {
id: 2,
name: 'New'
}
},
{
id: 3,
quantity: 101,
product: {
id: 3,
name: 'Shirt_C'
},
product_condition: {
id: 3,
name: 'New_C'
}
}]
}, {
id: 2,
cost: 40,
transaction_type: {
id: 2,
name: 'Buy'
},
supplier: {
id: 3,
name: 'XSUPPLIER'
},
invetory: [{
id: 1,
quantity: 2,
product: {
id: 1,
name: 'Cup'
},
product_condition: {
id: 2,
name: 'New'
}
}, {
id: 2,
quantity: 5,
product: {
id: 6,
name: 'Pants'
},
product_condition: {
id: 2,
name: 'New'
}
},
{
id: 3,
quantity: 55,
product: {
id: 7,
name: 'Pants_C'
},
product_condition: {
id: 8,
name: 'New_C'
}
}]
}];
}
try below code and do not forgot to include angular JS library....
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.2/angular.min.js
"></script>
<div ng-app>
<h3>Here's what I want it to be</h3>
<div ng-controller="MainCtrl">
<table >
<thead>
<tr>
<td rowspan=2>Movement</td>
<td colspan=3>Products</td>
<td rowspan=2>Supplier</td>
<td rowspan=2>Cost</td>
</tr>
<tr>
<td>Name</td>
<td>Quantity</td>
<td>Condition</td>
</tr>
</thead>
<tbody ng-repeat='t in transactions' ng-init='i=0'>
<tr>
<td rowspan=2>{{t.transaction_type.name}}</td>
<td>{{t.invetory[i].product.name}}</td>
<td>{{t.invetory[i].quantity}}</td>
<td>{{t.invetory[i].product_condition.name}}</td>
<td ng-if='$index==0' rowspan=2>{{t.client.name}}</td>
<td ng-if='$index==1' rowspan=2>{{t.supplier.name}}</td>
<td rowspan=2>{{ t.cost | currency }}</td>
</tr>
<tr>
<td>{{t.invetory[i+1].product.name}}</td>
<td>{{t.invetory[i+1].quantity}}</td>
<td>{{t.invetory[i+1].product_condition.name}}</td>
</tr>
</tbody>
</table>
</div>
<h4>This data isn't loaded from the controller, I'd like to know how to use ng-repead in this case</h4>
</div>
How could I use ng-repeat in a table that uses rowspan and colspan?
Yes you can use, I will put a very basic example and not fix the entire thing for you.
<table>
<tr ng-repeat="item in mylist">
<td ng-if="someCondition2" rowspan="2">AAA</td>
<td ng-if="someCondition1" colspan="2">BBB</td>
<td ng-if="someCondition3">CCC</td>
<tr>
</table>
This way you can conditionally add rowspan or colspan where ever required, hope you get the idea.

Resources