AngularJs ng-repeat 2D array in table, each subarray one column - arrays

I have an array and I need to put that array in table.
$scope.testArr=[
{'first':[
{ 'value':'1_1', 'rolle':'one1' },
{ 'value':'2_1', 'rolle':'two1' },
{ 'value':'3_1', 'rolle':'three1'}
]
},
{'second': [
{ 'value':'1_2', 'rolle':'one2' },
{ 'value':'2_2', 'rolle':'two2' },
{ 'value':'3_2', 'rolle':'three2' }
]
}
];
Resulting table should have 4 columns, each subarray should be one(or two) column(s). Like this:
one1 | 1_1 | one2 | 1-2
two1 | 2_1 | two2 | 2_2
three1|3_1 | three2|3_2
So far I got this. Its only the first subarray:
<table>
<tbody ng-repeat="test in testArr">
<tr ng-repeat="t1 in test.first">
<td> {{t1.rolle}} </td>
<td> {{t1.value}} </td>
</tr>
</tbody>
</table>
How can I add the second subarray as column? It's not necessary need to be a table.

var app = angular.module('app', []);
app.controller('mainCtrl', function ($scope) {
$scope.testArr = [{
'first': [{
'value': '1_1',
'rolle': 'one1'
}, {
'value': '2_1',
'rolle': 'two1'
}, {
'value': '3_1',
'rolle': 'three1'
}]
}, {
'second': [{
'value': '1_2',
'rolle': 'one2'
}, {
'value': '2_2',
'rolle': 'two2'
}, {
'value': '3_2',
'rolle': 'three2'
}]
}];
});
td {
border:solid 1px grey
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="mainCtrl">
<table>
<tbody ng-repeat="test in testArr">
<tr ng-repeat="t1 in test.first">
<td>{{t1.rolle}}</td>
<td>{{t1.value}}</td>
<td>{{testArr[1].second[$index].rolle}}</td>
<td>{{testArr[1].second[$index].value}}</td>
</tr>
</tbody>
</table>
</div>
</div>

Related

Vue v-for nested array

I've got a nested array that I would like to display in a table. However, I can't get my nested array to show correctly.
My data set looks like this:
[
{
"dd":"February",
"md":[
{ "dag":"2020-02-01" },
{ "dag":"2020-02-02" },
{ "dag":"2020-02-03" }
]
},
{
"dd":"March",
"md":[
{ "dag":"2020-03-01" },
{ "dag":"2020-03-02" },
{ "dag":"2020-03-03" }
]
}
]
I would like a table which look like this.
| February | March |
| 2020-02-01 | 2020-03-01 |
| 2020-02-02 | 2020-03-02 |
| 2020-02-03 | 2020-03-03 |
I got this working, but it gives me 2 tables instead of one.
<template v-for="(md2, index) in md2s">
<table :key=index >
<thead >
<tr align="center">
<th style="width: 80px">{{md2}}</th>
</tr>
</thead>
<tr v-for="(date, index) in md2.md" :key=index>
<td align="center" >{{date.dag }}</td>
</tr>
</table>
</template>
All help is appreciated.
br. Erik
You could use a different way to create the loop (one table, multiple columns)
In this case, to populate each header with 'dd' and each column with md elements.
var data=[
{
"dd":"February",
"md":[
{
"dag":"2020-02-01"
},
{
"dag":"2020-02-02"
},
{
"dag":"2020-02-03"
}
]
},
{
"dd":"March",
"md":[
{
"dag":"2020-03-01"
},
{
"dag":"2020-03-02"
},
{
"dag":"2020-03-03"
}
]
}
];
new Vue({
el:'#app',
data:{
md2s: data
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.8/vue.js"></script>
<div id=app >
<table >
<thead >
<tr align="center">
<th v-for="(md2, index) in md2s" :key=index style="width: 80px">{{md2.dd}}</th>
</tr>
</thead>
<tbody>
<tr align="center">
<td v-for="(md2, index) in md2s" :key=index style="width: 80px">
<div v-for="(mdcol, col) in md2.md" :key=col>
{{mdcol.dag}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
https://jsfiddle.net/bn5g1v09/1/
What you need is two diferent iterations. One for the header and another for the table body. For the header, all you need is to add the month name on order. The snippet shows with the computed property months how to do it. This completes the header iteration and the first.
The second one is a little more complex. You need to know beforehand how many lines there will be, for that I made a computed property maxLength that searches over each md and gives the greater one. Then for each row iterate over each month and then verify if the month has enough dates with v-if and if it does look up the desired date from the index and the nested data sctructure. That resumes the second iteration.
The below snippet is a working example with a more complex data showing what could happen with different md sizes and automatic month ordering.
var app = new Vue({
el: '#app',
data: () => ({
nested: [
{ "dd": "February",
"md": [{ "dag": "2020-02-01" },{ "dag": "2020-02-02" },{ "dag": "2020-02-03" },{ "dag": "2020-03-04" }]
},
{ "dd": "March",
"md": [{ "dag": "2020-03-01" },{ "dag": "2020-03-02" },{ "dag": "2020-03-03" }]
},
{ "dd": "January",
"md": [{ "dag": "2020-01-01" }]
}
]
}),
computed: {
staticMonths() {
return Array.from(Array(12),(e,i)=>new Date(25e8*++i).toLocaleString('en-US',{month: 'long'}));
},
months() {
return this.nested.map(item => item.dd).sort((a, b) => {
const A = this.staticMonths.indexOf(a);
const B = this.staticMonths.indexOf(b);
return A-B;
});
},
maxLength() {
return this.nested.reduce((accum, curr) => accum > curr.md.length ? accum : curr.md.length, 0);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<tr>
<th v-for="(item, index) in months">{{ item }}</th>
</tr>
</thead>
<tbody>
<tr v-for="index in maxLength">
<td v-for="item in months">
<span v-if="nested.find(nest => nest.dd === item).md.length > index-1">
{{nested.find(nest=>nest.dd===item).md[index-1].dag}}
</span>
</td>
</tr>
</tbody>
</table>
</div>

seach filter inside a parent ng-repeat not working

I have an array of objects which is shown below which I called $scope.parlist. I already did a research so that I can filter my nested ng-repeat whenever the user search for a particular account but I failed
[
{
bch: "001",
loan_product: [
{
id_code: "ML1",
asofmonitoring:[{ //asofmonitoring is fixed to have just one object here
days07:[
{
loan_no: "ML-XXX-XXX-XXX",
​​​​​​ name: "John Papa"
},
{
loan_no: "ML-XXX-XXX-XXX",
​​​​​​ name: "Grace Papa"
}
...
],
days08:[
{
loan_no: "ML-XXX-XXX-XXX",
​​​​​​ name: "Earl Papa"
},
{
loan_no: "ML-XXX-XXX-XXX",
​​​​​​ name: "Britney Papa"
}
...
]
...
}]
},
...
]
}
...
]
html
<tbody data-ng-repeat="par in parlist" ng-init="outerindex = $index">
<tr>
<td colspan="15" style="background-color:rgb(233, 236, 239)">
<table class="table table-sm">
<tbody ng-repeat="prod in par.loan_product" ng-init="innerindex = $index">
<tr>
<table>
<tbody ng-if="prod.asofmonitoring[0].days07.length > 0">
<tr>
<td colspan="2" class="text-left table-warning">
<input type="text" ng-model="q7[innerindex]" class="form-control" placeholder="Search account" aria-label="Search account" aria-describedby="basic-addon2">
</td>
</tr>
</tbody>
<tbody ng-repeat="days07 in prod.asofmonitoring[0].days07 | filter:q7[innerindex]">
<tr>
<td class="text-center" ng-bind="days07.loan_no">loading...</td>
</tr>
</tbody>
</table>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
My problem is I can't make the search filter to work in my ng-repeat="days07 in prod.asofmonitoring[0].days07 ng-repeat. I already make the other suggestion like putting a ng-init="outerindex = $index" and other solution but my ng-repeat wont filter. Can anybody help me out with this problem?
You've got a scope-related binding issue here.
Both ng-if and ng-repeat implicitly create new scopes.
You've not included any controller code but I think I'm making a fair assumption that you've not explicitly defining q7. Consequently, when q7 appears inside the ng-if, only this scope will be able to access the bound model. The ng-repeat is on a sibling element and thus doesn't have the same visibility, which is why nothing happens when you change the text filter model.
Quick solution here would be to explicitly initialise q7 in your controller to ensure no variable shadowing occurs.
Included a stripped down example below for you:
Edit: Updated to reflect commentary.
angular
.module('app', [])
.controller('ctrl', function ($scope) {
// Explicitly declare text filter model so `ng-repeat` does not create shadowed copies
$scope.q = {};
$scope.parlist = [
{
bch: '001',
loan_product: [
{
id_code: 'ML1',
asofmonitoring: [
{
days07: [
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'John Papa',
},
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'Grace Papa',
},
],
days08: [
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'Earl Papa',
},
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'Britney Papa',
},
],
},
],
},
],
},
{
bch: '002',
loan_product: [
{
id_code: 'ML1',
asofmonitoring: [
{
days07: [
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'John Papa',
},
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'Grace Papa',
},
],
days08: [
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'Earl Papa',
},
{
loan_no: 'ML-XXX-XXX-XXX',
name: 'Britney Papa',
},
],
},
],
},
],
},
];
});
<div ng-app="app" ng-controller="ctrl">
<div ng-repeat="par in parlist">
<div ng-repeat="prod in par.loan_product">
<div ng-if="prod.asofmonitoring[0].days07.length">
<input type="text" ng-model="q[par.bch][$index]" placeholder="bch: {{par.bch}}">
</div>
<div ng-repeat="days07 in prod.asofmonitoring[0].days07 | filter:q[par.bch][$index]">
<div ng-bind="days07.loan_no">loading...</div>
</div>
</div>
</div>
<pre>q = {{ q | json }}</pre>
</div>
<script src="https://unpkg.com/angular#1.7.5/angular.min.js"></script>

angularjs ng-repeat with dynamic json/object

I am looking a solution for dynamic data structure(inconsistent like different property name and property length) with ng-repeat. sample code are below.
$scope.data = [{
"table":[{
"employee":"name1",
"value1":"10",
"value2":"20"
},
{
"employee":"name2",
"value1":"15",
"value2":"30"
}]
},
{
"table":[{
"company":"name1",
"compnayValue":"12"
},
{
"company":"name2",
"compnayValue":"12"
}]
}]
<ul>
<li ng-repeat="item in data">
<table>
<tr ng-repeat="row in item.table">
<td>{{??}}</td>
<td>{{??}}</td>
</tr>
</table>
</li>
</ul>
You could enumerate all properties and display their values by another ng-repeat on td:
<li ng-repeat="item in data">
<table>
<tr ng-repeat="row in item.table">
<td ng-repeat="(key, value) in row">
{{row[key]}}
</td>
</tr>
</table>
</li>
but that would break the tabular format of data since some rows would have more tds. To prevent that you could first find out the set of all keys on all rows, do a th repeat with those first and then display them on the corresponding td below, e.g.:
<th ng-repeat="propertyName in allPossiblePropertyNames">
{{propertyName}}
</th>
and
<td ng-repeat="propertyName in allPossiblePropertyNames">
{{row[propertyName ]}}
</td>
Use <tbody> to represent an object inside table array and (key,value) syntax mentioned in iterating over object properties section to iterate over it's properties like:
angular.module('test', []).controller('testCtrl', function($scope) {
$scope.data = [{
"table": [{
"employee": "name1",
"value1": "10",
"value2": "20"
}, {
"employee": "name2",
"value1": "15",
"value2": "30"
}]
}, {
"table": [{
"company": "name1",
"compnayValue": "12"
}, {
"company": "name2",
"compnayValue": "12"
}]
}]
});
ul {
padding: 0;
}
ul li {
list-style-type: none;
margin-bottom: 10px;
}
table {
width: 100%;
table-layout: fixed;
background: #ebebeb;
}
tbody:nth-child(odd) tr {
color: #fff;
background: dodgerblue;
}
tbody:nth-child(even) tr {
color: #fff;
background: hotpink;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="testCtrl">
<ul>
<li ng-repeat="item in data">
<table>
<tbody ng-repeat="row in item.table">
<tr ng-repeat="(key, value) in row">
<td>
{{key}}
</td>
<td>
{{value}}
</td>
</tr>
</tbody>
</table>
</li>
</ul>
</div>
Check this plunker, you can define template depends on your data :
https://plnkr.co/edit/fVGhKZy5gnBzuPwspy5s?p=preview
Use angular filter :
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.data = [{
"table":[{
"employee":"name1",
"value1":"10",
"value2":"20"
},
{
"employee":"name2",
"value1":"15",
"value2":"30"
}]
},
{
"table":[{
"company":"name1",
"compnayValue":"12"
},
{
"company":"name2",
"compnayValue":"12"
}]
}]
})
.filter('isCompnay', function() {
return function(input) {
console.log(input.employee === undefined)
return input.company ? input : undefined;
};
})
.filter('isEmployee', function() {
return function(input) {
console.log(input.employee === undefined)
return input.employee ? input : undefined;
};
});

Add new column to table using existing values

I'm using angularjs and have a table which I build from two scope objects. It would be really nice have some kind of functionality where I can add columns by my self. The big problem is that I would like to use the existing values and try to use them in my new column. Is that possible? Or do I have to build up the columns on the server side and then return it?
PLUNKER
<!doctype html>
<html ng-app="plunker">
<head>
<script data-require="angular.js#*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng:controller="MainCtrl">
<table border="1">
<thead style="font-weight: bold;">
<tr>
<th class="text-right" ng-repeat="column in columnsTest" ng-if="column.checked" ng-bind="column.id"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in columnsTest" ng-if="column.checked">
{{ row[column.value] }}
</td>
</tr>
</tbody>
</table>
<br><br>
<input type="button" value="Add new column" ng-click="addColumn()" />
<br><br><br>
<p ng-repeat="c in columnsTest">Column {{$index}}: {{c}}</p>
</div>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $filter) {
$scope.addColumn = function() {
var newCol = { id: 'Value4', checked: true, value: 'Value1 + Value2 + Value3' }
$scope.columnsTest.push(newCol);
}
$scope.columnsTest = [{
id: 'Value1',
checked: true,
value: 'Value1'
}, {
id: 'Value2',
checked: true,
value: 'Value2'
}, {
id: 'Value3',
checked: true,
value: 'Value3'
}];
$scope.rows = [{
id: 1,
"Value1": 911,
"Value2": 20,
"Value3": 20
}, {
id: 2,
"Value1": 200,
"Value2": 20,
"Value3": 20
}];
});
</script>
You can use the $parse service.
Inject $parse into your controller and add a new method:
$scope.getCellValue = function(row, column) {
var getter = $parse(column.value);
return getter(row);
// Alternatively:
// return $parse(column.value)(row);
};
Use it like this:
<tr ng-repeat="row in rows">
<td ng-repeat="column in columnsTest" ng-if="column.checked">
{{ getCellValue(row, column) }}
</td>
</tr>
Demo: http://plnkr.co/edit/DVF2LXeZPqCL1Ik3EyVf?p=preview
Explanation:
The $parse service accepts a string expression to compile and returns a getter function. In your example we use the column.value:
var columnValue = 'Value1 + Value2 + Value3';
var getter = $parse(columnValue);
The returned getter function accepts a context object which the expression should be evaluated against. In your example we use the row object:
var row = { id: 1, "Value1": 911, "Value2": 20, "Value3": 20 };
var result = getter(row);
Basically the $parse service uses the string expression and the context object
and goes:
You want Value1 + Value2 + Value3, and you want to retrieve these values
from the row object.
Illustrated like this:
var result = row['Value1'] + row['Value2'] + row['Value2'];
With a 2D array you could structure your data like this:
$scope.rows = [
[911,20,30], // index 0 of 1st dim = 1st row; index 0,1,2 of 2nd dim = cells
[200,20,30] // index 1 of 1st dim = 2nd row
];
With this you can use two loops to get the cell and do your calculation, first loop for row and second for cell value.
Please have a look at the demo below or this plunkr.
In the demo I've created a function that does the calculation if you'd also pass an array like [0,1] it will tell the function to sum only col0 and col1 values.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $filter) {
function sumRows(data, values2sum) {
// e.g. data = [ [11, 12, 13], [21, 22, 23] ]
// new col = [ 32, 34, 46] // sum all
// new col = [ 32, 34, 0 ] // sum value1 & 2
// --> values2sum = [ 0, 1 ]
if ( angular.isUndefined(values2sum) ){
var all = true;
var value2sum = [];
}
angular.forEach(data, function(row, rowIndex) {
rowSum = 0;
angular.forEach(row, function(cell, colIndex) {
if ( all || values2sum.indexOf(colIndex) != -1 ) {
rowSum += cell;
}
});
row.push(rowSum);
})
}
$scope.addColumn = function() {
var rowSum, newRow = [], colId = $scope.columnsTest.length + 1;
$scope.columnsTest.push({
id: 'Value'+ colId,
checked: true,
value: 'Value'+colId
}); // rename columnsTest to tableHeading
//sumRows($scope.rows, [0,2]); // add value1 + value3
sumRows($scope.rows); // complete sum
//var newCol = { id: 'Value4', checked: true, value: 'Value1 + Value2 + Value3' }
//$scope.columnsTest.push(newCol);
}
$scope.columnsTest = [{
id: 'Value1',
checked: true,
value: 'Value1'
}, {
id: 'Value2',
checked: true,
value: 'Value2'
}, {
id: 'Value3',
checked: true,
value: 'Value3'
}];
/*$scope.rows = [{
id: 1,
"Value1": 911,
"Value2": 20,
"Value3": 20
}, {
id: 2,
"Value1": 200,
"Value2": 20,
"Value3": 20
}];*/
$scope.rows = [
[911,20,30],
[200,20,30]
]
});
<script data-require="angular.js#*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<div ng-app="plunker" ng:controller="MainCtrl">
<table border="1">
<thead style="font-weight: bold;">
<tr>
<th class="text-right" ng-repeat="column in columnsTest" ng-if="column.checked" ng-bind="column.id"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-repeat="cell in row track by $index">
<!--ng-if="column.checked">-->
{{ cell }}
</td>
</tr>
</tbody>
</table>
<br>
<br>
<input type="button" value="Add new column" ng-click="addColumn()" />
<br>
<br>
<br>
<p ng-repeat="c in columnsTest">Column {{$index}}: {{c}}</p>
</div>

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>

Resources