I want to use the following data
[
{
"id": "1",
"label": "Score",
"tabledata": [
{"label": "Day 1", "data": {"score": 73}},
{"label": "Day 2", "data": {"score": 64}},
{"label": "Day 3", "data": {"score": 72}}
]
}, {
"id": "2",
"label": "Success",
"tabledata": [
{"label": "Day 1", "data": {"score": 73 }},
{"label": "Day 2", "data": {"score": 64 }},
{"label": "Day 3", "data": {"score": 72}}
]
} ]
<table>
<tr>
<td>
</td>
<td>
Score
</td>
<td>
Success
</td>
</tr>
<tr>
<td>
Day 3
</td>
<td>
72
</td>
<td>
72
</td>
</tr>
<tr>
<td>
Day 2
</td>
<td>
64
</td>
<td>
64
</td>
</tr>
<tr>
<td>
Day 1
</td>
<td>
73
</td>
<td>
73
</td>
</tr>
</table>
The solution i came up is:
<table >
<tr>
<td> </td>
<td>
{this.props.GraphData[0].label}
</td>
<td>
{this.props.GraphData[1].label}
</td>
</tr>
<tr>
<td> </td>
<td>
{this.props.GraphData[0].tabledata.data.map(row => ([ <tr key={i}> <td> {row.score}</td> </tr> )])};
</td>
<td>
{this.props.GraphData[1].tabledata.data.map(row => ([ <tr key={i}> <td> {row.score}</td> </tr> )])};
</td>
</tr>
</table>
How can I turn with reverse.map the values to appear in descending order (the first row (day 3) and last row (day 1).
Finally is there a way to avoid using [0] or [1] from the data file but to use mapping for all the rows and columns of the table?
first of all, your data structure is adding complexity as hell, and i advice you to structure it well so you could easily consume it, I've made a refactoring for your code and also restructured your data with a generic way so even day4, ...dayn could be shown, that shows well how complex the consumption became if we think generically, so this is what I've got, so rename this file as Table.jsx and try to import it as so import Table from './Table';:
import React from 'react';
import { get, map, reduce, uniq, find, orderBy } from 'lodash';
const data = [
{
id: '1',
label: 'Score',
tabledata: [
{ label: 'Day 1', data: { score: 73 } },
{ label: 'Day 2', data: { score: 64 } },
{ label: 'Day 3', data: { score: 72 } },
{ label: 'Day 5', data: { score: 60 } }
]
},
{
id: '2',
label: 'Success',
tabledata: [
{ label: 'Day 1', data: { score: 73 } },
{ label: 'Day 2', data: { score: 64 } },
{ label: 'Day 3', data: { score: 73 } }
]
}
];
const columnsHeaders = ['day', ...map(data, 'label')];
const days = uniq(
reduce(
data,
(acc, item) => [...acc, ...map(get(item, 'tabledata'), 'label')],
[]
)
);
const restructuredData = reduce(
days,
(acc, day) => {
const dayData = reduce(
map(data, 'label'),
(dayDataAcc, colName) => {
return {
...dayDataAcc,
day,
[colName]: get(
find(get(find(data, { label: colName }), 'tabledata'), {
label: day
}),
['data', 'score']
)
};
},
{}
);
return [...acc, dayData];
},
[]
);
const sortedData = orderBy(restructuredData, 'day', 'desc');
const TableHeaders = () => (
<thead>
<tr>{map(columnsHeaders, (col, index) => <th key={index}>{col}</th>)}</tr>
</thead>
);
// checking our data
console.log('restructuredData', restructuredData);
console.log('sortedData', sortedData);
const TableRows = ({ data }) => (
<tbody>
{map(data, ({ day, Score: score, Success: success }) => (
<tr key={day}>
<td>{day || '-'}</td>
<td>{score || '-'}</td>
<td>{success || '-'}</td>
</tr>
))}
</tbody>
);
export default () => (
<table>
<TableHeaders />
<TableRows data={sortedData} />
</table>
);
Related
Hi im newbie on react js,
how to call nested object(array/list) on main object, i mean object in object
<tbody>
{
vehicles.map((v, index) =>(
<tr key={v.id}>
<td>{index +1}</td>
<td>{v.type}</td>
<td>{v.plateNumber}</td>
{v.employee.map(item => {
return (
<td>
<ul>{item.name}</ul>
</td>
);
})}
<td>
<Link className="btn btn-info" to={`/vehicles/edit/${v.id}`}>Update</Link>
<button className="btn btn-danger ml-2" onClick={() => {deleteSweetAlert(v.id)}}>Delete</button>
</td>
</tr>
))
}
</tbody>
This one my JSON example
[
{
"id": 1,
"type": "MasterCard",
"plateNumber": "3747948",
"status": "1",
"employee": {
"id": 1,
"name": "Joanne Hagenes Sr.",
"location": "60794 Rippin Cove Suite 080\nTroyberg, ND 95778",
"department": "Wuckert-Luettgen",
"status": "1"
}
}
]
I always get this error message at the console
react-dom.development.js:26874 Uncaught TypeError: v.employee.map is not a function
map() function can only applied with an array, so in your case employee should be an array instead an object:
{
"id": 1,
"type": "MasterCard",
"plateNumber": "3747948",
"status": "1",
"employee": [{
"id": 1,
"name": "Joanne Hagenes Sr.",
"location": "60794 Rippin Cove Suite 080\nTroyberg, ND 95778",
"department": "Wuckert-Luettgen",
"status": "1"
}]
}
Another way to fix this, since each of your vehicle contains only 1 employee (based on how u named it). So i suppose you dont need to use map() function in this case:
<tbody>
{
vehicles.map((v, index) =>(
<tr key={v.id}>
<td>{index +1}</td>
<td>{v.type}</td>
<td>{v.plateNumber}</td>
<td>
<ul>{item.name}</ul>
</td>
<td>
<Link className="btn btn-info" to={`/vehicles/edit/${v.id}`}>Update</Link>
<button className="btn btn-danger ml-2" onClick={() => {deleteSweetAlert(v.id)}}>Delete</button>
</td>
</tr>
))
}
</tbody>
You can use interface to define.
interface IFoo {
foo: string;
}
interface IBaz {
baz: string;
foo: IFoo;
}
const obj: IBaz = {
baz: 'bar',
foo: {
foo: 'foo',
},
};
console.log(obj.baz); // -> baz
console.log(obj.foo.foo); // -> foo
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>
I know that maybe this question can be confusing for you, but I will try to explain in more detail.
First: I have this api
{
"data": [
{
"id": 1,
"title": "Действие 1",
"subactions": [
{
"id": 2,
"title": "Субдействие 1",
"status": {
"id": 1,
"text": "Выполнено"
},
"countries": [
"kz",
"kg"
],
"theme": {
"id": 1,
"text": "Изменение климата"
},
"characteristics": {
"0": "Другие действия",
"id": 1
},
"monthes": [
{
"month": 1,
"year": 2017,
"complete": 0
},
{
"month": 2,
"year": 2017,
"complete": 1
},
{
"month": 3,
"year": 2017,
"complete": 1
},
{
"month": 4,
"year": 2017,
"complete": 1
},
{
"month": 5,
"year": 2017,
"complete": 1
},
{
"month": 1,
"year": 2018,
"complete": 0
},
{
"month": 2,
"year": 2018,
"complete": 0
}
]
}
]
}
The second: the table should look like in this picture
The third: The problem is that I don not know how to loop <tr> as child of another <tr> parent.
The Fourth: This my attempts of doing that
{
task.map((value, i) => {
if(value.subactions !== undefined){
return (
value.subactions.map((subaction, i) => {
return (
<div>
{/*Parent tr*/}
<tr key={i}>
<td>{value.id}</td>
<td>{value.title}</td>
</tr>
{/* end Parent tr*/}
{/*sub table of parent tr*/}
<tr key={i}>
<td>{subaction.id}</td>
<td>{subaction.title}</td>
<td>{subaction.status.text}</td>
<td>
{subaction.countries.map((country, i) => {
return (
i == subaction.countries.length - 1 ?
<span key={i}>{country} </span> :
<span key={i}> {country},</span>
)
})
}
</td>
<td></td>
<td>{subaction.theme.text}</td>
<td>{subaction.characteristics[0]}</td>
{subaction.monthes.map((month, i) => {
return (
month.complete ?
<td className='task-month task-end'>
<input
type="checkbox"
className="css-checkbox lrg"/>
<label htmlFor="checkbox69" name="checkbox69_lbl"
className="css-label lrg vlad"/>
</td> : <td></td>
)
})
}
</tr>
{/*end sub table of parent tr*/}
</div>
)
})
)
} else {
return undefined
}
})
}
Please, anyone help to point out of my mistakes.
Replace div tag to table tag and In you parent table at second td use colspan="(total number of td in your second tr) - 2"
check below changes , I have mentioned the colspan count :
task.map((value, i) => {
if(value.subactions !== undefined){
return (
value.subactions.map((subaction, i) => {
return (
<table>
{/*Parent tr*/}
<tr key={i}>
<td>{value.id}</td>
<td colspan="1+totalContriesCount+2+totalMonthsCount">{value.title}</td>
</tr>
{/* end Parent tr*/}
{/*sub table of parent tr*/}
<tr key={i}>
<td>{subaction.id}</td>
<td>{subaction.title}</td>
<td>{subaction.status.text}</td>
<td>
{subaction.countries.map((country, i) => {
return (
i == subaction.countries.length - 1 ?
<span key={i}>{country} </span> :
<span key={i}> {country},</span>
)
})
}
</td>
<td></td>
<td>{subaction.theme.text}</td>
<td>{subaction.characteristics[0]}</td>
{subaction.monthes.map((month, i) => {
return (
month.complete ?
<td className='task-month task-end'>
<input
type="checkbox"
className="css-checkbox lrg"/>
<label htmlFor="checkbox69" name="checkbox69_lbl"
className="css-label lrg vlad"/>
</td> : <td></td>
)
})
}
</tr>
{/*end sub table of parent tr*/}
</table>
)
})
)
} else {
return undefined
}
})
}
Try making the parent row span to the maximum so you might achieve the output
<tr>
<td rowspan="maxRowCount"></td>
<td colspan="1"></td>
</tr>
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.
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