ng-repeat in a table with colspan and rowspan - angularjs

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.

Related

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>

Show sorted Icon on sorting in angularJS using ng-repeat

I am not able to show proper icon on sorting of column, if I click of one column for sorting then icons of all other columns also gets updated.
Only sorted column should get update on sorting of respective column.
I have listed my code here : http://plnkr.co/edit/UonOgNz0s16iKJkWLwyg?p=preview
HTML Code :
<table border="1">
<thead>
<tr>
<th ng-repeat="key in getKeysOfCollection(colors[0])" ng-click="ctrl.predicate = key; ctrl.reverse=!ctrl.reverse;">
{{key}}
<span class="fa fa-sort columnSortIcons" ng-if="ctrl.reverse"></span>
<span class="fa fa-sort-down columnSortIcons" ng-if="!(ctrl.reverse)"></span>
<span class="fa fa-sort-up columnSortIcons" ng-if="ctrl.reverse"></span>
</th>
</tr>
<tr>
<th ng-repeat="key in getKeysOfCollection(colors[0])">
<input type="text" ng-model="search[key]" />
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in colors | filter:search | orderBy:ctrl.predicate:ctrl.reverse">
<td ng-repeat="key in getKeysOfCollection(item)">{{item[key]}}</td>
</tr>
</tbody>
</table>
JS Code :
this.predicate='id';
this.reverse=false;
$scope.search = {};
$scope.colors = [{
'id': 1,
'productId': 1001,
'productName': 'prd 1',
'minimumLevel': 2,
'price': 12.50,
'productDate': '2014-11-01T06:41:30.809Z'
}, {
'id': 2,
'productId': 1002,
'productName': 'prd 2',
'minimumLevel': 23,
'price': 12.54,
'productDate': '2014-11-02T06:41:30.809Z'
}, {
'id': 3,
'productId': 1003,
'productName': 'prd 3',
'minimumLevel': 2,
'price': 12.50,
'productDate': '2014-11-04T06:41:30.809Z'
}, {
'id': 4,
'productId': 1004,
'productName': 'prd 4',
'minimumLevel': 2,
'price': 12.50,
'productDate': '2014-11-22T06:41:30.809Z'
}, {
'id': 5,
'productId': 1005,
'productName': 'prd 5',
'minimumLevel': 2,
'price': 12.50,
'productDate': '2014-11-18T06:41:30.809Z'
}];
$scope.getKeysOfCollection = function(obj) {
obj = angular.copy(obj);
if (!obj) {
return [];
}
return Object.keys(obj);
}
In your ng-if statements you aren't checking to see if that column is the sorted column (the predicate):
<span class="fa fa-sort-down columnSortIcons" ng-if="!(ctrl.reverse) && key == ctrl.predicate"></span>
<span class="fa fa-sort-up columnSortIcons" ng-if="ctrl.reverse && key == ctrl.predicate"></span>
Updated code.

nested table using ng-repeat

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

Resources