I have a static information in web page.
class MyStaticWebPage extends React.Component {
render() {
return (
<TopContainer>
<IconListContainer>
<LeftButton
Icon={MyIcon1}
color="#ffffff"
text="text1"
/>
<CenterButton
Icon={MyIcon2}
color="#eeeeee"
text="text2"
/>
<RightButton
Icon={MyIcon3}
color="#dddddd"
text="text3"
/>
</IconListContainer>
<IconListContainer>
<LeftButton
Icon={MyIcon4}
color="#cccccc"
text="text4"
/>
</IconListContainer>
</TopContainer>
);
}
}
This page is statically display in a row list, per line maximum three icons, and now I want to turn them dynamically, suppose I store icon props in a props array.
[
{
icon: 'MyIcon1',
color: '#ffffff',
text: 'text1'
},
{
icon: 'MyIcon2',
color: '#eeeeee',
text: 'text2'
},
{
icon: 'MyIcon3',
color: '#dddddd',
text: 'text3'
},
{
icon: 'MyIcon4',
color: '#cccccc',
text: 'text4'
}
]
Finally make page automatically rendered using this props array.
class MyStaticWebPage extends React.Component {
render() {
var rows = []
for (var i = 0; i <= parseInt(iconNum / 3); i++) {
// row level for loop
// rows.push(row)
for (var j = iconNum; j % 3 !== 0; j--) {
// icon level for loop
// rows.push(icon)
}
}
return (
<TopContainer>
{rows}
</TopContainer>
);
}
}
How to do with this through realistic react code?
Given you have a flat array but want to render it in rows of three the first thing you should do is chunk the array. Lodash has a method for this or you can do a simple enough reduce on your array.
const chunkedArray = icons.reduce((reduction, icon, index) => {
index % 3 ? reduction[reduction.length - 1].push(icon) : reduction.push([icon])
return reduction
}, [])
Now you have your data in the right shape we can easily map that to output jsx.
class IconListWrapper extends React.Component {
render() {
const { subList } = this.props
const buttonTypes = ['LeftButton', 'CenterButton', 'RightButton']
const Element = buttonTypes[index]
return (
<IconListContainer>
{subList.map((icon, index) => <Element
Icon={MyIcon1}
color="#ffffff"
text="text1"
/>)}
</IconListContainer>
);
}
}
class MyStaticWebPage extends React.Component {
render() {
return (
<TopContainer>
{chunkedArray.map((subList) => <IconListWrapper subList={subList} />)}
</TopContainer>
);
}
}
I think you're asking how to make sure you group the icons into groups of three using LeftButton, CenterButton, and RightButton.
I'll assume you start with something like this:
var icons = [
{
icon: 'MyIcon1',
color: '#ffffff',
text: 'text1'
},
{
icon: 'MyIcon2',
color: '#eeeeee',
text: 'text2'
},
{
icon: 'MyIcon3',
color: '#dddddd',
text: 'text3'
},
{
icon: 'MyIcon4',
color: '#cccccc',
text: 'text4'
}
];
then, see comments:
class MyStaticWebPage extends React.Component {
var buttonTypes = [LeftButton, CenterButton, RightButton];
render() {
var rows = [];
var children = [];
for (var i = 0; i < icons.length; i++) {
// x will be 0, 1, or 2
var x = i % 3;
// Get the button type to use
var buttonType = buttonTypes[x];
// Create the button using `createElement`
children.push(React.createElement(buttonType, icons[i]);
// If this is the last button of three, add these in a container
// and get a new array for children
if (x == 2) {
rows.push(<IconContainer>{children}</IconContianer>);
children = [];
}
}
// Handle any remaining children
if (children.length) {
rows.push(<IconContainer>{children}</IconContianer>);
}
return (
<TopContainer>
{rows}
</TopContainer>
);
}
}
As pointed out in other answers - the loop can be achieved with map function. To display them dynamically, you may wish to take a look at flexbox and use them in css.
One possible way of writing is like this:
var buttonTypes = [LeftButton, CenterButton, RightButton];
let table = [];
arr.forEach((el, i) => {
let Component = buttonTypes[i%3];
rows.push(
<Component
Icon={el.icon}
text={el.text}
color={el.color}
/>
)
if(i%3 == 2) {
table.push( <IconListContainer> {rows} </IconListContainer> )
rows = [];
}
})
if (rows.length) {
table.push( <IconListContainer> {rows} </IconListContainer> );
}
return (
<TopContainer>
{table}
</TopContainer>
);
You can try something like this.
const icons = [
{
icon: 'MyIcon1',
color: '#ffffff',
text: 'text1'
},
{
icon: 'MyIcon2',
color: '#eeeeee',
text: 'text2'
},
{
icon: 'MyIcon3',
color: '#dddddd',
text: 'text3'
},
{
icon: 'MyIcon4',
color: '#cccccc',
text: 'text4'
}
];
class MyStaticWebPage extends React.Component {
const menu = [
({ icon, color, text }) => (<LeftButton Icon={icon} color={color} text={text} />),
({ icon, color, text }) => (<CenterButton Icon={icon} color={color} text={text} />),
({ icon, color, text }) => (<RightButton Icon={icon} color={color} text={text} />)
];
render() {
return (
<TopContainer>
<IconListContainer>
{icons && icons.map((icon, i) => menu[i % 3](icon))}
</IconListContainer>
</TopContainer>
);
}
}
Related
I am using an datatable of react-data-table-component, my table is generated from the API response data. I want to dynamically add a class to each of the rows generated based on the condition. How can I acheive this ?
https://www.npmjs.com/package/react-data-table-component
I am using the above datatable.
let columns= [
{
name: "ID",
selector: "ID",
sortable: true,
cell: row => <div>{row.ID}</div>
}];
<Datatable
columns={columns}
data={this.state.mydata} />
I want to add a custom CSS class to the entire row of this data table based on a condition.
I think you might be looking for the getTrProps callback in the table props:
getTrProps={ rowInfo => rowInfo.row.status ? 'green' : 'red' }
It's a callback to dynamically add classes or change style of a row element
Should work like this if I remember correctly:
getTrProps = (state, rowInfo, instance) => {
if (rowInfo) {
return {
className: (rowInfo.row.status == 'D') ? "status-refused" : "", // no effect
style: {
background: rowInfo.row.age > 20 ? 'red' : 'green'
}
}
}
return {};
}
render() {
<Datatable
columns={columns}
data={this.state.mydata}
getTrProps={this.getTrProps}
/>
}
example:
...
const conditionalRowStyles = [
{
when: row => row.calories < 300,
style: {
backgroundColor: 'green',
color: 'white',
'&:hover': {
cursor: 'pointer',
},
},
},
];
const MyTable = () => (
<DataTable
title="Desserts"
columns={columns}
data={data}
conditionalRowStyles={conditionalRowStyles}
/>
);
more info check here :) https://www.npmjs.com/package/react-data-table-component#conditional-row-styling
How can I compare if the props between two separate components have the same value?
1- Is what I'm seeking doable?
2- If not, how else could I accomplish the ask below:
The story:
I have an array of car objects.
Each car's name is displayed as <li /> on a <CarList /> component.
Upon click on each <li/> the car's color is revealed
I have a <Question /> component that renders: "What car is (random color here)"?
UI change:
How could I write a method that:
Checks if the props.color of <CarList /> === the props.color of <Question />
Then it fires a UI change such as:
onClick: If the car's color matches the question's color: change the <li /> to green (ie: background-color), else change it to red.
I'm struggling (wondering if it's possible) to compare props from different components + writing a method that checks and executes the UI change above.
This is the code reflecting the explanation above: Also here's the sandbox
// Garage
export default class Garage extends Component {
state = {
cars: [
{ name: "Ferrari", color: "red", id: 1 },
{ name: "Porsche", color: "black", id: 2 },
{ name: "lamborghini", color: "green", id: 3 },
{ name: "McLaren", color: "silver", id: 4 },
{ name: "Tesla", color: "yellow", id: 5 }
]
};
handleShuffle = () => {
this.setState({
cars: [...this.state.cars.sort(() => Math.random() - 0.5)]
});
};
render() {
const { cars } = this.state;
const car = cars.map(car => (
<CarList key={car.id} make={car.name} color={car.color} />
));
const guess = cars
.slice(2, 3)
.map(car => <Question key={car.id} color={car.color} />);
return (
<>
<div>{guess}</div>
<button onClick={this.handleShuffle}>load color</button>
<ul>{car}</ul>
</>
);
}
}
// CarList
class CarList extends Component {
state = {
show: false
};
handleShow = () => {
this.setState({ show: true });
console.log(this.props);
// check for props equality here
//desired result for <li /> would be
// className={ correctColor ? 'green' : 'red'}
};
render() {
console.log("car color props:", this.props.color);
const { make, color } = this.props;
const { show } = this.state;
return (
<li onClick={this.handleShow}>
{make}
<span className={show ? "show" : "hide"}>{color}</span>
</li>
);
}
}
// Question
const Question = ({ color }) =>
console.log("question color prop:", color) || <h1>What car is {color}</h1>;
Yes, you can pass the correct color to the CarList component or the flag whether the CarList is a correct one. Check my sandbox.
https://codesandbox.io/s/92xnwpyq6p
Basically we can add isCorrect prop to CarList which has value of correctCar.color === car.color and we use it to determine whether we should render it green or red.
Theres many ways to do this but the simplest is to send the color in the question down to the car component.
https://codesandbox.io/s/my4wmn427x
I am trying to use JsonSchema-Form component but i ran into a problem while trying to create a form that, after choosing one of the options in the first dropdown a secondary dropdown should appear and give him the user a different set o options to choose depending on what he chose in the first dropdown trough an API call.
The thing is, after reading the documentation and some examples found here and here respectively i still don't know exactly how reference whatever i chose in the first option to affect the second dropdown. Here is an example of what i have right now:
Jsons information that are supposed to be shown in the first and second dropdowns trough api calls:
Groups: [
{id: 1,
name: Group1}
{id: 2,
name: Group2}
]
User: [User1.1,User1.2,User2.1,User2.2,User3.1,User3.2, ....]
If the user selects group one then i must use the following api call to get the user types, which gets me the the USER json.
Component That calls JSonChemaForm
render(){
return(
<JsonSchemaForm
schema={someSchema(GroupOptions)}
formData={this.state.formData}
onChange={{}}
uiSchema={someUiSchema()}
onError={() => {}}
showErrorList={false}
noHtml5Validate
liveValidate
>
)
}
SchemaFile content:
export const someSchema = GroupOptions => ({
type: 'object',
required: [
'groups', 'users',
],
properties: {
groups: {
title: 'Group',
enum: GroupOptions.map(i=> i.id),
enumNames: GroupOptions.map(n => n.name),
},
users: {
title: 'Type',
enum: [],
enumNames: [],
},
},
});
export const someUISchema = () => ({
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
},
types: {
'ui:options': {
size: {
lg: 15,
},
},
},
});
I am not really sure how to proceed with this and hwo to use the Onchange method to do what i want.
I find a solution for your problem.There is a similar demo that can solve it in react-jsonschema-form-layout.
1. define the LayoutField,this is part of the demo in react-jsonschema-form-layout.To make it easier for you,I post the code here.
Create the layoutField.js.:
import React from 'react'
import ObjectField from 'react-jsonschema-form/lib/components/fields/ObjectField'
import { retrieveSchema } from 'react-jsonschema-form/lib/utils'
import { Col } from 'react-bootstrap'
export default class GridField extends ObjectField {
state = { firstName: 'hasldf' }
render() {
const {
uiSchema,
errorSchema,
idSchema,
required,
disabled,
readonly,
onBlur,
formData
} = this.props
const { definitions, fields, formContext } = this.props.registry
const { SchemaField, TitleField, DescriptionField } = fields
const schema = retrieveSchema(this.props.schema, definitions)
const title = (schema.title === undefined) ? '' : schema.title
const layout = uiSchema['ui:layout']
return (
<fieldset>
{title ? <TitleField
id={`${idSchema.$id}__title`}
title={title}
required={required}
formContext={formContext}/> : null}
{schema.description ?
<DescriptionField
id={`${idSchema.$id}__description`}
description={schema.description}
formContext={formContext}/> : null}
{
layout.map((row, index) => {
return (
<div className="row" key={index}>
{
Object.keys(row).map((name, index) => {
const { doShow, ...rowProps } = row[name]
let style = {}
if (doShow && !doShow({ formData })) {
style = { display: 'none' }
}
if (schema.properties[name]) {
return (
<Col {...rowProps} key={index} style={style}>
<SchemaField
name={name}
required={this.isRequired(name)}
schema={schema.properties[name]}
uiSchema={uiSchema[name]}
errorSchema={errorSchema[name]}
idSchema={idSchema[name]}
formData={formData[name]}
onChange={this.onPropertyChange(name)}
onBlur={onBlur}
registry={this.props.registry}
disabled={disabled}
readonly={readonly}/>
</Col>
)
} else {
const { render, ...rowProps } = row[name]
let UIComponent = () => null
if (render) {
UIComponent = render
}
return (
<Col {...rowProps} key={index} style={style}>
<UIComponent
name={name}
formData={formData}
errorSchema={errorSchema}
uiSchema={uiSchema}
schema={schema}
registry={this.props.registry}
/>
</Col>
)
}
})
}
</div>
)
})
}</fieldset>
)
}
}
in the file, you can define doShow property to define whether to show another component.
Next.Define the isFilled function in JsonChemaForm
const isFilled = (fieldName) => ({ formData }) => (formData[fieldName] && formData[fieldName].length) ? true : false
Third,after you choose the first dropdown ,the second dropdown will show up
import LayoutField from './layoutField.js'
const fields={
layout: LayoutField
}
const uiSchema={
"ui:field": 'layout',
'ui:layout': [
{
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
}
},
{
users: {
'ui:options': {
size: {
lg: 15,
},
},
doShow: isFilled('groups')
}
}
]
}
...
render() {
return (
<div>
<Form
schema={schema}
uiSchema={uiSchema}
fields={fields}
/>
</div>
)
}
I am having issues on even trying to get started with doing pagination without the use of any packages. I am pulling data from a JSON file that contains about 30-32 quotes. I need 15 quotes per page to be displayed and have no idea how to even do that using React. So far what I have is all the quotes being displayed by default. I have three buttons, each filters through the JSON to provide quotes by the theme of the quote which is displayed by the button. This is how far I got:
class App extends Component {
constructor(props) {
super(props);
this.state ={
results: quotes,
search: ""
}
}
gameFilterClick = event => {
event.preventDefault();
const games = [];
for(let i = 0; i < quotes.length; i++){
if (quotes[i].theme === "games"){
games.push(quotes[i])
}
}
this.setState({results: games})
}
movieFilterClick = event => {
event.preventDefault();
console.log('blah!!')
const movies = [];
for(let i =0; i < quotes.length; i++){
if(quotes[i].theme === 'movies'){
movies.push(quotes[i])
}
}
this.setState({results: movies})
}
allButtonClick = event => {
this.setState({results: quotes})
}
quoteSearch = query => {
let search = quotes.map
}
render() {
return (
<div className="App">
<h1>Quotes</h1>
<Search />
<div id='buttons'>
Filters:
<button onClick={this.allButtonClick}>All Quotes</button>
<button onClick={this.gameFilterClick}>Games</button>
<button onClick={this.movieFilterClick}>Movies</button>
</div>
<div id='resultsDiv'>
<Results
results={this.state.results}
/>
</div>
</div>
);
}
}
export default App;
I would recommend using react-bootstrap for this. You'll need to install two packages (they use to come in one, but now pagination package is separated):
react-bootstrap-table-next
react-bootstrap-table2-paginator
So, let's install them:
npm i --save react-bootstrap-table-next
npm i react-bootstrap-table2-paginator
And here goes a simple example of implementation:
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// Let's imagine this is your JSON data
const yourJsonData = [{id: 1, author: "David Goggins", quote: "Life goes on"},
{ id: 2, author: "Robert Green", quote: "yes it does"}]:
// Here we define your columns
const columns = [{
dataField: 'author',
text: 'AUTHOR'
}, {
dataField: 'quote',
text: 'QUOTE'
}];
// Give it an option to show all quotes
let allQuotes = Number(yourJsonData.length);
// Set all of the major pagination options. You can reduce them if you want less
const options = {
paginationSize: 15,
pageStartIndex: 0,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
sizePerPageList: [{
text: 'show 15', value: 15
}, {
text: 'show 30', value: 30
}, {
text: 'Show all', value: allQuotes
}]
};
... and then somewhere later in your code where you want to display the table with pagination you just insert this:
<BootstrapTable
keyField='rowNumber'
data={ yourJsonData }
columns={ columns }
pagination={ paginationFactory(options) } />
I hope this solves your problem.
I've simplified your filtering logic and added client side pagination. Check out this simple working example (i've set item per page to 3, you can add more data and change it to 15 const QUOTES_PER_PAGE = <number of quotes per page>;)
const QUOTES_PER_PAGE = 3;
const Quote = ({text}) => <li>{text}</li>;
const Pagination = ({pages, goTo}) => (
<div>
{pages.map((p, i) => (
<button key={i} onClick={goTo} value={i}>{i+1}</button>
))}
</div>
)
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 0,
pagedQuoutes: this.divideQuoutesIntoPages(props.quotes)
};
}
divideQuoutesIntoPages = (quotes => {
const pagedQuotes = [];
[...Array(Math.ceil(quotes.length/QUOTES_PER_PAGE))].forEach((q, i) => {
pagedQuotes.push(quotes.slice(0 + QUOTES_PER_PAGE*i, QUOTES_PER_PAGE + QUOTES_PER_PAGE*i))
})
return pagedQuotes;
})
filterQuoutes = (evt) => {
const filterValue = evt.target.value;
const filteredQuoutes = this.props.quotes.filter(q => !filterValue || q.theme === filterValue);
this.setState({
pagedQuoutes: this.divideQuoutesIntoPages(filteredQuoutes)
})
}
goToPage = (evt) => {
this.setState({
page: evt.target.value
})
}
render() {
return (
<div>
<h1>Quotes</h1>
<div>
Filters:
<button onClick={this.filterQuoutes}>All Quotes</button>
<button onClick={this.filterQuoutes} value="games">Games</button>
<button onClick={this.filterQuoutes} value="movies">Movies</button>
</div>
{this.state.pagedQuoutes[this.state.page]
.map(q => (
<ul>
<Quote {...q} />
</ul>
))}
<Pagination pages={this.state.pagedQuoutes} goTo={this.goToPage} />
</div>
);
}
}
const exampleQuotes = [{
theme: 'games',
text: 'games q1'
}, {
theme: 'games',
text: 'games q2'
}, {
theme: 'games',
text: 'games q3'
}, {
theme: 'games',
text: 'games q4'
}, {
theme: 'games',
text: 'games q5'
}, {
theme: 'movies',
text: 'movies q1'
}, {
theme: 'movies',
text: 'movies q2'
}, {
theme: 'movies',
text: 'movies q3'
}]
ReactDOM.render(<App quotes={exampleQuotes} />, document.getElementById("el"))
<div id="el"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
https://github.com/carlosrocha/react-data-components package does not allow sending html into a td cell. See:
My goal is hyperlink to that product.
My use is:
import React from 'react';
var DataTable = require('react-data-components').DataTable;
import PlainTable from './PlainTable'
class ReduxDataTable extends React.Component {
constructor(props) {
super(props);
}
processHeaders(){
var columns = [];
for (let i = 0; i < this.props.data.headers.length; i++){
var header = this.props.data.headers[i];
var item = {title: header, prop: header};
columns.push(item);
}
return columns;
}
render() {
var dataList = this.props.data.data;
console.log("datalist is", dataList);
console.log("datalist length is", dataList.length);
var headerList = this.processHeaders();
if(dataList.length > 2) {
return (
<DataTable
keys="name"
columns={headerList}
initialData={dataList}
initialPageLength={20}
initialSortBy={{ prop: headerList[0].title, order: 'descending' }}
pageLengthOptions={[ 20, 60, 120 ]}
/>
);
}
else {
return (
<PlainTable
headers={headerList}
rows={dataList}
/>
);
}
}
}
export { ReduxDataTable as default };
then just
return (
<div className="card">
<h2 className="style-1">Detailed Report</h2>
<br/>
<h2 className="style-1:after">Data about products </h2>
<ReduxDataTable data={data}/>
</div>
)
Plain table is a <table> in case there's few products.
The package does not show any "htmlTrue" option, as searching "html" show nothing useful. I'm getting the same issue with any html at all:
I'm not opposed to forking it, but is there a simple way to use this package and declare html here?
I didn't use that component, but looking through the code, it seems that you can use a render function to do what you need. See here: https://github.com/carlosrocha/react-data-components/blob/3d092bd375da0df9428ef02f18a64d056a2ea5d0/src/Table.js#L13
See the example here https://github.com/carlosrocha/react-data-components/blob/master/example/table/main.js#L17
Relevant code snippet:
const renderMapUrl =
(val, row) =>
<a href={`https://www.google.com/maps?q=${row['lat']},${row['long']}`}>
Google Maps
</a>;
const tableColumns = [
{ title: 'Name', prop: 'name' },
{ title: 'City', prop: 'city' },
{ title: 'Street address', prop: 'street' },
{ title: 'Phone', prop: 'phone', defaultContent: '<no phone>' },
{ title: 'Map', render: renderMapUrl, className: 'text-center' },
];
return (
<DataTable
className="container"
keys="id"
columns={tableColumns}
initialData={data}
initialPageLength={5}
initialSortBy={{ prop: 'city', order: 'descending' }}
pageLengthOptions={[ 5, 20, 50 ]}
/>
);
Try adding the render property to your dataList. Maybe something like this
var dataList = this.props.data.data;
for (let i=0; i<dataList.length; i++)
dataList[i].render = function(val, row) {return (
<a href={row.href}>row.title</a>
)}