Display different data from json file, depending on link clicked - json

React.js
On the main page example.js there are 4 links: link1, link2, link3, link4
When the user clicks on one of the links they are sent to a site called template.js.
Each link sends the user to the same site, template.js, however the data is different depending on what link was clicked.
I have tried just to display the entire data from one of my .json files, without any functionality and style -- but I didn't get any response whatsoever...
I have tried:
var data = require(url);
for(var i = 0; i < data.length; i++) {
var obj = data[i];
console.log("Name: " + obj.first_name + ", " + obj.last_name);
}
OR
fetch(url)
.then(response => response.json().then(data => ({status:
response.status, body: data})))
.then(object => console.log(object));
OR
fetch(url)
.then(response = response.json())
Question:
How would I tell the template.js file to display the relevant information.

You can pass your query through the link and then read it directly from the url.
I do it this way:
Your links
// Here we want to send our search terms, this is just an example with 'someId'
<a src="/template?first_name=john"></a>
<a src="/template?first_name=jenny"></a>
<a src="/template?first_name=gabriel"></a>
<a src="/template?first_name=jose"></a>
You can read the search values with window.location.search or window.location.hash depending on your router.
I prefer use the parse function from the query-string module
Your template
import React, { Component } from 'react';
import * as qs from 'query-string';
class Dashboard extends Component {
render() {
const {
location,
} = this.props;
const { search } = location;
const query = qs.parse(search, { ignoreQueryPrefix: true });
const info = YOURJSONDATA.filter(data => (
// Here we compare the field we want with the query search
data.first_name === query.first_name
));
return (
<div>
{
!!(info) && info.map(o => (<div>{o.first_name}</div>))
}
</div>
);
}
}

Here's how I did it....
In Learn.js__
//reading url
componentDidMount() {
const values = queryString.parse(this.props.location.search)
console.log(values.filter)
console.log(values.origin)
}
//redirection
redirect = (url) => {
this.props.history.push(url)
console.log(this.props)
}
<LearnCard onClick={() => this.redirect("/learn/Template/Cooks")} name="Cooks" image={process.env.PUBLIC_URL + '/image/cook.jpg'}/>
<LearnCard onClick={() => this.redirect("/learn/Template/Websites")} name="Websites" image={process.env.PUBLIC_URL + '/image/website.jpg'}/>
<LearnCard onClick={() => this.redirect("/learn/Template/Tv-Series")} name="Tv-Series" image={process.env.PUBLIC_URL + '/image/tv_series.jpg'}/>
<LearnCard onClick={() => this.redirect("/learn/Template/Cookbooks")} name="Cookbooks" image={process.env.PUBLIC_URL + '/image/cookbook.jpg'}/>
In Template.js__
componentDidMount () {
const url_name = this.props.match.params.name
console.log(this.props.match.params.name)
if (url_name === "Cooks") {
this.setState({data: cooks})
console.log(cooks)
}
if (url_name === "Cookbooks") {
this.setState({data: cookbooks})
console.log(cookbooks)
}
if (url_name === "Tv-Series") {
this.setState({data: tv_series})
console.log(tv_series)
}
if (url_name === "Websites") {
this.setState({data: websites})
console.log(websites)
}
}
render () {
return (
<div>
<div className="templateWrapper">
{
this.state.data && this.state.data.map((data, key) => {
return <TemplateCard className="templateCard" name={data.name} description={data.description} image={data.image} cuisine={data.cuisine} author={data.author} channel={data.channel} href={data.web_url} href={data.chef_url}/>
})
}
</div>
</div>
);
}

Related

React Beautiful DnD, multiple columns inside single droppable

I am trying to have a grid column layout, (2 columns) inside a single droppable container. The project is for an online menu where you can create a menu item, which goes into a droppable container, then you can drag that onto the menu that will be displayed to the user. So there is currently two columns. However the style of the menu demands two columns. Currently I am assigning different classNames to the mapped columns so I can make one of them grid but its pretty messy. Maybe there is a way I can hardcode the droppable instead of map them and run the map on the lists themselves inside each of the hardcoded droppables? Sorry if this is confusing, it sure is for me.
'results' is API data that is initially mapped into savedItems array where newly created menu items will go. Later on menuItems array will pull from the database as well. Right now just trying to have better styling control over the different droppables.
you can see where im assigning different classNames to the droppable during the mapping and its really not a reliable option.
//drag and drop states
const [state, setState] = useState({
menuItems: {
title: "menuItems",
items: []
},
savedItems: {
title: "savedItems",
items: results
}
})
useEffect(() => {
setState({ ...state, savedItems: { ...state.savedItems, items: results } })
}, [results])
// console.log("state", state)
console.log("dummy data", dummyArry)
// updating title graphql mutation
const [elementId, setElementId] = useState(" ");
const updateTitle = async () => {
//api data
const data = await fetch(`http://localhost:8081/graphql`, {
method: 'POST',
body: JSON.stringify({
query: `
mutation {
updateMenu(menuInput: {_id: ${JSON.stringify(elementId)},title: ${JSON.stringify(inputValue)}}){
title
}
}
`
}),
headers: {
'Content-Type': 'application/json'
}
})
//convert api data to json
const json = await data.json();
}
//drag end function
const handleDragEnd = (data) => {
console.log("from", data.source)
console.log("to", data.destination)
if (!data.destination) {
// console.log("not dropped in droppable")
return
}
if (data.destination.index === data.source.index && data.destination.droppableId === data.source.droppableId) {
// console.log("dropped in same place")
return
}
//create copy of item before removing from state
const itemCopy = { ...state[data.source.droppableId].items[data.source.index] }
setState(prev => {
prev = { ...prev }
//remove from previous items array
prev[data.source.droppableId].items.splice(data.source.index, 1)
//adding new item to array
prev[data.destination.droppableId].items.splice(data.destination.index, 0, itemCopy)
return prev
})
}
const columnClass = [
"menuItems-column",
"savedItems-column"
]
let num = 0
return (
<>
<div className='app'>
{results && <DragDropContext onDragEnd={handleDragEnd}>
{_.map(state, (data, key) => {
return (
<div key={key} className='column'>
<h3>{data.title}</h3>
<Droppable droppableId={key}>
{(provided, snapshot) => {
return (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={columnClass[num]}
// className="droppable-col"
><span className='class-switch'>{num++}</span>
{data.items.map((el, index) => {
return (
<Draggable key={el._id} index={index} draggableId={el._id}>
{(provided) => {
return (
<div className='element-container'
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div contentEditable="true">
{el.title}
</div>
</div>
)
}}
</Draggable>
)
})}
{provided.placeholder}
</div>
)
}}
</Droppable>
</div>
)
})}
</DragDropContext>}
</div>
</>
)
}

html strings array convert to JSX

`import React from 'react'
export default function Quiz(props){
// generate random index without duplicates
function generateRandomIndex(){
const randomNumArr=[]
for (var a = [0, 1, 2, 3], i = a.length; i--; ) {
var random = a.splice(Math.floor(Math.random() * (i + 1)), 1)[0];
randomNumArr.push(random)
}
return randomNumArr
}
let randomNumbers = generateRandomIndex()
let spreadOptions = ()=>{
let optionsHtmlArray = []
for(let i=0; i<props.answers.length; i++){
optionsHtmlArray.push(`<span className='answers' key=${i} style={${{backgroundColor: props.correct===props.answers[i] ? "green" : "red"}}}>
{ ${props.answers[i]} } </span>`)
}
return optionsHtmlArray
}
return (
<div className='Quiz'>
<h3 className='question'>{props.question}</h3>
<div className='answers_div'>
{ spreadOptions()[randomNumbers[0]] }
{ spreadOptions()[randomNumbers[1]] }
{ spreadOptions()[randomNumbers[2]] }
{ spreadOptions()[randomNumbers[3]] }
</div>
<hr className='hr'/>
</div>)
}
'
'//this is from App.js
// fetch to API when first render to save data to the state,
// and fetch depending on the sate of showOverlay
React.useEffect(() => {
fetch("https://opentdb.com/api.php?amount=5&category=9&difficulty=easy&type=multiple")
.then(res => res.json())
.then(data => {
setQuestions(data.results)
//after set questions state that comes from fetch request
//and set the custom questions with some properties I need
setCustomQuestions(prevQuestions=>{
let newArr=[]
for(let i=0; i<data.results.length; i++){
newArr.push({question: data.results[i].question,
questionId: nanoId(),
answers: [data.results[i].correct_answer].concat(data.results[i].incorrect_answers),
correct: data.results[i].correct_answer})
}
return newArr
})
})
}, [])
// Quiz component properties
const customQuestionsArr = customQuestions.map(question => {
return < Quiz
key={question.questionId}
question={question.question}
answers={question.answers}
correct={question.correct}
/>
})'
Hi all, I am trying to render all options of the answers in Quiz component, however,
spreadOptions() returns an array of html strings for the answers
I gotta parse to JSX to make it work.
I tried to install react-html-parser, didn't work it only gave me a bunch of error every time when I try to install dependencies through npm
I tried dangerouslySetInnerHTML, but also didn't work
Would you be able to provide the props that you are trying to pass to Quiz component?
Below is a snippet of code with modified spreadOptions and jsx. I wasn't able to test this code tho but will update it if you can provide the sample props.
let spreadOptions = props.answers.map((a, i) => (
<span
key={i}
className='answers'
style={{
backgroundColor: props.correct === a ? 'green' : 'red',
}}
>
{a}
</span>
));
return (
<div className="Quiz">
<h3 className="question">{props.question}</h3>
<div className="answers_div">
{spreadOptions}
</div>
<hr className="hr" />
</div>
);

state district json binding react

I want to display display list of districts from the json, receiving the following error
'TypeError: suggestion.districts.slice(...).toLowerCase is not a function'
json file.
How can I get the list of districts details, so that I can perform autocomplete using downshift?
any help appreciated.
json format
{
"states":[
{
"state":"Andhra Pradesh",
"districts":[
"Anantapur",
"Chittoor",
"East Godavari",
]
},
{
"state":"Arunachal Pradesh",
"districts":[
"Tawang",
"West Kameng",
"East Kameng",
]
},
}
component
import React, { Component } from 'react'
import statedist from "./StateDistrict.json";
const suggestions = statedist.states;
/*.... */
function getSuggestions(value, { showEmpty = false } = {}) {
// const StatesSelected=props.StatesSelected;
const inputValue = deburr(value.trim()).toLowerCase();
const inputLength = inputValue.length;
let count = 0;
//console.log(StatesSelected)
return inputLength === 0 && !showEmpty
? []
: suggestions.filter(suggestion => {
const keep =
count < 5 &&
suggestion.districts.slice(0, inputLength).toLowerCase() === inputValue;
if (keep) {
count += 1;
}
return keep;
});
}
function renderSuggestion(suggestionProps) {
const {
suggestion,
index,
itemProps,
highlightedIndex,
selectedItem
} = suggestionProps;
const isHighlighted = highlightedIndex === index;
const isSelected = (selectedItem || "").indexOf(suggestion.districts) > -1;
return (
<MenuItem
{...itemProps}
key={suggestion.districts[0]}
selected={isHighlighted}
component="div"
style={{
fontWeight: isSelected ? 500 : 400
}}
>
{suggestion.districts[0]} -- how can I get all the values instead of one here
</MenuItem>
);
}
class autoCompleteState extends Component {
constructor(props) {
super(props);
this.state = {
SelectedState:'',
}
// this.showProfile = this.showProfile.bind(this)
}
setSelectedDistrict = (newState) => {
this.setState({ SelectedState: newState });
console.log(newState)
this.props.onDistrictSelected(newState);
}
render() {
const { classes, } = this.props;
console.log(this.state.SelectedState)
const StatesSelected=this.props.StateList;
return (
<div>
<DownshiftMultiple
classes={classes}
setSelectedDistrict={this.setSelectedDistrict}
StatesSelected={StatesSelected}
/>
</div>
)
}
}
export default withStyles(Styles)(autoCompleteState);
I want the district details to come as suggestion like state in the below image
Currently, you are doing this:
suggestion.districts.slice(0, inputLength).toLowerCase() === inputValue;
This is throwing an error because .slice is copying inputLength items from your districts array and then trying to call .toLowerCase() on that array.
If I understand correctly, you are trying to filter your districts according to the inputValue. One way of doing this would be to use reduce on the districts array like this:
suggestion.districts.reduce((acc,curr)=>curr.substring(0,inputLength)===inputValue?[...acc,curr.substring(0,inputLength)]:acc, [])
If you only want the first 5 then you can slice the result of this:
suggestion.districts.reduce((acc,curr,index)=>index<5&&curr.substring(0,inputLength)===inputValue?[...acc,curr.substring(0,inputLength)]:acc, [])

How can I remove duplicate values ​from my json in React

I am developing an application with React and I have a problem. I have made a fetch from a json and the values are repeated. I want only each value to appear once.
I hope that it only returns once each of the elements of "Padre": "CRM", "Permisos" and "Telefonia". The same for "Hijo".
I've tried changing ".concat" to ".reduce" because I've read that it can be done like this, but it does not work. I have seen examples but none of them works on "this.state" so I do not know how to do it.
Can you help me with the correct way to do it?
This is a part of my json file
[
{
"Id":"114",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"115",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"116",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"44",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"45",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"46",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"47",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"50",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"51",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"52",
"Padre":"Telefonia",
"Hijo":"Configuracion"
},
{
"Id":"70",
"Padre":"Telefonia",
"Hijo":"Rutas"
}
]
For confidential reasons I can not show the actual file.
In my next code I change the fetch for a fake json url
I Edit my code with #Avanthika and #blaz code. Currently the code works but shows only the first value it finds of "Father" and "Son". I need to show only once each of the different values ​​of "Father" and "Son". Example result:
Father:
CRM
PERMISOS
Son:
ARGUMENTARIOS
ROOT
Currenly, with my new code i see only: CRM (Padre) and Argumentarios (Son). The rest of the elements are not shown.
import React, { Component } from 'react';
class Nav extends Component{
constructor(props){
super(props)
this.state = {
menuSubmenu:[]
}
}
componentWillMount(){
fetch('http://FAKE.php')
.then(response => response.json())
.then(menuSubmenu =>{
menuSubmenu.forEach(datos => {
let data = {
menu:datos.Padre,
submenu:datos.Hijo,
id:datos.Id
}
//console.log( data )
//console.log (data.menu);
this.setState({
menuSubmenu:this.state.menuSubmenu.concat([data])
})
})
})
}
render() {
const array = [...this.state.menuSubmenu];
const distinctValues = array.filter(
({ Padre, Hijo }, index) => {
return array.findIndex(item => item.Padre === Padre && item.Hijo === Hijo) === index;
});
//console.log(this.state.menuSubmenu);
//console.log(distinctValues);
if (distinctValues.length > 0) {
return(
<div>
{distinctValues.map(datos => <Navegacion key={datos.id} menu={datos.menu} submenu={datos.submenu} />)}
</div>
);
}
return (<p>Cargando usuarios...</p>);
}
}
class Navegacion extends Component{
render(){
return (
<ul className="list">
<li className="list__item">{this.props.menu}
<ul className="list">
<li className="list__item">
{this.props.submenu}
</li>
</ul>
</li>
</ul>
)
}
}
export default Nav;
This image is the result of my json.My json result
I hope you can help me with the "reduce" function.
¡Thanks a lot!
Hi you can use lodash and function uniqBy https://lodash.com/docs#uniqBy
_.uniqBy(menuSubmenu, function (e) {
return e.Padre;
});
You can use new Set to unique your array list by Padre and Hijo.
const array = [{"Id":"114","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"115","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"116","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"44","Padre":"Permisos","Hijo":"root"},
{"Id":"45","Padre":"Permisos","Hijo":"root"},
{"Id":"46","Padre":"Permisos","Hijo":"root"},
{"Id":"47","Padre":"Permisos","Hijo":"root"},
{"Id":"50","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"51","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"52","Padre":"Telefonia","Hijo":"Configuracion"},
{"Id":"70","Padre":"Telefonia","Hijo":"Rutas"}];
const distinctValues = Array.from(new Set(array.map(elem => `${elem.Padre}-${elem.Hijo}`))).map(distinctVal => {
const [ Padre, Hijo ] = distinctVal.split("-");
return ({
Id: array.find(elem => elem.Padre === Padre && elem.Hijo === Hijo).Id,
Padre,
Hijo
});
});
console.log(distinctValues);
Or you can go for lodash as mentioned to extract uniq values. You have to use uniqBy.
const array = [{"Id":"114","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"115","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"116","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"44","Padre":"Permisos","Hijo":"root"},
{"Id":"45","Padre":"Permisos","Hijo":"root"},
{"Id":"46","Padre":"Permisos","Hijo":"root"},
{"Id":"47","Padre":"Permisos","Hijo":"root"},
{"Id":"50","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"51","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"52","Padre":"Telefonia","Hijo":"Configuracion"},
{"Id":"70","Padre":"Telefonia","Hijo":"Rutas"}];
const distintValues = _.uniqBy(array, elem => [elem.Padre, elem.Padre].join());
console.log(distintValues);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You need to fix the solution this way:
render() {
const array = [...this.state.menuSubmenu];
// Put the solution you like here.
// const distinctValues = the solution you like
if (distinctValues.length > 0) {
return(
<div>
{distinctValues.map(datos => <Navegacion key={datos.id} menu={datos.menu} submenu={datos.submenu} />)}
</div>
);
}
return (<p>Cargando usuarios...</p>);
}
Original solution from Avanthika doesn't work because in your data, some item might possibly have Padre value containing dash character ("-"). Use a character that will never appear in Padre value as separator instead and you will be good.
My solution:
Use Array.prototype.filter
const distinctValues = this.state.menuSubmenu.filter(
({ Padre, Hijo }, index) => {
return this.state.menuSubmenu.findIndex(item => item.Padre === Padre && item.Hijo === Hijo) === index;
});
With Array.prototype.reduce it will look more bulky:
const distinctValues = this.state.menuSubmenu.reduce(
(array, item) => {
if (array.findIndex(i => i.Padre === item.Padre && i.Hijo === item.Hijo) === -1) {
array.push(item);
}
}, []);

Data passed to handlebars template not showing in the client side

I passed a viewData to the handlebar template like this
app.get('/employee/:value', (req, res) => {
let viewData = {};
dataService.getEmployeeByNum(req.params.value).then((data) => {
if (data) {
viewData.employee = data;
} else {
viewData.employee = null;
}
}).catch(() => {
viewData.employee = null;
}).then(dataService.getDepartments).then((data) => {
viewData.departments = data;
for (let i = 0; i < viewData.departments.length; i++) {
if (viewData.departments[i].departmentId == viewData.employee.department) {
viewData.departments[i].selected = true;
}
}
}).catch((err) => {
console.log(err);
viewData.departments = [];
}).then(() => {
if (viewData.employee == null) {
res.status(404).send("Employee not found");
} else {
res.render("employee", { layout: 'main', viewData: viewData })
}
})
});
and try to use it in the client side like this but ain't showing at all
<h2>{{viewData.employee.firstName}} {{ viewData.employee.lastName}} - Employee: {{ viewData.employee.employeeNum}}</h2>
I created an helper function to stringify the viewData object and this showed
{"employee":[{"employeeNum":3,"firstName":"Foster Lewa","lastName":"Billy","email":"louis.jessica86#gmail.com","SSN":"935-74-9918","addressStreet":"8 Midway Park","addressCity":"New York","addressState":"NY","addressPostal":"111","maritalStatus":null,"isManager":true,"employeeManagerNum":1,"status":"Full Time","department":3,"hireDate":"12/02/1999"}],"departments":[{"departmentId":1,"departmentName":null},{"departmentId":2,"departmentName":null},{"departmentId":3,"departmentName":"New Department"}]}
But never worked in the client side (html), what could be wrong ?
Try this one.
<h2>{{viewData.employee.1.firstName}} {{ viewData.employee.1.lastName}} - Employee: {{ viewData.employee.1.employeeNum}}</h2>
The viewData was passed as an array to the template and here is what I did to get the values
<h2>{{viewData.employee.[0].firstName}} {{ viewData.employee.[0].lastName}} - Employee: {{ viewData.employee.[0].employeeNum}}</h2>
Thanks!