Display JSON in table with React - javascript

I am new to React and I am trying to display table of data from objects with plan to also have a collapsible detail of the properties for each store but for now I am struggling to display anything other than the first value. Any help appreciated. I looked at many other examples but being a newbie I can't get my head around them so hoping understanding my exact scenario first will allow me figure out more on my own.
Tried return multiple calls with for loop but returns just one element and exits.
import React, { Component } from "react";
class DataStores extends Component {
state = [
{
name:"PODs",
storeType:"fixed",
properties: [
{
name:"PODNo",
type: "text",
regex: ""
}
,
{
name:"CustomerCode",
type: "text",
regex: ""
},
{
name:"CustomerName",
type: "text",
regex: ""
},
{
name:"RefDate",
type: "datetime",
regex: ""
}]
}
,
{
name:"SalesOrders",
storeType:"variable",
properties: [
{
name:"OrderNo",
type: "text",
regex: ""
}
,
{
name:"CustomerRef",
type: "text",
regex: ""
},
{
name:"OrderDate",
type: "datetime",
regex: ""
}]
}
];
;
render() {
console.log('state', this.state);
for (let store of this.state){
console.log(store.name);
let chars = store["properties"]
console.log(chars);
for(let i=0, len=chars.length; i < len; i++){
console.log(chars[i].name)
// console.log(chars[i].name);
// console.log(chars[i]["type"]);
}
}
return (
<table>
<tbody>{this.state.map((item, key) =>{
return (
<tr key = {key}>
<td>{item.name}</td>
<td>{item.storeType}</td>
</tr>
)
})}</tbody>
</table>
)
// let dataStores = this.state.dataStores.map(store => {
// console.log("this the ",Object.keys(store),store.name);
// return (<div><li>{store.name}</li>
// </div>
// );
// });
}
}
export default DataStores;

Here you go, the following approach is much better.
Check it out on sandbox as well: https://codesandbox.io/s/silly-meadow-f6hpz?fontsize=14
import React, { Component } from "react";
class DataStores extends Component {
state = {
values: [
{
name: "PODs",
storeType: "fixed",
properties: [
{
name: "PODNo",
type: "text",
regex: ""
},
{
name: "CustomerCode",
type: "text",
regex: ""
},
{
name: "CustomerName",
type: "text",
regex: ""
},
{
name: "RefDate",
type: "datetime",
regex: ""
}
]
},
{
name: "SalesOrders",
storeType: "variable",
properties: [
{
name: "OrderNo",
type: "text",
regex: ""
},
{
name: "CustomerRef",
type: "text",
regex: ""
},
{
name: "OrderDate",
type: "datetime",
regex: ""
}
]
}
]
};
render() {
// console.log('state', this.state);
// for (let store of this.state){
// console.log(store.name);
// let chars = store["properties"]
// console.log(chars);
// for(let i=0, len=chars.length; i < len; i++){
// console.log(chars[i].name)
// // console.log(chars[i].name);
// // console.log(chars[i]["type"]);
// }
// }
return (
<table>
<tbody>
{this.state.values.map((item, key) => {
return (
<tr key={key}>
<td>{item.name}</td>
<td>{item.storeType}</td>
</tr>
);
})}
</tbody>
</table>
);
// let dataStores = this.state.dataStores.map(store => {
// console.log("this the ",Object.keys(store),store.name);
// return (<div><li>{store.name}</li>
// </div>
// );
// });
}
}
export default DataStores;

Related

How to loop over nested JSON array and filter out search Query in Angular?

I have a complex nested JSON Array and I want to filter it(name property) through based on what user enters in input tag and show it as an autocomplete. A basic of it I have created here on stackblitz click here for the code. I have two entries of name "Tom" in two different objects so when user types Tom it should appear twice in the autocomplete as of now it shows only once. So if I press letter "T" it should show me all the names starting with "T". Here in this case "Tom" twice if I press "To" and if I press just "T" then Tiffany and Tom 2 times. Could you please help me here in the code ?
Any help is appreciated. Thanks much!
You can check the below code also, I have updated code you check in stackbliz https://stackblitz.com/edit/angular-ivy-k14se7
matches = [];
ngOnInit() {}
searchKeyup(ev) {
var term: any = ev.target as HTMLElement;
console.log("Value", term.value);
this.matches = [];
let content = [
{
name: 'Robert',
children: [],
},
{
name: 'Doug',
children: [
{
name: 'James',
children: [
{
name: 'John',
children: [
{
name: 'Tom',
children: [],
},
],
},
],
},
],
},
{
name: 'Susan',
children: [
{
name: 'Tiffany',
children: [
{
name: 'Merry',
children: [
{
name: 'Sasha',
children: [],
},
{
name: 'Tommy',
children: [],
},
],
},
],
},
],
},
];
if(term.value.length > 0){
this.filter(content, term.value);
} else {
document.getElementById('list').innerHTML = '';
}
if (this.matches.length > 0) {
document.getElementById('list').innerHTML = this.matches.map(match => match.name).join(",");
} else{
document.getElementById('list').innerHTML = "";
}
}
filter(arr, term) {
arr.forEach((i) => {
if (i.name.includes(term)) {
this.matches.push(i);
}
if (i.children.length > 0) {
this.filter(i.children, term);
}
});
console.log(this.matches);
}
You were on a good path. The only missing thing in this recursive walk is keeping state of matches like this:
filter(arr, term, matches) {
if (term.length == 0) {
matches = [];
document.getElementById('list').innerHTML = '';
}
arr.forEach((i) => {
if (i.name.includes(term)) {
matches.push(i);
}
if (i.children.length > 0) {
this.filter(i.children, term, matches);
}
});
console.log(matches);
if (matches.length > 0) {
document.getElementById('list').innerHTML = matches[0].name;
}
}

how can I fetch arrays of the object in an object in vue or javascript

I have an object in my Vue instance with the name of items
<script>
export default {
data() {
return {
selected: "",
items: {
item1: [{ selected: "", inputType: "", inputTarget: "" }],
item2: [{ selected: "", inputType: "", inputTarget: "" }]
},
textarea: ""
};
},
methods: {
selectboxAction(index) {
this.items.item1.forEach(val => {
if (val.selected.toLowerCase() === "file") {
this.$refs.inputData[index].type = "file";
} else {
this.$refs.inputData[index].type = "text";
}
});
}
}
};
</script>
how can I fetch an array of it, I want to put some condition over every item, but it could have item more than 2 items, maybe in the future it reaches 100
as you can see in selectboxAction method, I was able to fetch only one item which is item1 in this case
how can I fetch all of the arrays from items, not just one item1
I suggest you format your data using a computed getter and use Object.keys as others have suggested.
get itemsArray() {
return Object.keys(this.items).map((key) =>
return this.items[key];
});
}
Instead of naming your 'items' properties 'item1', 'item2', etc, it would be better make 'items' an array and add an 'id' property to each 'items' object:
data() {
return {
selected: "",
items: [
{ id: 1, selected: "", inputType: "", inputTarget: "" },
{ id: 2, selected: "", inputType: "", inputTarget: "" }
],
textarea: ""
};
},
you can do something like
methods: {
selectboxAction(index) {
Object.keys(this.items).forEach(val => {
this.items[val].forEach(item => {
if (item.selected.toLowerCase() === "file") {
this.$refs.inputData[index].type = "file";
} else {
this.$refs.inputData[index].type = "text";
}
});
});
}
}

How do I asynchronously get JSON schema through an API call in react-crud-admin library?

I'm using react-crud-admin library to the generate UI. There is a get_form method that returns JSON schema which is used to create a react form. The method given in the library returns hardcoded JSON schema only. Is there any way to use asynchronous API call to get the schema from a file instead of using a hardcoded schema?
Here's the sample code:
import React from "react";
import Admin from "react-crud-admin";
import Form from "react-jsonschema-form";
import "react-crud-admin/css"; //optional css import
export default class Example extends Admin {
constructor() {
super();
this.name = "Contact";
this.name_plural = "Contacts";
this.list_display_links = ["name"];
this.list_display = ["name", "number", "address.street"];
}
get_queryset(page_number, list_per_page, queryset) {
return [
{
id: 1,
name: "Ken Next",
number: "08939303003",
address: { street: "Hallmark Street" }
},
{
id: 2,
name: "Isa Yoll",
number: "0908839202",
address: { street: "Barbican Street" }
}
];
}
get_form(object = null) {
let schema = {
title: this.name,
type: "object",
required: ["name"],
properties: {
id: {
type: "number",
title: "id",
default: Math.floor(1000 * Math.random()) + 1
},
name: { type: "string", title: "Name", default: "" },
number: { type: "string", title: "Number", default: "" },
address: {
type: "object",
title: "Address",
properties: {
street: { type: "string", title: "Street" }
}
}
}
};
if (!object) {
return <Form schema={schema} />;
} else {
return <Form schema={schema} formData={object} />;
}
}
}
Yes, sure. It is possible. You probably want to implement it in componentDidMount.
import React from "react";
import Admin from "react-crud-admin";
import Form from "react-jsonschema-form";
import "react-crud-admin/css"; //optional css import
export default class Example extends Admin {
constructor() {
super();
this.name = "Contact";
this.name_plural = "Contacts";
this.list_display_links = ["name"];
this.list_display = ["name", "number", "address.street"];
}
get_queryset(page_number, list_per_page, queryset) {
return [
{
id: 1,
name: "Ken Next",
number: "08939303003",
address: { street: "Hallmark Street" }
},
{
id: 2,
name: "Isa Yoll",
number: "0908839202",
address: { street: "Barbican Street" }
}
];
}
get_form(object = null)
if (!object) {
return <Form schema={this.state.schema} />;
} else {
return <Form schema={this.state.schema} formData={object} />;
}
}
componentDidMount()
{
fetch("/url/to/schema").then(response =>{
if(response.ok)
{
response.json().then(schema => this.setState({schema}))
}
});
}
}

Creating new data object set from multiple nested arrays

I've have a complex data structure with multiple nested arrays in place.
Below is the current structure
var contentData = {
data: {
content: [
{
type: "column",
sections: [
{
sub: [
{
type: "heading-1",
text: "Heading Text"
}
]
}
]
},
{
type: "acc-item",
sections: [
{
sub: [
{
type: "heading-1",
text: "Heading Text"
},
{
type: "ordered-item",
text: "Item 1"
},
{
type: "unordered-item",
text: "Item 2"
}
]
}
]
},
{
type: "acc-item",
sections: [
{
sub: [
{
type: "heading-1",
text: "Heading Text 2"
}
]
}
]
}
]
}
}
So What I wanted is,
I wanted to group all the ordered-item & unordered-item into a new object like {type: 'list', items:[all list items]}.
I need to extract all items which are inside sub and push it to new object embedded and it should placed in the root level like below,
{type:"acc-item",embedded:[{type:"heading-1",text:"Heading Text 2"}]};
So What I've done so far,
I can able to group acc-item, but not the ordered-item & unordered-item.
So my final expected result should like this,
[{
"type": "column",
"embedded": [
{
"type": "heading-1",
"text": "Heading Text"
}
]
},
{
"type": "acc-group",
"items": [
{
"type": "acc-item",
"embedded": [
{
"type": "heading-1",
"text": "Heading Text"
},
{
"type": "list",
"items": [
{
"type": "ordered-item",
"text": "Item 1"
},
{
"type": "unordered-item",
"text": "Item 2"
}
]
}
]
},
{
"type": "acc-item",
"embedded": [
{
"type": "heading-1",
"text": "Heading Text 2"
}
]
}
]
}]
Below is my code,
var group,contentData={data:{content:[{type:"column",sections:[{sub:[{type:"heading-1",text:"Heading Text"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text"},{type:"ordered-item",text:"Item 1"},{type:"unordered-item",text:"Item 2"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text 2"}]}]}]}},types=[["list",["ordered-item","unordered-item"]],["accordion",["acc-item"]]];
var result = contentData.data.content.reduce((r, o) => {
var type = (types.find(({ 1: values }) => values.indexOf(o.type) > -1)|| {})[0];
if (!type) {
r.push(o);
group = undefined;
return r;
}
if (!group || group.type !== type) {
group = { type, items: [] };
r.push(group);
}
group.items.push(o);
return r;
}, []);
document.body.innerHTML = '<pre>' + JSON.stringify(result, null, ' ') + '</pre>';
You could store the last items array as well as the last embedded array and use them until a column type is found.
var contentData = { data: { content: [{ type: "column", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }, { type: "ordered-item", text: "Item 1" }, { type: "unordered-item", text: "Item 2" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text 2" }] }] }] } },
list = ["ordered-item", "unordered-item"],
lastItems, lastEmbedded,
result = contentData.data.content.reduce((r, { type, sections }) => {
if (type === 'column') {
r.push({ type, embedded: sections.reduce((q, { sub }) => q.concat(sub), []) });
lastItems = undefined;
lastEmbedded = undefined;
return r;
}
if (!lastItems) r.push({ type: "acc-group", items: lastItems = [] });
lastItems.push(...sections.map(({ sub }) => ({
type,
embedded: sub.reduce((q, o) => {
if (list.includes(o.type)) {
if (!lastEmbedded) q.push({ type: 'list', items: lastEmbedded = [] });
lastEmbedded.push(o);
} else {
q.push(o);
lastEmbedded = undefined;
}
return q;
}, [])
})));
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The Array.prototype and Object.prototype methods are perfect for this kind of thing.
And you're right that this is some complicated kind of logic.
I would suggest that you definitely need some unit tests for this, and try break in to separate pieces.
Here's how I'm thinking I'd do it.
1. Group By the type to create your groups..
I'm actually creating a more generic solution that you've asked for here. That is, I'm not just grouping the 'acc-item', but everything.
I did a quick search for 'array group by javascript' and it gives us this answer which suggests using Array.reduce, so let's do that.
const groupedData = contentData.data.content.reduce((acc, cur) => {
//Check if this indexed array already exists, if not create it.
const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];
return {
...acc,
[`${cur.type}-group`]: {
type: `${cur.type}-group`,
items: [...currentArray, cur]
}
}
}, {});
2. Now for each of those items, we need to look at their subs, and group just the list items.
To do this, we basically want to find all the `item -> sections -> sub -> types and filter them into two arrays. A quick google on how to create two arrays using a filter gives me this answer.
First though, we need to flatten that sections-> subs thing, so lets just do that.
function flattenSectionsAndSubs(item) {
return {
type: item.type,
subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
};
}
And I'll just copy paste that partition function in:
function partition(array, isValid) {
return array.reduce(([pass, fail], elem) => {
return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]];
}, [[], []]);
}
const listTypes = ['ordered-item', 'unordered-item'];
function createEmbeddedFromItem(item) {
const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type);
return {
type: item.type,
embedded: [
...nonLists,
{
type: "list",
items: lists
}
]
}
}
Putting this all together and we get.
const contentData = {
data: {
content: [{
type: "column",
sections: [{
sub: [{
type: "heading-1",
text: "Heading Text"
}]
}]
},
{
type: "acc-item",
sections: [{
sub: [{
type: "heading-1",
text: "Heading Text"
},
{
type: "ordered-item",
text: "Item 1"
},
{
type: "unordered-item",
text: "Item 2"
}
]
}]
},
{
type: "acc-item",
sections: [{
sub: [{
type: "heading-1",
text: "Heading Text 2"
}]
}]
}
]
}
}
function partition(array, isValid) {
return array.reduce(([pass, fail], elem) => {
return isValid(elem) ? [
[...pass, elem], fail
] : [pass, [...fail, elem]];
}, [
[],
[]
]);
}
function flattenSectionsAndSubs(item) {
return {
type: item.type,
subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
};
}
const listTypes = ['ordered-item', 'unordered-item'];
function createEmbeddedFromItem(item) {
const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type));
return {
type: item.type,
embedded: [
...nonLists,
{
type: "list",
items: lists
}
]
}
}
const groupedData = contentData.data.content.reduce((acc, cur) => {
//Check if this indexed array already exists, if not create it.
const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];
const flattenedItem = flattenSectionsAndSubs(cur);
const embeddedItem = createEmbeddedFromItem(flattenedItem);
return {
...acc,
[`${cur.type}-group`]: {
type: `${cur.type}-group`,
items: [...currentArray, embeddedItem]
}
}
}, {});
console.log(groupedData);
Now this doesn't exactly match what you've asked for - but it should probably work.
You can add your own bits into only add a list item, if the array isn't empty, and to stop the column from being in its own group.
The thing is - tbh it seems like a little bit of a red flag that you would create an array of items that don't having matching structures, which is why I've done it this way.

JS: change params format from hash to array

I have this piece of JS, where I have create action with Ajax:
$(document).ready(function() {
editor = new $.fn.dataTable.Editor( {
table: "#user_groups_table",
template: '#user_groups_form',
display: "details",
idSrc: "id",
ajax: {
create: {
type: 'POST',
url: '/strongbolt/user_groups',
}
},
fields: [ {
name: "name"
}, {
name: "description"
}, {
type: "checkbox",
name: "users[].id",
optionsPair: {
label: 'name',
value: 'id'
}
}, {
type: "checkbox",
name: "roles[].id",
optionsPair: {
label: 'name',
value: 'id'
}
}
]
} );
editor.on( 'preSubmit', function ( e, data, action ) {
if ( action === 'create' ) {
data.strongbolt_user_group = {
"name": data.data[0].name,
"description": data.data[0].description,
"user_ids": data.data[0].users,
"role_ids": data.data[0].roles
};
delete data.data;
}
} );
The last section which starts with editor.on( 'preSubmit', function ( e, data, action ) { basically modifies data before they are passed to server.
At the moment I get my params in terminal like this:
{
"strongbolt_user_group"=>{
"name"=>"Some test group",
"description"=>"Some test description",
"user_ids"=>{"0"=>{"id"=>"3"}, "1"=>{"id"=>"2"}, "2"=>{"id"=>"5"}},
"role_ids"=>{"0"=>{"id"=>"1"}, "1"=>{"id"=>"2"}}
}
}
however I need it to be like this:
{
"strongbolt_user_group"=>{
"name"=>"Some test group",
"description"=>"Some test description",
"user_ids"=>["3", "2", "5"],
"role_ids"=>["1", "2"]
}
}
Basically I need user_ids and role_ids to be array.
How do I modify this in my JS, please? Thank you!
You have to map the array (or array-like object) of objects to an array of their ids:
"user_ids": Array.prototype.map.call(data.data[0].users, function(o) { return o.id; }),
If you are certain that data.data[0].users is an array, then just use map without call like:
"user_ids": data.data[0].users.map(function(o) { return o.id; }),
or even shorter in ES 6's arrow functions:
"user_ids": data.data[0].users.map(o => o.id),
Note: same applies for role_ids.

Categories

Resources