Angular bind table to cross data - angularjs

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?

Related

Vue.js create selector that shows Data from selected Dataset

Hello Everybody I've tried to create a select input field where i fill the options with a label from a dataset i created, which looks like this:
visitsList: [
{
label: '2017',
values: [
{ id: 1, title: "January", value: 20000 },
{ id: 2, title: "February", value: 30000 },
{id: 3,title: "March", value: 40000},
{ id: 4, title: "April", value: 40000},
{id: 5,title: "May",value: 50000},
{ id: 6,title: "June",value: 60000},
{id: 7, title: "July",value: 20000},
{ id: 8,title: "August", value: 70000},
{ id: 9,title: "September",value: 70000},
{id: 10, title: "October",value: 80000},
{id: 11,title: "November",value: 90000},
{id: 12,title: "December",value: 100000}
]},
{
label: '2018',
values: [
{ id: 1, title: "January", value: 20000 },
{ id: 2, title: "February", value: 30000 },
{id: 3,title: "March", value: 40000},
{ id: 4, title: "April", value: 40000},
{id: 5,title: "May",value: 50000},
{ id: 6,title: "June",value: 60000},
{id: 7, title: "July",value: 20000},
{ id: 8,title: "August", value: 70000},
{ id: 9,title: "September",value: 70000},
{id: 10, title: "October",value: 80000},
{id: 11,title: "November",value: 90000},
{id: 12,title: "December",value: 100000}
]},
{
label: '2019',
values: [
{ id: 1, title: "January", value: 20000 },
{ id: 2, title: "February", value: 30000 },
{id: 3,title: "March", value: 40000},
{ id: 4, title: "April", value: 40000},
{id: 5,title: "May",value: 50000},
{ id: 6,title: "June",value: 60000},
{id: 7, title: "July",value: 20000},
{ id: 8,title: "August", value: 70000},
{ id: 9,title: "September",value: 70000},
{id: 10, title: "October",value: 80000},
{id: 11,title: "November",value: 90000},
{id: 12,title: "December",value: 100000}
]
}
],
selectedYear: [],
The goal is if I select an option with the year it should show the values.
The template looks like this
<select v-model="widget.selectedYear">
<option v-for="year in widget.visitsList" v-bind:key="year.values">
{{year.label}}
</option>
</select>
<!--v-select :option="widget.visitsList.label" ></v-select-->
<table class="table table-bordered table-striped mb-0">
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Views</th>
</tr>
</thead>
<tbody>
<tr v-for="year in widget.visitsList" v-bind:key="year.label" ><!--v-if="year.label == selectedYear"-->
<th scope="row">{{visit.title}}</th>
<td>{{visit.value}}</td>
</div>
</tr>
</tbody>
</table>
I tried so many things, but somehow i didn't find the right solution. Maybe there is no solution.
Thanks for the help
Regards Maxim
I think you should use computed properties for this, bind your first selector to a data property then you can have a computed property watch that data property and return the values for the second select based on the changes in the data property.

Call a function in loop

The problem is that I have a list of people and their city id. I want to get the city name based on their id from another list by a function.
<table class="table">
<tr>
<th>#</th>
<th>Name</th>
<th>Type</th>
<th>City</th>
</tr>
<tr ng-repeat="item in samples">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.type}}</td>
<td>{{getCity(item.city)}}</td>
</tr>
</table>
and the controller:
$scope.samples = [
{id: 1, name: "alex", type: "Average", city: 12},
{id: 2, name: "Alex", type: "Average", city: 12},
{id: 3, name: "Mia", type: "Medium", city: 13},
{id: 4, name: "Sasha", type: "Top", city: 14},
{id: 5, name: "Eric", type: "Top", city: 12},
{id: 6, name: "Taz", type: "Average", city: 14},
{id: 7, name: "Normai", type: "Low", city: 13},
{id: 8, name: "Jim", type: "Average", city: 11}];
$scope.city = [
{id: 11, name: "Dallas"},
{id: 12, name: "Los Angeles"},
{id: 13, name: "New York"},
{id: 14, name: "Washington"}
];
$scope.getCity = function(name) {
angular.forEach($scope.city, function(value, key){
if(value.id == name){
$scope.city_name = value.name;
}
return $scope.city_name;
});
}
Here is a Fiddle for more details.
you are return value at wrong place. I just update the jsfiddle you can check here.
Here is the changes of the code.
$scope.getCity = function(name) {
$scope.city_name = "";
angular.forEach($scope.city, function(value, key){
if(value.id == name){
$scope.city_name = value.name;
}
});
return $scope.city_name;
}
}]);
Try this function
$scope.getCity = function(id) {
var city = $scope.city.filter(function(c){ return angular.equals(c.id, id); })[0];
return city.name;
}

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;
}

Angular - multiple ng repeat in table

[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>

How do i create a table with a variable amount of columns and dynamic titles in Angular? pref using ng-tables

i am trying create a table from a set of data using ng-tables. The data might not be the same every time. For example, i have these data:
var data = [
{name: "St.Gabriels", MeaslesCR: 50, ANC: 30, OPD: 20, SCORE: 100},
{name: "N'Lale", MeaslesCR: 52, ANC: 33, OPD: 20, SCORE: 90},
{name: "Centrum Hospital", MeaslesCR: 20, ANC: 70, OPD: 30, SCORE: 80},
{name: "Something Hospital", MeaslesCR: 20, ANC: 50, OPD: 30, SCORE: 70},
{name: "Wohoo Clinic", MeaslesCR: 50, ANC: 30, OPD: 40, SCORE: 60}];
But i may for example, not have MeaslesCR included.
So how do i create a table without manually creating all the columns with their respective titles?
I tried something like this:
var indicators = [];
//Getting indicator names
angular.forEach(data[0], function(value, key){
indicators.push(key);
});
$scope.getTitle = function(i){
return indicators[i];
}
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
total: data.length, // length of data
getData: function($defer, params) {
// use build-in angular filter
var orderedData = params.sorting() ?
$filter('orderBy')(data, params.orderBy()) :
data;
$defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
and
<table ng-table="tableParams" class="table">
<tr ng-repeat="user in $data">
<td data-title="getTitle($index)" sortable="Name">
{{user.name}} // pref user.getTitle($index)
</td>
</tr>
</table>
The html code obviously only writes one cell, but can i make it write out "$data.length" amount of cells somehow(this case 5)?
With dynamic titles using getTitle($index) or some other better alternative?
Then fill these columns with their respective user.varValue(user.name, user.MeaslesCR ect)?
I am sorry if my questions are a bit confusing, because i think i am a bit confused myself.
Thanks alot.
Took a bit of work, but this is a working plnkr does what I think you're trying to do.
http://plnkr.co/edit/7SqPoD2u323KKzi0SYpa?p=preview
The notSorted function I got from another post discussing how to guarantee order of iteration over an object. Required to make sure the column and headers are in the same order. You can simplify and get rid of that if you don't need column headers.
And the ng-if is to leave out an extraneous column that I think is also introduced by the notSorted function. Could probably not have that either if you get rid of the headers.
html:
<table border=1>
<thead>
<tr>
<th ng-repeat="key in notSorted(cols)" ng-init="value=cols[key]">{{value}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data">
<td ng-if="!$last" ng-repeat="dat in notSorted(row)" ng-init="value=row[dat]">{{value}}</td>
</tr>
</tbody>
</table>
script:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.data = [
{name: "St.Gabriels", MeaslesCR: 50, ANC: 30, OPD: 20, SCORE: 100},
{name: "N'Lale", MeaslesCR: 52, ANC: 33, OPD: 20, SCORE: 90},
{name: "Centrum Hospital", MeaslesCR: 20, ANC: 70, OPD: 30, SCORE: 80},
{name: "Something Hospital", MeaslesCR: 20, ANC: 50, OPD: 30, SCORE: 70},
{name: "Wohoo Clinic", MeaslesCR: 50, ANC: 30, OPD: 40, SCORE: 60}
];
$scope.cols = Object.keys($scope.data[0]);
$scope.notSorted = function(obj){
if (!obj) {
return [];
}
return Object.keys(obj);
}
});
The notSorted() function was found here:
https://groups.google.com/forum/#!topic/angular/N87uqMfwcTs

Resources