VueJS - how build dynamic values, using the mustaches? - arrays

In data, I have 2 arrays.
In the template, I want to use value of a special key of an array, to target the second array.
<template>
<table>
<tr v-for="sa in mySecondArray">
<td v-for="fa in myFirstArray">
{{ ??? }}
</td>
</tr>
</table>
</template>
// ...
data() {
myFirstArray: [
{
a: "KeyOne",
b: "we don't care",
c: "we don't care",
},
{
a: "KeyTwo",
b: "we don't care",
c: "we don't care",
},
],
mySecondArray: [
{
KeyOne: "foo",
KeyTwo: "bar"
},
{
KeyOne: "hello",
KeyTwo: "world"
},
],
In this exemple, I want to display
<table>
<tr>
<td>foo</td>
<td>hello</td>
</tr>
<tr>
<td>bar</td>
<td>world</td>
</tr>
</table>
I tried :
<tr v-for="sa in mySecondArray">
<td v-for="fa in myFirstArray">
{{ sa + fa.a }}
</td>
</tr>
in this case it displays [object Object].
I tried :
<tr v-for="sa in mySecondArray">
<td v-for="fa in myFirstArray">
{{ sa.concat('.',fa.a) }}
</td>
</tr>
in this case console says: "sa.concat is not a function".
I also tried with quotes, but it just concats the strings: "sa.KeyOne".
How to make this final string used as a target and not just a string ?

You can do something like this to get your result,
new Vue({
el: '#app',
data: {
firstArray: [{
a: "KeyOne",
b: "we don't care",
c: "we don't care",
},
{
a: "KeyTwo",
b: "we don't care",
c: "we don't care",
}],
secondArray: [
{
KeyOne: "foo",
KeyTwo: "bar"
},
{
KeyOne: "hello",
KeyTwo: "world"
},
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.0/dist/css/bootstrap.min.css" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<div id="app">
<table class="table">
<tr>
<th>head1</th>
<th>head2</th>
</tr>
<tr v-for="(sa1, index1) in secondArray" >
<td v-for="(sa2, index2) in secondArray">
{{secondArray[index2][firstArray[index1].a]}}
</td>
</tr>
</table>
</div>
Hope this solves your problem!

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>

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

Angular add filter based on Object value

I'm starting AngularJS. I'm dynamically building a table with angular and I'm trying to add a filter to one of my columns.
Here the code I have in my controller :
$scope.columns = [
{
"alias":"alias1",
"name":"name1",
"type":"string"
},
{
"alias":"alias2",
"name":"name2",
"type":"currency"
}
];
$scope.data = [
{
"alias1": "value1",
"alias2": "22489"
},
{
"alias1": "value2",
"alias2": "22489"
},
{
"alias1": "value3",
"alias2": "22489"
},
];
And my table looks like this :
<table class="table table-hover">
<thead>
<tr>
<th ng-repeat="c in columns" ng-click="sort(c.alias)">{{ c.name }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="result in results | filter:search | orderBy:sortKey:reverse">
<td ng-repeat="c in columns">{{ result[c.alias] }}</td>
</tr>
</tbody>
</table>
What I'd like to do is to apply a filter to the <td> value based on the type of the column. Something like {{ result[c.alias] | c.type }} doesn't work...
Is it something that can be achieved by some formatting directive for example ?
Thanks for you help.
You mean this?:
app.filter('myfilter', function() {
return function(input, param) {
//do whatever
}
})
{{ result[c.alias] | myfilter:c.type }}

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>

Create a table from scope object values

I have a table which I'm trying to create from a scope-object that I'm creating in my controller.
I would like the headers to take the value from 'rowHeader', which works fine. But the problem is that I wan't my cell values to be taken from 'cellValue' property.
In my fiddle I've added a "Desired table", thats how I would like the results to be in my first approach. If possible..
As you can see I would like to use a filter on one of the columns as well.
The reason that I would like to use this approach is so that I can use the checkboxlist to hide/show columns, and of course so that I can setup my table easy within the controller
Fiddle: http://jsfiddle.net/HB7LU/21469/
<!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">
<p>My table</p>
<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.rowHeader"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows" border="1">
<td ng-repeat="column in columns" ng-if="column.checked" ng-bind="row[column.cellValue]"></td>
</tr>
</tbody>
</table>
<p>Visible Columns:</p>
<br />
<div class="cbxList" ng-repeat="column in columnsTest">
<input type="checkbox" ng-model="column.checked">{{column.rowHeader}}
</div>
</div>
<script>
var app = angular.module('plunker', []);
app.filter('percentage', function () {
return function (changeFraction) {
return (changeFraction * 100).toFixed(2) + "%";
}
});
app.controller('MainCtrl', function($scope) {
$scope.columnsTest = [
{ checked: true, cellValue: 'ModelName', rowHeader: 'Name' },
{ checked: true, cellValue: 'value1', rowHeader: 'PL' },
{ checked: true, cellValue: 'value1 / value2 | percentage', rowHeader: '+/-' }
];
$scope.rows = [
{ value1: 100, value2: 5, ModelName: "This is a cell value" },
{ value1: 15, value2: 5, ModelName: "This is a cell value2" },
{ value1: 38, value2: 2, ModelName: "This is a cell value3" }
];
});
</script>
</body>
</html>
There is a typo in the code cause the problem:
<tr ng-repeat="row in rows" border="1">
<td ng-repeat="column in columns"
ng-if="column.checked" ng-bind="row[column.cellValue]"></td>
</tr>
You don't have a scope variable called columns, change it to columnsTest.
<tr ng-repeat="row in rows" border="1">
<td ng-repeat="column in columnsTest"
ng-if="column.checked" ng-bind="row[column.cellValue]"></td>
</tr>

Resources