Material UI - TreeView datastructure - javascript

I want to structure the data that I get from a server, so I can use the TreeView component from Material UI: https://material-ui.com/api/tree-view/
I'm fetching large amounts of data so I want to fetch child nodes from the server when the user clicks on the expand button. So
when the first node is expanded a HTTP request is sent to a server which returns all of the children of that node. When another node is expanded the children of that node is fetched etc.
On startup of the page I want to fetch the root node and its children. The JSON returned will look something like this:
{
"division": {
"id": "1234",
"name": "Teest",
"address": "Oslo"
},
"children": [
{
"id": "3321",
"parentId": "1234",
"name": "Marketing",
"address": "homestreet"
},
{
"id": "3323",
"parentId": "1234",
"name": "Development",
"address": "homestreet"
}
]
}
When expanding the Marketing node I want to make a HTTP call to fetch the children of this node. So I would get JSON like this:
{
"children": [
{
"id": "2212",
"parentId": "3321",
"name": "R&D",
"address": "homestreet"
},
{
"id": "4212",
"parentId": "3321",
"name": "Testing",
"address": "homestreet"
}
]
}
But I am confused on how to create such a data structure which can later be used my the TreeView component. How can I create such a structure?

For anyone still looking for a solution to this problem I've recently tackled it using a combination of the selected and expanded props in the TreeView API. See this Code Sandbox demo for an example of how to asynchronously load new children and expand their parent once they are loaded.
import React from "react";
import TreeView from "#material-ui/lab/TreeView";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
import TreeItem from "#material-ui/lab/TreeItem";
import TreeNode from "./TreeNode";
const mockApiCall = async () => {
return new Promise((resolve) => {
setTimeout(() => {
const nextId = Math.ceil(Math.random() * 100);
resolve([
{
id: `${nextId}`,
name: `child-${nextId}`,
children: []
},
{
id: `${nextId + 1}`,
name: `child-${nextId + 1}`,
children: []
}
]);
}, Math.ceil(Math.random() * 1000));
});
};
export default class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: [],
selected: "1",
tree: new TreeNode({
id: "1",
name: "src",
children: []
})
};
}
handleChange = async (event, nodeId) => {
const node = this.state.tree.search(nodeId);
if (node && !node.children.length) {
mockApiCall()
.then((result) => {
this.setState({ tree: this.state.tree.addChildren(result, nodeId) });
})
.catch((err) => console.error(err))
.finally(() => {
this.setState({
selected: nodeId,
expanded: [...this.state.expanded, nodeId]
});
});
}
};
createItemsFromTree = (tree) => {
if (tree.children.length) {
return (
<TreeItem key={tree.id} nodeId={tree.id} label={tree.name}>
{tree.children.length > 0 &&
tree.children.map((child) => this.createItemsFromTree(child))}
</TreeItem>
);
}
return <TreeItem key={tree.id} nodeId={tree.id} label={tree.name} />;
};
render() {
return (
<TreeView
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
selected={this.state.selected}
onNodeSelect={this.handleChange}
expanded={this.state.expanded}
>
{this.createItemsFromTree(this.state.tree)}
</TreeView>
);
}
}

Related

how do i fetch data by id on react native (rest api)

import React, { Component } from 'react';
import { ActivityIndicator, FlatList, Text, View } from 'react-
native';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true
};
}
async getMovies() {
try {
const response = await
fetch('https://reactnative.dev/movies.json');
const json = await response.json();
this.setState({ data: json.movies });
} catch (error) {
console.log(error);
} finally {
this.setState({ isLoading: false });
}
}
componentDidMount() {
this.getMovies();
}
render() {
const { data, isLoading } = this.state;
return (
<View style={{ flex: 1, padding: 24 }}>
{isLoading ? <ActivityIndicator/> : (
<FlatList
data={data}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => (
<Text>{item.title}, {item.releaseYear}</Text>
)}
/>
)}
</View>
);
}
};
this is the api
{
"title": "The Basics - Networking",
"description": "Your app fetched this from a remote endpoint!",
"movies": [
{ "id": "1", "title": "Star Wars", "releaseYear": "1977" },
{ "id": "2", "title": "Back to the Future", "releaseYear": "1985" },
{ "id": "3", "title": "The Matrix", "releaseYear": "1999" },
{ "id": "4", "title": "Inception", "releaseYear": "2010" },
{ "id": "5", "title": "Interstellar", "releaseYear": "2014" }
]
}
I want this code to fetch and show data by id. just like the query show movies by id.
This is the result.
I try to fetch data with id 1 but i dont know how to do it. i dont know how to show it to react native. and i dont know how fetch the data by id
I pass a parameter like this:
my call to the API:
const getDetails = (id) => {
return api.Get(`/api/details/${id}`);
}
then in the react code
const getInfo = async(e) => {
apiCall.GetDetails(value to be passed here)
}

draftjs entity Content State to HTML

I am using draftjs in which users can type in anything and can also click a button on which I am inserting a IMMUTABLE entity.
const text = "foo";
const editorState = this.state.value;
const selectionState = editorState.getSelection();
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity("TOKEN", "IMMUTABLE", { time: new Date().getTime() });
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const modifiedContent = Modifier.insertText(contentState, selectionState, text, OrderedSet([ "INSERT" ]), entityKey);
const nextState = EditorState.push( editorState, modifiedContent, editorState.getLastChangeType() );
this.setState({value: nextState}, this.focus );
https://codepen.io/dakridge/pen/XgLWJQ
All this is working fine but when the editor state is saved and now I am trying to render the HTML from the saved ContentState in my webpage then I am not able to identify the immutable entity and apply styles to it or render it differently.
For ex, in the above example how can foo be rendered with a different color and how the saved timestamp say can be logged in the console when I hover over foo?
I am using draftjs-to-html to render html from draftjs output.
If you want to track the timestamp (and other data of immutability) then your best option is to store the "raw" form instead of html (or keep alongside the html). You can use the convertToRaw to get the raw content, here's an example of foo inserted with timestamp as epoch.
{
"entityMap": {
"0": {
"type": "TOKEN",
"mutability": "SEGMENTED",
"data": {
"time": 1657116932641
}
}
},
"blocks": [
{
"key": "ekpc6",
"text": "aaa",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [],
"data": {}
},
{
"key": "dmkkr",
"text": "foo",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [
{
"offset": 0,
"length": 3,
"style": "INSERT"
}
],
"entityRanges": [
{
"offset": 0,
"length": 3,
"key": 0
}
],
"data": {}
}
]
};
I've taken your codepen code & added an explicit button to get raw content. Once you get the raw JSON, you can extract any meta data out.
const { Editor, EditorState, Modifier, convertToRaw, convertFromRaw } = Draft;
const { OrderedSet } = Immutable;
const sample_saved_state = {
"entityMap": {
"0": {
"type": "TOKEN",
"mutability": "SEGMENTED",
"data": {
"time": 1657116932641
}
}
},
"blocks": [
{
"key": "ekpc6",
"text": "aaa",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [],
"data": {}
},
{
"key": "dmkkr",
"text": "foo",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [
{
"offset": 0,
"length": 3,
"style": "INSERT"
}
],
"entityRanges": [
{
"offset": 0,
"length": 3,
"key": 0
}
],
"data": {}
}
]
};
class EditorComponent extends React.Component {
constructor(props) {
super(props);
// In case you store the sample_saved_state as string in server side, you might have to do JSON.parse
console.log("Converting from saved state");
const saved_state = sample_saved_state != null ? EditorState.createWithContent(convertFromRaw(sample_saved_state))
: EditorState.createEmpty();
console.log("Assining to state ");
this.state = {
value : saved_state
};
this.saveContent = this.saveContent.bind(this);
//this.state = { value: EditorState.createEmpty() };
this.onChange = (value) => this.setState({value});
this.focus = () => this.refs.editor.focus();
this.insert = this.insert.bind( this );
}
insert () {
const text = "foo";
const editorState = this.state.value;
const selectionState = editorState.getSelection();
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity("TOKEN", "SEGMENTED", { time: new Date().getTime() });
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const modifiedContent = Modifier.insertText(contentState, selectionState, text, OrderedSet([ "INSERT" ]), entityKey);
const nextState = EditorState.push( editorState, modifiedContent, editorState.getLastChangeType() );
this.setState({value: nextState}, this.focus );
}
saveContent () {
console.log("inside saveContent");
const content = convertToRaw(this.state.value.getCurrentContent());
// you can save the content as json in server side
console.log("Raw Content: ",content)
}
render () {
return (
<div>
<div onClick={ this.focus } className="editor">
<Editor
ref="editor"
onChange={ this.onChange }
editorState={ this.state.value }
customStyleMap={{ "INSERT": { backgroundColor: "yellow", padding: "0 2px" } }}
/>
</div>
<button onClick={ this.insert }>Insert</button>
<button onClick={this.saveContent}>Save</button>
</div>
);
}
}
ReactDOM.render(
<EditorComponent />,
document.getElementById('app')
);
Solution codesandbox: https://codesandbox.io/s/staging-fire-wo3oc5?file=/src/Editor.js
Instead of draftjs-to-html, I recommend draft-convert that comes with the functionality you want. You can customize the output HTML based on style and entity.
const html = convertToHTML({
styleToHTML: (style) => {
if (style === 'INSERT') {
return <span style={{ backgroundColor: 'yellow', padding: '0 2px' }} />;
}
},
entityToHTML: (entity) => {
if (entity.type === 'TOKEN') {
return <span data-time={entity.data.time} className="entity-token" />;
}
},
})(this.state.value.getCurrentContent());
For instance, I am wrapping nodes with the INSERT style inside a styling span, and nodes that correspond with the entity type TOKEN inside a functional span. This will result in an HTML node that looks like this:
Of course, you can combine these logic to wrap your token nodes inside 1 span only, if you want.
The reason we need functional spans is because handling interactions (like displaying time in console on hover) needs custom JavaScript, and we need to do this the old fashioned way. Where you render the HTML, just add the following effect hook:
useEffect(() => {
const tokens = [
...containerRef.current.getElementsByClassName("entity-token")
];
const handler = (event) => {
const timestamp = event.target.dataset.time;
console.log(timestamp);
};
tokens.forEach((token) => {
token.addEventListener("mouseenter", handler);
});
return () => {
tokens.forEach((token) => {
token.removeEventListener("mouseenter", handler);
});
};
}, [content]);
This should handle the interactions for you. Sorry for mixing class components and functional components with hooks, but you can achieve the same thing with class components (using componentDidMount and componentDidUpdate) as well. Using hook is just more elegant.

Fetch data from server (REST API) and set data in a Multiple Select Picker in React Native

I am using REST API to fetch the data from server and I want to set that data in a multiple select picker in React Native but I am getting an error. Here is my response from the server:
[
{
"id": 22,
"name": "Business",
"slug": "business"
},
{
"id": 17,
"name": "Digital Marketing",
"slug": "digital-marketing"
},
{
"id": 23,
"name": "Fun & Lifestyle",
"slug": "fun-lifestyle"
},
{
"id": 16,
"name": "Mobiles",
"slug": "mobiles"
}]
I want to set the name in the drop down and select multiple value from it. Here is my code:
import React from 'react';
import { View, StyleSheet } from 'react-native';
import MultiSelect from 'react-native-multiple-select';
export default class PostJob extends React.Component {
state = {
LangPickerValueHolder: [],
LangKnown: []
}
componentDidMount () {
fetch('taxonomies/get_taxonomy?taxonomy=project_cat').then(response => response.json())
.then(responseJson => {
let langString = responseJson;
let LangPickerValueHolder = langString.map((name, id) => { return { name, id }; });
this.setState({
LangPickerValueHolder
});
console.log(langArray);
}).catch(error => {
console.error(error);
});
}
render () {
return (
<View style={styles.container}>
<MultiSelect
ref={(component) => { this.multiSelect = component; }}
onSelectedItemsChange={(value) =>
this.setState({ LangKnown: value })
}
uniqueKey="id"
items={this.state.langString}
selectedItems={this.state.LangKnown}
onChangeInput={ (text) => console.log(text)}
displayKey = 'name'
submitButtonText="Submit" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'white',
padding: 8
}
});
But it is showing no item to display.
Your mapping is wrong. It should be langString.map(item => ({ name: item.name, id: item.id })).
items prop should be this.state.LangPickerValueHolder.

React JS tree view to display api response

In my react application, I am implementing a tree view structure to display the api response in more readable format. I am using tree view-react-bootstrap for that.
import React from 'react';
import ReactDOM from 'react-dom';
import TreeView from 'treeview-react-bootstrap'
class Example extends React.Component {
constructor(){
super();
// SET YOUR DATA
this.state = {
data: [
{
text: "John Peter",
nodes: [
{
text: "ID: 11111",
nodes: [
{
text: "VIN"
},
{
text: "Policy Effective Date"
},
{
text: "Policy Expiration Date"
},
{
text: "Vehicle Make"
},
{
text: "Vehicle Model"
}
]
},
{
text: "ID: 123456",
nodes: [
{
text: "VIN"
},
{
text: "Policy Effective Date"
},
{
text: "Policy Expiration Date"
},
{
text: "Vehicle Make"
},
{
text: "Vehicle Model"
}
]
}
]
},
{
text: "Scott Brown"
}
]
}
}
render(){
return (
// RENDER THE COMPONENT
<TreeView data={this.state.data} />
);
}
}
export default Example
I am using dummy data for now but this is the format that I want my data to be displayed. The api response I have is "array of objects" and it is only in one level JSON format.
Sample response -
[
{
"id": "1234",
"name": "John Scott",
"vin": "45",
"make": "Toyota",
"model": "Etios"
},
{
"id": "4567",
"name": "James Scott",
"vin": "67",
"make": "Hyundai",
"model": "Etios"
}
]
If you see the response, I would like my key value to be printed in a tree structure.
Is there a way I can render this response to accommodate with treeview-react-bootstrap?
I am not sure if I need to use map function inside my render method to iterate and display the data and how will it work along.Can someone let me know if I am doing it right or is there any better way of doing it. thanks in advance.
You can transform the response something like this. Have just added a dummy response. Please check the following code and let me know if this helps:
import React from "react";
import ReactDOM from "react-dom";
import TreeView from "treeview-react-bootstrap";
import axios from "axios";
class Example extends React.Component {
constructor() {
super();
// SET YOUR DATA
this.state = {
data: []
};
}
componentDidMount() {
axios
.get("https://www.mocky.io/v2/5bb85d723000005f00f93bb6")
.then(data => {
let transformedData = data.data.map(d => {
return {
text: d.text,
nodes: [
{
text: "dummy 1",
nodes: []
}
]
};
});
this.setState({ data: transformedData });
});
}
render() {
return (
// RENDER THE COMPONENT
<TreeView data={this.state.data} />
);
}
}
ReactDOM.render(<Example />, document.getElementById("app"));
You can also see it in action here: https://codesandbox.io/s/73ryny9ywq?autoresize=1&hidenavigation=1

Accessing data in an array List using axios.get in React Native

How to Make a Accessing data in an array, and list separated by categories Using axios React Native
I am trying to deploy a list with categories and products using axio in react native
my json structure
[
{
"id": "1",
"name": "TV",
"list": [
{
"idp": "1",
"namep": "TV 43"
},
{
"idp": "2",
"namep": "TV 32"
}
]
},
{
"id": "2",
"name": "Couch",
"list": [
{
"idp": "3",
"namep": "Couch for 3 people"
},
{
"idp": "4",
"namep": "Couch for 2 people"
}
]
}
]
what I've already done, so I can display categories
constructor(props) {
super(props);
this.state = { category: [], list: [] };
}
componentWillMount() {
axios.get('http://localhost/api/list.json')
.then(response => {
this.setState({
category: response.data,
list: response.data.list });
})
.catch(() => { console.log('Err'); });
}
.............
{this.state.category.map((item, i) => {
<Text>{item.name}</Text>
{ item.list.map((product, i) => {
<Text>{product.namep}</Text>
})}
})}
Example list
Sorry I can't give you a more detailed and focused answer since I'm not familiar with react native but logic goes like this:
A loop to iterate over main array and extract categories names then another loop inside that one for iterating over products.
const arr = [
{
"id": "1",
"name": "TV",
"list": [
{
"idp": "1",
"namep": "TV 43"
},
{
"idp": "2",
"namep": "TV 32"
}
]
},
{
"id": "2",
"name": "Couch",
"list": [
{
"idp": "3",
"namep": "Couch for 3 people"
},
{
"idp": "4",
"namep": "Couch for 2 people"
}
]
}
];
const parentEl = document.querySelector('#list');
arr.forEach((cat) => {
const catEl = document.createElement('div')
catEl.textContent = cat.name
parentEl.append(catEl)
cat.list.forEach((prod) => {
const listEl = document.createElement('ul')
const listItem = document.createElement('li')
listItem.textContent = prod.namep
listEl.append(listItem)
parentEl.append(listEl)
})
})
<div id="list">
</div>
So, not knowing react native AND assuming this code you pasted is correct, I'm going to risk saying it would go something like this:
{
this.state.category.forEach((item, i) => {
<Text>{item.name}</Text>
item.list.forEach((product, i) => {
<Text>{product.namep}</Text>
})
})
}

Categories

Resources