I am using the Dropdown of PrimeReact.
I have this code, but in the Dropdown I only get to show the label and not the name.
How can I display the name of each element of the optionsSearch variable in the Dropdown menu to select these names?
import React, {Component} from 'react';
import {Dropdown} from 'primereact/components/dropdown/Dropdown';
class OptionsExample extends Component {
constructor(props) {
super(props);
this.state = {
optionsSearch: 'FORM_FIELDS'
};
}
onOptionChange = e => {
this.setState({optionsSearch: e.value});
}
render() {
const optionsSearch = [
{key: 'NAME1', name: 'NAME1', label: 'DESCRIPTION1'},
{key: 'NAME2', name: 'NAME2', label: 'DESCRIPTION2'},
{key: 'NAME3', name: 'NAME3', label: 'DESCRIPTION3'}
];
return (
<div>
<div className='ui-g-12 ui-md-12 ui-lg-12'>
<Dropdown value={this.state.optionsSearch} options={optionsSearch} onChange={this.onOptionChange} style={{width: '180px'}} placeholder={`${this.state.optionsSearch}`} />
</div>
</div>
);
}
}
export default OptionsExample;
This code is copied from PrimeReact source code for dropdowns..
https://github.com/primefaces/primereact/blob/master/src/components/dropdown/DropdownItem.js
render() {
let className = classNames('ui-dropdown-item ui-corner-all', {
'ui-state-highlight': this.props.selected,
'ui-dropdown-item-empty': (!this.props.label || this.props.label.length === 0)
});
let content = this.props.template ? this.props.template(this.props.option) : this.props.label;
return (
<li className={className} onClick={this.onClick}>
{content}
</li>
);
}
As you can see, {content} is being rendered for each dropdown item, which only contains the "label".
let content = this.props.template ? this.props.template(this.props.option) : this.props.label;
Therefore if you want to show the "name", you have to put it in label.
Their demo also uses the "label" and "value" attribute only.
https://www.primefaces.org/primereact/#/dropdown
EDIT credits to #Chris G
You can also render a custom content, but then you have to pass a template function to the dropdown.
Their demo shows this.
carTemplate(option) {
if(!option.value) {
return option.label;
}
else {
var logoPath = 'showcase/resources/demo/images/car/' + option.label + '.png';
return (
<div className="ui-helper-clearfix">
<img alt={option.label} src={logoPath} style={{display:'inline-block',margin:'5px 0 0 5px'}} width="24"/>
<span style={{float:'right',margin:'.5em .25em 0 0'}}>{option.label}</span>
</div>
);
}
}
Which then they passed it to the Dropdown component.
<Dropdown value={this.state.car} options={cars} onChange={this.onCarChange} itemTemplate={this.carTemplate} style={{width:'150px'}} placeholder="Select a Car"/>
Related
I have a class component that Renders a list of elements and I need to focus them when an event occurs.
Here is an example code
class page extends React.Component {
state = {
items: [array of objects]
}
renderList = () => {
return this.state.items.map(i => <button>{i.somekey}</button>)
}
focusElement = (someitem) => {
//Focus some item rendered by renderList()
}
render(){
return(
<div>
{this.renderList()}
<button onClick={() => focusElement(thatElement)}>
</div>
)
}
}
I know that I need to use refs but I tried several ways to do that and I couldn't set those refs properly.
Can someone help me?
you should use the createRefmethod of each button that you would like to focus, also you have to pass this ref to the focusElement method that you have created:
const myList = [
{ id: 0, label: "label0" },
{ id: 1, label: "label1" },
{ id: 2, label: "label2" },
{ id: 3, label: "label3" },
{ id: 4, label: "label4" },
{ id: 5, label: "label5" }
];
export default class App extends React.Component {
state = {
items: myList,
//This is the list of refs that will help you pick any item that ou want to focus
myButtonsRef: myList.map(i => React.createRef(i.label))
};
// Here you create a ref for each button
renderList = () => {
return this.state.items.map(i => (
<button key={i.id} ref={this.state.myButtonsRef[i.id]}>
{i.label}
</button>
));
};
//Here you pass the ref as an argument and just focus it
focusElement = item => {
item.current.focus();
};
render() {
return (
<div>
{this.renderList()}
<button
onClick={() => {
//Here you are able to focus any item that you want based on the ref in the state
this.focusElement(this.state.myButtonsRef[0]);
}}
>
Focus the item 0
</button>
</div>
);
}
}
Here is a sandbox if you want to play with the code
I'm trying to make a react component that can filter a list based on value chosen from a drop-down box. Since the setState removes all data from the array I can only filter once. How can I filter data and still keep the original state? I want to be able to do more then one search.
Array list:
state = {
tree: [
{
id: '1',
fileType: 'Document',
files: [
{
name: 'test1',
size: '64kb'
},
{
name: 'test2',
size: '94kb'
}
]
}, ..... and so on
I have 2 ways that I'm able to filter the component once with:
filterDoc = (selectedType) => {
//way #1
this.setState({ tree: this.state.tree.filter(item => item.fileType === selectedType) })
//way#2
const myItems = this.state.tree;
const newArray = myItems.filter(item => item.fileType === selectedType)
this.setState({
tree: newArray
})
}
Search component:
class SearchBar extends Component {
change = (e) => {
this.props.filterTree(e.target.value);
}
render() {
return (
<div className="col-sm-12" style={style}>
<input
className="col-sm-8"
type="text"
placeholder="Search..."
style={inputs}
/>
<select
className="col-sm-4"
style={inputs}
onChange={this.change}
>
<option value="All">All</option>
{this.props.docTypes.map((type) =>
<option
value={type.fileType}
key={type.fileType}>{type.fileType}
</option>)}
</select>
</div>
)
}
}
And some images just to get a visual on the problem.
Before filter:
After filter, everything that didn't match was removed from the state:
Do not replace original data
Instead, change what filter is used and do the filtering in the render() function.
In the example below, the original data (called data) is never changed. Only the filter used is changed.
const data = [
{
id: 1,
text: 'one',
},
{
id: 2,
text: 'two',
},
{
id: 3,
text: 'three',
},
]
class Example extends React.Component {
constructor() {
super()
this.state = {
filter: null,
}
}
render() {
const filter = this.state.filter
const dataToShow = filter
? data.filter(d => d.id === filter)
: data
return (
<div>
{dataToShow.map(d => <span key={d.id}> {d.text}, </span>)}
<button
onClick={() =>
this.setState({
filter: 2,
})
}
>
{' '}
Filter{' '}
</button>
</div>
)
}
}
ReactDOM.render(<Example />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<body>
<div id='root' />
</body>
Don't mutate local state to reflect the current state of the filter. That state should reflect the complete available list, which should only change when the list of options changes. Use your filtered array strictly for the view. Something like this should be all you need to change what's presented to the user.
change = (e) => {
return this.state.tree.filter(item => item.fileType === e.target.value)
}
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 created a dynamically changing form, using my own components and I am not sure how can I get the information out of it since the select tag itself exists only in the children components.
I googled and found that I need to use refs, though I am not sure how to do it either. Another option I found is to use
document.getElementsByClassName, but for some reasons, it's not an optimal way to do it, apparently.
the parent class where the form exists
import React, {Component} from 'react';
import ChoicePair from'./ChoicePair';
import ChoosingPane from './ChoosingPane';
class AppComponent extends React.Component {
state = {
numChildren: 3
}
render () {
const children = [];
this.subjects = [ {name: 'a', dep: 1}, {name: 'b', dep: 2}, {name: 'c', dep: 1}, {name: 'd', dep: 1}];
for (var i = 0; i < this.state.numChildren; i ++) {
children.push(<ChoicePair key={i} number={i} options = {this.subjects} subjects = {this.subjects}/>);
};
return (
<div>
<form onSubmit={this.handleSubmit}>
<ChoosingPane addChild={this.onAddChild}>
{children}
</ChoosingPane>
<button type = "submit"> Get my perfect schedule </button>
</form>
{ console.log(document.getElementById("kira") + "WOW")
}
</div>
);
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
handleSubmit(event) {
event.preventDefault();
alert("hi!");
}
}
export default AppComponent;
The mid component
import React from 'react';
const ChoosingPane = props => (
<div>
<div id="children-pane">
{props.children}
</div>
<div className="card calculator">
<p><button onClick={props.addChild}>+</button></p>
</div>
</div>
);
export default ChoosingPane;
the leaf class where the select tag exists (1)
import React, {Component} from 'react';
class ChoicePair extends Component {
constructor(props){
super();
console.log("KKKKKKK");
this.state = {
depValue: '-1',
subValue: '-1',
}
this.handleDepChange = this.handleDepChange.bind(this);
this.handleSubChange = this.handleSubChange.bind(this);
this.getNames = this.getNames.bind(this);
}
handleDepChange(event) {
this.setState({depValue: event.target.value});
}
handleSubChange(event) {
this.setState({subValue: event.target.value});
}
getNames(collection)
{
console.log(collection);
if(collection)
return collection.map( (item, i) => {
return <option id="kira" key = {i} value={i} cat = {item}>{item.name}</option>
});
}
render() {
//create filtered options constant
const subjectList = this.props.subjects.filter((subjName) => subjName.dep === Number(this.state.depValue));
console.log(subjectList + "kill me");
return(
<div>
<select name = "Select department" value={this.state.depValue} onChange = {this.handleDepChange}>
<option value = '-1' disabled>Department</option>
{this.getNames(this.props.options)}
</select>
<select name = "Select subject" value={this.state.subValue} onChange = {this.handleSubChange}>
<option value = '-1' disabled>Subject</option>
{this.getNames(subjectList)}
</select>
</div>
)
}
}
export default ChoicePair;
the leaf class where the select tag exists (2)
Apparently, getElementsByClassNames works, but you can also use references on Dom nodes, or you can look into your architecture and use callback functions.
This is a good link if you are just starting to work with react and need some basic guidance with forms
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>
)}