Angular - multiple ng repeat in table - angularjs

[EDIT : Solved but i think there's a better way, if you find it, i'm still interested]
I have a list of banks.
Each bank contains a list of accounts.
Each accounts contains a list of operations.
I would like to display all the operations in a table.
Here's what the bank list looks like :
$scope.banks = [
{
id: 1,
name: 'bank_1',
accounts: [
{
id: 1,
name: 'account_1',
operations: [
{id: 1, name:'operation_1', price:100},
{id: 2, name:'operation_2', price:200},
{id: 3, name:'operation_3', price:300},
{id: 4, name:'operation_4', price:400}
]
},
{
id: 2,
name: 'account_2',
operations: [
{id: 5, name:'operation_5', price:100},
{id: 6, name:'operation_6', price:200},
{id: 7, name:'operation_7', price:300},
{id: 8, name:'operation_8', price:400}
]
}
]
},
{
id: 2,
name: 'bank_2',
accounts: [
{
id: 3,
name: 'account_3',
operations: [
{id: 9, name:'operation_9', price:100},
{id: 10, name:'operation_10', price:200},
{id: 11, name:'operation_11', price:300},
{id: 12, name:'operation_12', price:400}
]
},
{
id: 4,
name: 'account_4',
operations: [
{id: 13, name:'operation_13', price:100},
{id: 14, name:'operation_14', price:200},
{id: 15, name:'operation_15', price:300},
{id: 16, name:'operation_16', price:400}
]
}
]
}
];
In order to display that properly with Angular i wanted to make something like that :
<table>
<tr>
<th>Banque</th>
<th>Account</th>
<th>ID OP</th>
<th>Name</th>
<th>Price</th>
</tr>
<div ng-repeat="bank in banks">
<div ng-repeat="account in bank.accounts">
<div ng-repeat="operation in account.operations">
<tr>
<td>{{banque.name}}</td>
<td>{{account.name}}</td>
<td>{{operation.id}}</td>
<td>{{operation.name}}</td>
<td>{{operation.price}}</td>
</tr>
</div>
</div>
</div>
</table>
But i know it's not correct to break the table > tr > td.
After some researches, i think i may have to use the ng-repeat-start, ng-repeat-end directive but i don't understand how and i'm not even sure it's the good thing to do.
If you have any idea how to solve that i'd be glad to hear your solution.

This can be easily achieved by creating custom filter that will return the list of operation.
Filter
.filter('filterData', function(){
return function(data, firstParam, secondParam){
var returnData = [];
angular.forEach(data, function(value, index){
angular.forEach(value[firstParam], function(val, ind){
angular.forEach(val[secondParam], function(v, i){
returnData.push(v)
});
});
});
return returnData;
}
});
Markup
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
</tr>
<tr ng-repeat="operation in banks| filterData: 'accounts': 'operations'">
<td>{{operation.id}}</td>
<td>{{operation.name}}</td>
<td>{{operation.price}}</td>
</tr>
</table>
Working Plunkr Here
Hope this could help you, Thanks.

Thx you saved my day.
I enhance your solution because filterData function is running twice (for each element of $scope.banks array).
Prefers ng-init in tbody container of tr_ngrepeat.
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody ng-init="operationlist=(banks|filterData: 'accounts': 'operations')">
<tr ng-repeat="operation in operationlist">
<td>{{operation.id}}</td>
<td>{{operation.name}}</td>
<td>{{operation.price}}</td>
</tr>
</tbody>
</table>

Related

DataTables - Requested unknown parameter on 3 of 7 Columns

I'm at my wits end with this issue and hoping someone can help. I've perused all the other answers and none of the solutions offered are resolving my problem. The following is the JSON string:
{
Description="XT86 EXTENDED WARRANTY (3 YEARS)",
Id=88,
InternalPartNumber="000-063",
ManufacturerPartNumber="000-063",
SellPrice=350.00,
Cost=280.00,
Category="Maintenance"
}
The following is my JS:
$('#products').dataTable({
serverSide: true,
ajax: {
url: "/Product/LoadProducts",
type: "POST",
datatype: "json"
},
columns: [
{ data: "id" },
{ data: "internalpartnumber" },
{ data: "manufacturerpartnumber" },
{ data: "description" },
{ data: "category" },
{ data: "sellprice" },
{ data: "cost" }
]
});
The HTML is as follows:
<div class="shadow-lg p-1 mb-1 bg-secondary rounded">
<table class="table table-hover table-striped table-bordered table-sm w-100" id="products">
<thead class="bg-primary">
<tr style="cursor:pointer;" class="text-light">
<th scope="col">Id</th>
<th scope="col">DCT Part #</th>
<th scope="col">MFG Part #</th>
<th scope="col">Description</th>
<th scope="col">Category</th>
<th scope="col">Sell Price</th>
<th scope="col">Cost</th>
</tr>
</thead>
</table>
</div>
The result is the following:
DataTables warning: table id=products - Requested unknown parameter
'internalpartnumber' for row 0, column 1. For more information about
this error, please see http://datatables.net/tn/4
Clicking OK on the error does load the table but 3 columns are missing data.
I've tried removing { data: "internalpartnumber" } but then I get the error on ManufacturingPartNumber. When I remove { data: "manufacturerpartnumber" } I get the error on SellPrice. When I remove { data: "sellprice" } I get no errors!
When I view the data from Visual Studio, the order the data is in is the same as I have it for each "index" order however when you expand each row index, it is being sorted.
Any help that can be provided would be much appreciated.
The LoadProducts method is as follows:
var results = (from p in _productRepository.GetAllActive
select new
{
p.Id,
p.InternalPartNumber,
p.ManufacturerPartNumber,
p.Description,
Category = p.ProductCategory.Display,
p.SellPrice,
p.Cost
});
Your Columns in your DataTable are not set up in an order as your data is
HTML
<table id="products" class="display" style="width:100%">
<thead>
<tr>
<th>Description</th>
<th>Id</th>
<th>InternalPartNumber</th>
<th>ManufacturerPartNumber</th>
<th>SellPrice</th>
<th>Cost</th>
<th>Category</th>
</tr>
</thead>
</table>
Jquery
var ret = {
data: [
{
Description: "XT86 EXTENDED WARRANTY (3 YEARS)",
Id: 88,
InternalPartNumber: "000-063",
ManufacturerPartNumber: "000-063",
SellPrice: 350.00,
Cost: 280.00,
Category: "Maintenance"
}
]
};
$('#products').dataTable({
data: ret.data,
columns: [
{ data: "Description" },
{ data: "Id" },
{ data: "InternalPartNumber" },
{ data: "ManufacturerPartNumber" },
{ data: "SellPrice" },
{ data: "Cost" },
{ data: "Category" }
]
});
I was finally able to figure this out. In Chrome, I selected the Network Option under the developer tools to examine the payload. What I noticed is that the first letter of the columns was lowercased which was different than what Visual Studio was showing.
I updated my columns definitions to the following:
columns: [
{ data: "id", name: "id", autoWidth: true },
{ data: "internalPartNumber", name: "internalPartNumber", autoWidth: true },
{ data: "manufacturerPartNumber", name: "manufacturerPartNumber", autoWidth: true },
{ data: "description", name: "description", autoWidth: true },
{ data: "category", name: "category", autoWidth: true },
{ data: "sellPrice", render: $.fn.dataTable.render.number(',', '.', 2, '$') },
{ data: 'cost', render: $.fn.dataTable.render.number(',', '.', 2, '$') }
]
and now everything works.

ng-table data not showing

here my question:
I am trying to build a directive in angular, which would use an ng-table inside. I am using mocked data. The problem is that I cannot render data in the table. here is my directive:
;(function(app) {
app.directive('customTables', [
'NgTableParams',
function(NgTableParams) {
return {
restrict: 'E',
templateUrl: 'templates/directives/table_directive.html',
scope: {
},
link: function(scope) {
var dataset = [{
name: "Moroni50",
age: "50"
}, {
name: "Moroni49",
age: "49"
}];
scope.tableParams = new NgTableParams({}, {
data: dataset
});
}
};
}]);
})(app);
here is the html:
<table ng-table="tableParams" class="table table-bordered table-striped table-condensed">
<tr ng-repeat="user in $data">
<td title="'Name'" filter="{ name: 'text'}" sortable="'name'">{{user.name}}</td>
<td title="'Age'" filter="{ age: 'number'}" sortable="'age'">{{user.age}}</td>
</tr>
here is how I call it:
<custom-table></custom-table>
Any suggestions?
Replace
scope.tableParams = new NgTableParams({}, {
data: dataset
});
with
scope.tableParams = new NgTableParams({}, {
scope.data: dataset
});
and in html:
<tr ng-repeat="user in $data">
with
<tr ng-repeat="user in data">
Try this:
scope.data = [{
name: "Moroni50",
age: "50"
}, {
name: "Moroni49",
age: "49"
}];
scope.tableParams = new NgTableParams({}, {
data: scope.data
});
Try this instead
scope.tableParams = new NgTableParams({}, {
dataset: dataset
});

Angular bind table to cross data

Basically I am trying to two-way bind to a table of values based on the intersection of point in the table.
I've set up a plnkr of what I am trying to achieve here...
Angular Bind To Value Table
So given this set of data...
$scope.beers = [
{ id: 27, description: "Hopslam Ale" },
{ id: 28, description: "Founders Kentucky Breakfast Stout" },
{ id: 29, description: "Zombie Dust" } ];
$scope.characteristics = [
{ id: 3, description: "ABV" },
{ id: 4, description: "IBU" },
{ id: 5, description: "Calories" },
{ id: 6, description: "Reviews"}];
$scope.crossData = [
{ beerId: 27, characteristicId: 3, value: 10 },
{ beerId: 27, characteristicId: 4, value: 70 },
{ beerId: 27, characteristicId: 5, value: 300 },
{ beerId: 27, characteristicId: 6, value: 3419 },
{ beerId: 28, characteristicId: 3, value: 11 },
{ beerId: 28, characteristicId: 4, value: 70 },
{ beerId: 28, characteristicId: 5, value: 336 },
{ beerId: 28, characteristicId: 6, value: 2949 },
{ beerId: 29, characteristicId: 3, value: 6 },
{ beerId: 29, characteristicId: 4, value: 50 },
{ beerId: 29, characteristicId: 5, value: 186 },
{ beerId: 29, characteristicId: 6, value: 1454 }];
How can I two-way bind to the value in the cross data?
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th ng-repeat="char in characteristics">
{{ char.description }}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="beer in beers">
<td>{{ beer.description }}</td>
<td ng-repeat="char in characteristics">
<!-- some kind of binding expression here -->
</td>
</tr>
</tbody>
</table>
Any help would be greatly appreciated.
Sorry for my last answer. I did not notice the two way data bind need.
So, I've found two solutions
1 - This one filter the crossData on the html: http://plnkr.co/edit/M1rnx6IJqJ95apJJ55iN
<tbody>
<tr ng-repeat="beer in beers">
<td>{{ beer.description }}</td>
<td ng-repeat="char in characteristics">
<input ng-model="(crossData | filter:{beerId: beer.id, characteristicId: char.id})[0].value"/>
</td>
</tr>
</tbody>
2 - This one uses a function to filter the crossData: http://plnkr.co/edit/MHQy31rqZMg2TXYn0P23
What I did was Add a function to return the object of the crossData list. The function is as follow:
$scope.getCrossDataRow = function(beerId, charId){
return $filter('filter')($scope.crossData, {beerId: beerId, characteristicId: charId})[0];
};
Do not forget to load the $filter in your controller.
Finally, you can use this function in your table row to bind the value as follow:
<td ng-repeat="char in characteristics">
<input type="text" ng-model="getCrossDataRow(beer.id, char.id).value" />
</td>
You can write a 'matching' function in the controller like this (current implementation just returns the first match):
$scope.findMatch = function(beer, characteristic) {
return $scope.crossData.filter(function(data) {
return data.beerId === beer.id && data.characteristicId === characteristic.id
})[0];
}
Working code snippet:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.beers = [
{ id: 27, description: "Hopslam Ale" },
{ id: 28, description: "Founders Kentucky Breakfast Stout" },
{ id: 29, description: "Zombie Dust" } ];
$scope.characteristics = [
{ id: 3, description: "ABV" },
{ id: 4, description: "IBU" },
{ id: 5, description: "Calories" },
{ id: 6, description: "Reviews"}];
$scope.crossData = [
{ beerId: 27, characteristicId: 3, value: 10 },
{ beerId: 27, characteristicId: 4, value: 70 },
{ beerId: 27, characteristicId: 5, value: 300 },
{ beerId: 27, characteristicId: 6, value: 3419 },
{ beerId: 28, characteristicId: 3, value: 11 },
{ beerId: 28, characteristicId: 4, value: 70 },
{ beerId: 28, characteristicId: 5, value: 336 },
{ beerId: 28, characteristicId: 6, value: 2949 },
{ beerId: 29, characteristicId: 3, value: 6 },
{ beerId: 29, characteristicId: 4, value: 50 },
{ beerId: 29, characteristicId: 5, value: 186 },
{ beerId: 29, characteristicId: 6, value: 1454 }];
$scope.name = 'Stack Overflow friends';
$scope.findMatch = function(beer, characteristic) {
return $scope.crossData.filter(function(data) {
return data.beerId === beer.id && data.characteristicId === characteristic.id
})[0];
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="plunker" ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<br />
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th ng-repeat="char in characteristics">
{{ char.description }}
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="beer in beers">
<td>{{ beer.description }}</td>
<td ng-repeat="char in characteristics">
{{findMatch(beer, char).value}}
</td>
</tr>
</tbody>
</table>
</body>
You could do this with this expression:
<span ng-repeat="cross in crossData" ng-if="cross.beerId === beer.id && cross.characteristicId === char.id">{{cross.value}}</span>
But it would be better if you had another data structure, but i guess you have no influence on this?

How get a label from a store in Angular while repeat another store

I've two stores in my controller:
$scope.categoryStore = [{id: 1, label: "A"}, {id: 2, label: "B"}, {id: 3, label: "C"}];
$scope.collectionStore = [{id: 1, categoryId: 1, name: "Test"}, {id: 1, categoryId: 2, name: "Test2"}];
In my view I have a ng-repeat for the collectionStore. Now I want to display the label of the categoryStore instead of the categoryId.
<tr ng-repeat="item in collectionStore">
<td>{{item.id}}</td>
<td>{{item.categoryid}} <!-- this would be the label --></td>
<td>{{item.name}}</td>
</tr>
My suggestion is to create a function to handle this and use the $filter within that function.
In your view:
<tr ng-repeat="item in collectionStore">
<td>{{item.id}}</td>
<td>{{getCategoryLabel(item.categoryId)}}</td> <!-- handle this with a function -->
<td>{{item.name}}</td>
</tr>
Then in your controller:
$scope.getCategoryLabel = function(categoryId) {
var category = $filter('filter')($scope.categoryStore, { id: categoryId }, true);
if (category.length) return category[0].label;
}

Knockout update cell value in table

When I change selection in column B, the text in column A should change too, but not.
Why?
HTML:
<table>
<thead>
<tr>
<th style="width: 100px;">A</th>
<th style="width: 100px;">B</th>
</tr>
</thead>
<tbody data-bind="foreach: Data">
<tr>
<td><span data-bind="text: idOpt"></span></td>
<td><select data-bind="options: $root.MyOptions, optionsText: 'name', optionsValue: 'id', value: idOpt"></select></td>
</tr>
</tbody>
</table>
JS:
function AppViewModel() {
var self = this;
self.MyOptions = ko.observableArray([
{id: 'a1', name: 'One'},
{id: 'a2', name: 'Two'},
{id: 'a3', name: 'Three'}
]);
self.Data = ko.observableArray([
{idOpt: 'a1'},
{idOpt: 'a2'},
{idOpt: 'a1'}
]);
}
var vm = new AppViewModel();
ko.applyBindings(vm);
http://jsfiddle.net/bnowicki/CrVBr/2/
Please help.
You must declare the items in your Data array as observables if you want them to bind/update.
function AppViewModel() {
var self = this;
self.MyOptions = ko.observableArray([
{id: 'a1', name: 'One'},
{id: 'a2', name: 'Two'},
{id: 'a3', name: 'Three'}
]);
self.Data = ko.observableArray([
{idOpt: ko.observable('a1')},
{idOpt: ko.observable('a2')},
{idOpt: ko.observable('a1')}
]);
}
var vm = new AppViewModel();
ko.applyBindings(vm);
The Knockout documentation shows you the difference between using plain models, and then models with observables http://knockoutjs.com/documentation/observables.html

Resources