React - How to iterate over dictionary in render method using JSX? - ecmascript-6

I'm learning React and have set up a small test app that makes an Ajax call that returns a JSON object that I want to iterate over in the return method of my component. I've tried everything I can think of and have googled this, but like an hour later I'm still stumped.
Here is what I have...
render() {
const { vals } = this.state;
return (
<div>
{/* note that this correctly outputs the value of vals[key]: {vals['key']} */}
Object.keys({vals}).map((key, index) => (
<p key={index}> this is my key {key} and this is my value {vals[{key}]} </p>
))
</div>
)
}
What am I doing wrong here? Any recommendations on a good reference for ES6/JSX? I've been struggling with simple things, with no good way to look up this info.

{vals} extracts out the vals property from an object. Hence Object.keys({vals}) is incorrect as vals is already an object. Likewise, it should be {vals[key]} instead of {vals[{key}]}.
render(){
const {vals} = this.state; // Essentially does: const vals = this.state.vals;
return (
<div>
{
Object.keys(vals).map((key, index) => (
<p key={index}> this is my key {key} and this is my value {vals[key]}</p>
))
}
</div>
)
}
If vals is an object containing { a: 1, b: 2}, Object.keys(vals) will get you ['a', 'b'] and in the first iteration of the map, key will be 'a' and to access the value, do vals[key] which is essentially vals['a'] => 1.
I think you are confused by the Object destructuring syntax. It's really quite simple as it's just syntactic sugar over ES5 JavaScript (most ES6 is just sugar). Have a read at MDN's docs on Destructuring assignment to understand it better.

Related

React: Skip row if json parent tag don't exist

I am in the following situation and have the following problems.
I am developing an application that reads data from a .json file. I store this data in rows. Now the .json files can be different and therefore I have to cover every case (there are many cases). If a tag is not present in the .json, this row should not be displayed.
Now it can happen that I have covered a case which searches for data.test.person but data.test is not present in the JSON.
A case could look like this:
<TestAntragsdatenAbschnitt
title={"Test"}
value={data.test}>
<TestAntragsdatenRow
label1={"Person"}
value1={data.test.person}
/>
</TestAntragsdatenAbschnitt>
This is my component.
export default class TestAntragsdatenAbschnitt extends React.Component {
value;
title;
render() {
this.value = this.props.value;
this.title = this.props.title;
return (
<>
<h4 className={"antragsdatenAbschnitt"}>
{checkAbschnitt(this.title, this.value)}
</h4>
{this.value != null &&
this.props.children
}
</>
);
}
}
With the query this.value != null && I have tried to work around the error.
The error I get: TypeError: Cannot read property 'person' of undefined
My question now is, how can I query JSON tags if they exist, if so the rows should be checked. If not all rows with this tag should be skipped.
Object.keys(data).includes('test')
can be a way of checking if json object has the property.
There are a lot of other ways as well. You can also try:
value1={data.test ? data.test.person : 'error'}
kind of approach for safe null-checking
There are a couple of things wrong with your code, but let's try to unpack everything:
You're passing two props, title and value to your component named TestAntragsdatenAbschnitt. This you should receive in props, and therefore there's no need for you to store them in fields (this.value = this.props.value) for example.
Rather, just do this in the render function:
const {title, value, children} = this.props;
And now you don't need to use this.title and this.value, but just title and value.
OK we got that out of the way, now let's figure out why your optional key isn't working:
Now you should be rendering your children like this:
{children}
Now you need to conditionally render particular rows, and this shouldn't be done here in this component at all. This should be done in the component that's rendering TestAntragsdatenAbschnitt (first component you wrote in the post).
So you'd do something like this:
<TestAntragsdatenAbschnitt
title={"Test"}
value={data.test}>
{data && data.test && data.test.person ? (
<TestAntragsdatenRow
label1={"Person"}
value1={data.test.person}
/>
) : (
<p>data.test.person is not valid</p>
)}
</TestAntragsdatenAbschnitt>
As you can see, I check with the ternary operator if data.test.person is not null, if it isn't then just render the row, if not then just do something else you'd like.
You could do this in other component, but this way it's way cleaner in my opinion.

How to render nested routes with multiple optional params and path placeholders?

I've been trying to use react-router to define a series of nested components/routes w/ optional params, but also separated by path placeholders.
i.e.
/list
/list/1
/list/items
/list/1/items
/list/1/items/1
I would assume the two <Route> paths would be something like:
/list/:listId?
and
`${match.url}`/items/:itemId?`
But alas.. "items" always ends up being accepted as the listId param, and therefore, the sub-routing for /items never matches.
I have a generic example I've coded here (doesn't accomplish a solution): https://stackblitz.com/edit/nesting-w-optional-params-react-router
I see examples all over the internet for /root/:id1?/:id2? but nothing for what I'm trying to do where I have a placeholder between the params: /root/:id1/placeholder/:id2.
Is this possible to do w/ react-router v4+ ??
Figured out a solution utilizing RR's children pattern, to always render the nested route/component, but then inside the component use path-to-regexp directly to do route matching and to grab relevant route params from props.location.pathname.
Relevant bit of code:
render() {
const pathRe = new PathToRegexp('/foo/:fooId?/bar/:barId?')
const match = pathRe.match(this.props.location.pathname)
if (!match) {
return null
}
const barId = (this.props.match && this.props.match.params.barId) || match.barId
return <h1>Bar <em>{barId}</em></h1>
}
https://stackblitz.com/edit/nesting-w-optional-params-react-router-solution

Implement CSV download using current filters and sort

I need to implement a download feature. It will read the data in the react-data-grid (adazzle), respecting the current columns, filters and sort, and create an array json (or comma separated strings) I can then pass to the react-csv module.
I have a data structure populated from the backend but it is not filtered nor sorted. I need to be able to ask the grid for it's data on a row-by-row basis. Can anyone point me in the right direction?
Without code or some context, I can't answer with certainty...
You supply the rowGetter prop with the collection to display, or the method to get the rows to display...I'm thinking if you filtering, then most likely you've got some sort of mechanism supporting that... Either way, you can use this property's value somehow to get exactly what you see in the grid.
If you literally want to interrogate the grid, you could try adding a reference to the grid, and then see if you can ask it for the row data. I can't remember with certainty that I saw a rows prop in the grids available props via the ref, but I imagine you should be able to (**,)
...
handleExport = async => {
const exportRows = rows;
// const exportRows = getRows(initialRows, filters);
// const exportRows = this.state.gridref.CurrentRows DISCLAIMER:CurrentRows is just for giving the idea... check out the ref yourself to see if it's possible to get the rows via the grid refs props.
downloadCSV( exportRows )
}
...
<ReactDataGrid
ref={input => {this.state.gridref = input}}
columns={columns}
rowGetter={i => rows[i]} // or maybe rowGetter={i => getRows(initialRows, filters)[i]}
rowsCount={rows.length}
onGridSort={(sortColumn, sortDirection) =>
setRows(sortRows(initialRows, sortColumn, sortDirection))
}
/>
I've only ever [set / initialised] the this.state.gridRef prop in my constructor, but I guess you could also [set / initialise] it in your componentDidMount as well...
initialise like this:
this.state.gridRef = React.createRef()

Vuejs changes order of json_encoded array, when decodes it back from props in vuejs component

Php:
$json_string = "{
"26":{"blabla":123,"group_id":1,"from":"27.08.2018","to":"02.09.2018"},
"25":{"blabla":124,"group_id":1,"from":"20.08.2018","to":"26.08.2018"},
"24":{"blabla":125,"group_id":1,"from":"20.08.2018","to":"26.08.2018"}
}"
my.blade.php template:
<my-component :records={{ $json_string }}></my-component>
MyComponent.vue:
export default {
props: ['records'],
...
mounted: function () {
console.log(this.records);
}
Output is:
{__ob__: Observer}
24:(...)
25:(...)
26:(...)
And when I use v-for, records in my table in wrong order (like in console.log output).
What I am doing wrong?
EDIT:
I figured out 1 thing:
When I do json_encode on collection where indexes are from 0 till x, than json string is: [{some data}, {some data}]
But if I do ->get()->keyBy('id') (laravel) and than json_encode, json string is:
{ "26":{some data}, "25":{some data}, "24":{some data} }
Then how I understood, issue is in different outer brackets.
In Javascript keys of objects have no order. If you need a specific order then use arrays.
Here is documentation for keyBy Laravel method: https://laravel.com/docs/5.6/collections#method-keyby
I wanted to have ids for rows data to fast and easy access without iterating over all rows and check if there is somewhere key Id which is equals with my particular Id.
Solution: not to use keyBy method in Laravel and pass json string to Vue component like following [{some data}, {some data}] (as I described in my Question Edit section) - this will remain array order as it used to be.
I found this short and elegant way how to do this, instead of writing js function by myself:
Its find() method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
Example:
let row = records.find( record => record.id === idToFind );

How to work with JSON object in React

So here is the set up:
I have simple nodejs backend (based on express and mongoose), which responds to GET request with some JSON (in the form of Object of objects).
So after I get the response, I want to render a component, for each elements of said Object of objects. If it was array, I could simply use array.map(), and render a component in the callback function. But since what I have, is Object, i can not use that.
So... Should I return and Array from the backend. If so how do I tell mongoose to return the result of model.find() in the form of array.
Or should I convert the object to array in the frontend? In this case, how would I do it without putting it through a loop of some sort?
Lastly, I tried to make it work like so:
render: function() {
//console.log('render TodoList componenr');
var items = this.state.todoItems;
return(
<ul>
{for (var item in items){
console.log(item);
}}
</ul>
);
}
To which i get this error:
Uncaught SyntaxError: embedded: Unexpected token (30:9)
28 | return(
29 | <ul>
> 30 | {for (var item in items){
| ^
31 |
32 | }}
33 | </ul>
Which is super weird, as it points to empty location?
Any ideas how could make this work?
To iterate over an object you could use Object.keys like so:
Object.keys(yourObject).map(function(key) {
return renderItem(yourObject[key]);
});
The method returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
It's supported by IE >= 9, Chrome >= 5, Safari >= 5, Firefox >= 4.
You can setting the object.map function equal to a variable outside the return function and then just return that variable.
render() {
var article = this.props.article;
var articleNodes = article.map(function(article, key){
if(article.iurl == ""){
article.iurl = "basketball.jpg";
};
return(
<li key={key}>
<Image item={article}/>
<div className="post-basic-info">
<h3><a target="_blank" href={article.url}>{article.title}</a></h3>
<span><label> </label>{article.team}</span>
<p>{article.description}</p>
</div>
</li>
)
});
return(
<div>
{articleNodes}
</div>
)
}