I thinking for few days but cant realize how can i make it. I have 4 json data and 4 picker.
Its for city,district,village,neirborhood. In first i must choose city then in second picker it must show district about that i choose city. When i choose district from picker third one must show villages about that district. And neirborhood is same too. In that json datas they have some connection. Like city json have ' id-name' district have 'id-cityid-name' village have 'id-districtid-name' neirborhood have 'id-villageid-name' Like that. But i cant figure out how can i make it. Its my codes I really stuck with that hardly i need some help please. Thank you! My codes :
Elements :
const DropdownElements = [
{
key: 1,
title: "Şehir",
placeholder: "Şehir Seçiniz",
apiUrl: "https://api.npoint.io/995de746afde6410e3bd",
type: "city",
selecteditem: "",
data : [],
},
{
key: 2,
title: "İlçe",
placeholder: "İlçe Seçiniz",
apiUrl: "https://api.npoint.io/fc801dbd3fc23c2c1679",
type: "district",
selecteditem: "",
data : [],
},
{
key: 3,
title: "Köy",
placeholder: "Köy Seçiniz",
apiUrl: "https://api.npoint.io/72cf025083b70615b8bb",
type: "village",
selecteditem: "",
data : [],
},
{
key: 4,
title: 'Mahalle',
placeholder:'Mahalle Seçiniz',
apiUrl: 'https://api.npoint.io/0c04c63923c8ca4e117b',
type: 'neighborhood',
selecteditem: "",
data : [],
},
];
Component :
const PickerCompanent = (props) => {
const [xdata, setData] = useState([]);
const [newData, setNewData] = useState([]);
let x;
let y = [];
// data.filter((a) => a.il_id == "3");
useEffect(() => {
props.datasource.then(setData);
switch (props.type) {
case "city":
x = props.selecteditem;
setNewData(xdata);
break;
case "district":
y = xdata.filter((element) => {
if (props.selecteditem === element.id) {
return element;
}
});
break;
case "village":
console.log("village");
break;
default:
console.log("def");
break;
}
}, [props.datasource]);
return (
<Select
showSearch
style={{ width: 200, marginLeft: 15 }}
placeholder={props.placeholder}
optionFilterProp="children"
onChange={(x) => props.onChange(x)}
onFocus={props.onFocus()}
datasource={xdata}
onSearch={props.onSearch()}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{xdata &&
xdata.map((x) => {
return (
<Select.Option key={x.id} value={x.id}>
{x.name}{" "}
</Select.Option>
);
})}
</Select>
);
};
My App :
const App = () => {
const [dataap, setDataAp] = useState([]);
const [idhold, setIDHold] = useState();
const filldata = (value) => {};
function onChange(value) {
setIDHold(value);
console.log(value);
}
const getData = (value, type) => {
return fetch(value)
.then((x) => x.json())
.then((y) => {
return y;
});
};
function onFocus() {}
function onSearch(val) {}
return (
<Space>
{DropdownElements.map((x) => {
return (
<PickerCompanent
showSearch
selecteditem={idhold}
key={x.key}
placeholder={x.placeholder}
type={x.type}
datasource={getData(x.apiUrl)}
onFocus={onFocus}
onChange={(z) => onChange(z)}
onFocus={onFocus}
onSearch={onSearch}
></PickerCompanent>
);
})}
</Space>
);
};
If you need i can give my teamviewer or skype too. I really need that help thanks for replies!
Sandbox : codesandbox.io/s/runtime-monad-vxit
https://codesandbox.io/s/mystifying-moore-7w105?file=/src/App.js
Select CityTwo to see the dropdown update.
You need a switch. Updating arrays inside state is tricky. You can't populate or push anything in an array that's in state. Update your array outside state, THEN update state.
So in my application user have an option to enter a number, for example if user enters as "5" then it will add 5 textInputs. By this, i have successfully looped the textInput. Now, how to populate or store the values in the state ?
My code :
this.state =
{
usersDetails: [{name: "", age: "", gender: "", primary: false}, {name: "", age: "", gender: "", primary: false}]
};
handleChange(i, e) {
const { name, value } = e;
let users = [...this.state.usersDetails];
users[i] = {...users[i], [name]: value};
this.setState({ usersDetails });
console.log(this.state.usersDetails);
}
let items = [];
for (let i = 0; i < this.props.maxSeats; i++) {
items.push(
<TextInput
placeholder="Enter Name"
onChangeText={this.handleChange.bind(this, i)}
value={this.state.userDetails}
/>)
}
With the help of stockoverflow i created the handleChange function but it gives error ! How to populate the values and save on the state for dynamic form !
Kindly guide !
handleChange = (value, index,key) => {
this.setState((prevState) => ({
...prevState,
usersDetails: prevState.usersDetails.map((val, mapIndex) => {
if (mapIndex === index) {
val[key] = value;
return val;
}
return val;
}),
}));
};
for (let i = 0; i < this.props.maxSeats; i++) {
items.push(
<TextInput
key={i}
placeholder="Enter Name"
onChangeText={(text) => {
handleChange(text, i,'name'); // here you can pass name,age whatever
}}
value={this.state.userDetails[i].name}
/>,
);
}
I have a complex data set, so I will show a very simplified version for an example.
Input data:
const data = [
{
type: "input",
caption: "Name",
defaultValue: "John Smith"
},
{
type: "input",
caption: "Name",
defaultValue: "John Smith"
},
{
type: "input",
caption: "Name",
defaultValue: "John Smith"
},
{
type: "input",
caption: "Name",
defaultValue: "John Smith"
},
{
type: "input",
caption: "Name",
defaultValue: "John Smith"
}
];
Each item of the array is removable. It turns out something like this.
There are several conditions. I should not modify the data array so i create a deep copy. As well inside the copy i can only delete elements but don't modify their properties. Thus each element has to have local state with a new value.
Working example:
function App() {
const [mainData, setMainData] = useState(deepCopy(data));
return (
<React.Fragment>
{
mainData.map((item, i) => {
return (
<Input {...item} key={i} num={i} setMainData={setMainData}/>
)
})
}
</React.Fragment>
)
}
const Input = (props) => {
const [value, setValue] = useState(props.defaultValue);
const deleteElem = () => {
props.setMainData((mainData) => {
return [...mainData.filter((_, ind) => ind !== props.num)];
});
};
return (
<div>
<div>
<div>{`${props.caption}:`}</div>
<input value={value} onChange={(e)=>setValue(e.target.value)}/>
</div>
<button onClick={deleteElem}>delete</button>
</div>
)
};
const deepCopy = (aObject) => {
if (!aObject) {
return aObject;
}
let value;
let bObject = Array.isArray(aObject) ? [] : {};
for (const key in aObject) {
value = aObject[key];
bObject[key] = (typeof value === "object") ? deepCopy(value) : value;
}
return bObject;
};
If you try to delete not the last element then (because of the keys) the values of the inputs elements will be mixed up.
What can I do about it?
With deepCopy you can add a unique id to each item when you initialize your state. Once you do that you can leverage that id for passing as key to the Input element
import {uuid} from 'uuidv4';
function deepCopyAndAddId = () => {
let newData = deepCopy(data);
newData = newData.map((item, index) => ({...item, id: uuid()}));
}
function App() {
const [mainData, setMainData] = useState(deepCopyAndAddId);
return (
<React.Fragment>
{
mainData.map((item, i) => {
return (
<Input {...item} key={item.id} num={i} setMainData={setMainData}/>
)
})
}
</React.Fragment>
)
}
To make minimum changes in your code - just never delete the item in deleteElem, but add a flag deleted to it instead.
When render an item, show <Fragment> for the deleted item.
I am working with React and Vanilla JS. Demo
Main Code
import React from "react";
import { render } from "react-dom";
import { makeData } from "./Utils";
import Select from "react-select";
import "react-select/dist/react-select.css";
// Import React Table
import ReactTable from "react-table";
import "react-table/react-table.css";
import jsondata from "./sample";
class App extends React.Component {
constructor() {
super();
this.state = {
// data: makeData(),
data: jsondata,
filtered: [],
select2: null,
select3: null
};
this.uniqueOptions = this.uniqueOptions.bind(this);
}
onFilteredChangeCustom(value, accessor) {
console.log("The value is " + value);
let filtered = this.state.filtered;
console.log("the filtered items" + JSON.stringify(this.state.filtered));
let insertNewFilter = 1;
if (filtered.length) {
console.log("filtered.length " + filtered.length);
filtered.forEach((filter, i) => {
if (filter["id"] === accessor) {
if (value === "" || !value.length) filtered.splice(i, 1);
else filter["value"] = value;
insertNewFilter = 0;
}
});
}
if (insertNewFilter) {
filtered.push({ id: accessor, value: value });
}
this.setState({ filtered: filtered });
console.log("The filtered data is " + JSON.stringify(this.state.filtered));
}
uniqueOptions = (objectsArray, objectKey) => {
var a = objectsArray.map((o, i) => {
return o[objectKey];
});
return a.filter(function(i, index) {
return a.indexOf(i) >= index;
});
};
render() {
const { data } = this.state;
let first_names = null;
let last_names = null;
let first_names_label = null;
let last_names_label = null;
if (this.state.filtered.length == 1) {
if (this.state.filtered[0].id == "firstName") {
first_names_label = "First Name:";
} else {
first_names_label = "Last Name:";
}
first_names = this.state.filtered[0].value.map(name => <p>{name}</p>);
} else if (this.state.filtered.length == 2) {
first_names_label = "First Name:";
first_names = this.state.filtered[0].value.map(name => <p>{name}</p>);
last_names_label = "Last Name:";
last_names = this.state.filtered[1].value.map(name => <p>{name}</p>);
}
return (
<div>
<pre>
{first_names_label}
{first_names}
{last_names_label}
{last_names}
</pre>
<br />
<br />
Extern Select2 :{" "}
<Select
style={{ width: "50%", marginBottom: "20px" }}
onChange={entry => {
this.setState({ select2: entry });
this.onFilteredChangeCustom(
entry.map(o => {
return o.value;
}),
"firstName"
);
}}
value={this.state.select2}
multi={true}
options={this.uniqueOptions(this.state.data, "firstName").map(
(name, i) => {
return { id: i, value: name, label: name };
}
)}
/>
Extern Select3 :{" "}
<Select
style={{ width: "50%", marginBottom: "20px" }}
onChange={entry => {
this.setState({ select3: entry });
this.onFilteredChangeCustom(
entry.map(o => {
return o.value;
}),
"lastName"
);
}}
value={this.state.select3}
multi={true}
options={this.uniqueOptions(this.state.data, "lastName").map(
(name, i) => {
return { id: i, value: name, label: name };
}
)}
/>
<ReactTable
data={data}
filtered={this.state.filtered}
onFilteredChange={(filtered, column, value) => {
this.onFilteredChangeCustom(value, column.id || column.accessor);
}}
defaultFilterMethod={(filter, row, column) => {
const id = filter.pivotId || filter.id;
if (typeof filter.value === "object") {
return row[id] !== undefined
? filter.value.indexOf(row[id]) > -1
: true;
} else {
return row[id] !== undefined
? String(row[id]).indexOf(filter.value) > -1
: true;
}
}}
columns={[
{
Header: "Name",
columns: [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Last Name",
id: "lastName",
accessor: d => d.lastName
}
]
},
{
Header: "Info",
columns: [
{
Header: "Age",
accessor: "age"
}
]
}
]}
defaultPageSize={10}
className="-striped -highlight"
/>
<br />
</div>
);
}
}
render(<App />, document.getElementById("root"));
I have a filtered state variable (this.state.filtered) which has all the filtered items from dropdown.
I want to show the filtered state variable like this
<div>
<div>id: val0,val1 (and a cross button to remove this filter)</div>
<div>id: val0,val1 (and a cross button to remove this filter)</div> .... so on
</div>
I have been able to do this for 2 variables. But in future I will have 10 to 15 dropdowns. That is why I want to display using a loop. How to do this?
The code I have in my demo do not have loop and I want to make it a generic code which can display all key value pairs of this.state.filtered. Without loop I will not be able to use this functionality when I have a huge number of dropdowns. Also the values should be comma separated. I also do not want to make the render function big and dirty. I want to call a function basically which will show the keys and values
So, I have a property (fields), within which I wish to change the value of an element (countries). Alerting the value of countries currently displays the value 2, but I want to change the value to 100, so that re-alerting fields.countries.value, after the change, displays the new value.
How do I do this?
import type { State } from '../../common/types';
import DynamicField from './DynamicField';
import R from 'ramda';
import React from 'react';
import buttonsMessages from '../../common/app/buttonsMessages';
import linksMessages from '../../common/app/linksMessages';
import { FormattedMessage } from 'react-intl';
import { ValidationError } from '../../common/lib/validation';
import { connect } from 'react-redux';
import { fields } from '../../common/lib/redux-fields';
import {
Block,
Box,
Button,
Checkbox,
FieldError,
Flex,
Form,
Heading,
Input,
PageHeader,
Pre,
Radio,
Select,
Space,
Title,
View,
} from '../app/components';
// The example of dynamically loaded editable data.
// cato.org/publications/commentary/key-concepts-libertarianism
const keyConceptsOfLibertarianism = [
'Individualism',
'Individual Rights',
'Spontaneous Order',
'The Rule of Law',
'Limited Government',
'Free Markets',
'The Virtue of Production',
'Natural Harmony of Interests',
'Peace',
].map((concept, index) => ({
id: index,
name: concept,
}));
// Proof of concept. Country list will be read from firebase
const countryArray = [
{ label: 'Select Country', value: 0 },
{ label: 'France', value: 2 },
{ label: 'England', value: 4 },
{ label: 'Swizterland', value: 8 },
{ label: 'Germany', value: 16 },
{ label: 'Lithuania', value: 32 },
{ label: 'Romania', value: 64 },
].map((countryName, index) => ({
id: index,
name: countryName,
}));
// Dynamically create select list
const countryOptions = [];
countryArray.map(countryItem =>
countryOptions.push({ label: countryItem.name.label, value: countryItem.name.value }),
);
// Proof of concept. Country list will be read from firebase
const cityArray = [
{ label: 'Select City', value: 0 },
{ label: 'London', value: 50 },
{ label: 'Paris', value: 75 },
].map((cityName, index) => ({
id: index,
name: cityName,
}));
// Dynamically create select list
const cityOptions = [];
cityArray.map(cityItem =>
cityOptions.push({ label: cityItem.name.label, value: cityItem.name.value }),
);
// Proof of concept. Country list will be read from firebase
const gymArray = [
{ label: 'Select Gym', value: 0 },
{ label: 'Virgin Sport', value: 23 },
{ label: 'Sports Direct', value: 45 },
].map((gymName, index) => ({
id: index,
name: gymName,
}));
// Dynamically create select list
const gymOptions = [];
gymArray.map(gymItem =>
gymOptions.push({ label: gymItem.name.label, value: gymItem.name.value }),
);
type LocalState = {
disabled: boolean,
error: ?Object,
submittedValues: ?Object,
};
class FieldsPage extends React.Component {
static propTypes = {
fields: React.PropTypes.object.isRequired,
dynamicFields: React.PropTypes.object,
// getCities: React.PropTypes.object,
};
state: LocalState = {
disabled: false,
error: null,
submittedValues: null,
};
onFormSubmit = () => {
const { dynamicFields, fields } = this.props;
const values = {
...fields.$values(),
concepts: {
...dynamicFields,
},
};
// This is just a demo. This code belongs to Redux action creator.
// Disable form.
this.setState({ disabled: true });
// Simulate async action.
setTimeout(() => {
this.setState({ disabled: false });
const isValid = values.name.trim();
if (!isValid) {
const error = new ValidationError('required', { prop: 'name' });
this.setState({ error, submittedValues: null });
return;
}
this.setState({ error: null, submittedValues: values });
fields.$reset();
}, 500);
};
handleSelectedCountryChange = () => {
// Pass in the selected country value to get associated cites
const { fields, getCities } = this.props;
getCities('country', fields.$values());
};
/*
handleSelectedCityChange = (event => {
// Pass in the selected city value to get associated gyms
this.setState({secondLevel: event.target.value});
});
*/
render() {
const { fields } = this.props;
const { disabled, error, submittedValues } = this.state;
return (
<View>
<Title message={linksMessages.fields} />
<PageHeader
description="New clients enter their gym details here."
heading="New user entry form."
/>
<Form onSubmit={this.onFormSubmit}>
<Input
{...fields.name}
aria-invalid={ValidationError.isInvalid(error, 'name')}
disabled={disabled}
label="Your Name"
maxLength={100}
type="text"
/>
<FieldError error={error} prop="name" />
<Heading alt>Key Concepts of Libertarianism</Heading>
<Block>
<Flex wrap>
{keyConceptsOfLibertarianism.map(item =>
<Box mr={1} key={item.id}>
<DynamicField
disabled={disabled}
item={item}
path={['fieldsPage', 'dynamic', item]}
/>
</Box>,
)}
</Flex>
</Block>
<Block>
<Checkbox
{...fields.isLibertarian}
checked={fields.isLibertarian.value}
disabled={disabled}
label="I'm libertarian"
/>
<Checkbox
{...fields.isAnarchist}
checked={fields.isAnarchist.value}
disabled={disabled}
label="I'm anarchist"
/>
</Block>
<Block>
<Flex>
<Radio
{...fields.gender}
checked={fields.gender.value === 'male'}
disabled={disabled}
label="Male"
value="male"
/>
<Space x={2} />
<Radio
{...fields.gender}
checked={fields.gender.value === 'female'}
disabled={disabled}
label="Female"
value="female"
/>
<Space x={2} />
<Radio
{...fields.gender}
checked={fields.gender.value === 'other'}
disabled={disabled}
label="Other"
value="other"
/>
</Flex>
</Block>
<Block>
<Select
{...fields.countries}
disabled={disabled}
label="Countries"
onChange={this.handleSelectedCountryChange}
options={countryOptions}
/>
</Block>
<Block>
<Select
{...fields.cities}
disabled={disabled}
label="Cities"
// onChange={this.handleSelectedCityChange}
options={cityOptions}
/>
</Block>
<Block>
<Select
{...fields.gyms}
disabled={disabled}
label="Gyms"
// onChange={this.handleSelectedCityChange}
options={gymOptions}
/>
</Block>
{/*
Why no multiple select? Because users are not familiar with that.
Use checkboxes or custom checkable dynamic fields instead.
*/}
<Button disabled={disabled} type="submit">
<FormattedMessage {...buttonsMessages.submit} />
</Button>
{submittedValues &&
<Pre>
{JSON.stringify(submittedValues, null, 2)}
</Pre>
}
</Form>
</View>
);
}
}
FieldsPage = fields({
path: 'fieldsPage',
fields: [
'countries',
'cities',
'gyms',
'gender',
'isAnarchist',
'isLibertarian',
'name',
],
getInitialState: () => ({
countries: '0',
cities: '0',
gyms: '0',
gender: 'male',
isAnarchist: false,
isLibertarian: false,
}),
})(FieldsPage);
export default connect(
(state: State) => ({
dynamicFields: R.path(['fieldsPage', 'dynamic'], state.fields),
}),
)(FieldsPage);
=====================================================================
fields.js
/* #flow weak */
import R from 'ramda';
import React from 'react';
import invariant from 'invariant';
import { resetFields, setField } from './actions';
type Path = string | Array<string> | (props: Object) => Array<string>;
type Options = {
path: Path,
fields: Array<string>,
getInitialState?: (props: Object) => Object,
};
const isReactNative =
typeof navigator === 'object' &&
navigator.product === 'ReactNative'; // eslint-disable-line no-undef
// Higher order component for huge fast dynamic deeply nested universal forms.
const fields = (options: Options) => (WrappedComponent) => {
const {
path = '',
fields = [],
getInitialState,
} = options;
invariant(Array.isArray(fields), 'Fields must be an array.');
invariant(
(typeof path === 'string') ||
(typeof path === 'function') ||
Array.isArray(path)
, 'Path must be a string, function, or an array.');
return class Fields extends React.Component {
static contextTypes = {
store: React.PropTypes.object, // Redux store.
};
static getNormalizePath(props) {
switch (typeof path) {
case 'function': return path(props);
case 'string': return [path];
default: return path;
}
}
static getFieldValue(field, model, initialState) {
if (model && {}.hasOwnProperty.call(model, field)) {
return model[field];
}
if (initialState && {}.hasOwnProperty.call(initialState, field)) {
return initialState[field];
}
return '';
}
static lazyJsonValuesOf(model, props) {
const initialState = getInitialState && getInitialState(props);
// http://www.devthought.com/2012/01/18/an-object-is-not-a-hash
return options.fields.reduce((fields, field) => ({
...fields,
[field]: Fields.getFieldValue(field, model, initialState),
}), Object.create(null));
}
static createFieldObject(field, onChange) {
return isReactNative ? {
onChangeText: (text) => {
onChange(field, text);
},
} : {
name: field,
onChange: (event) => {
// Some custom components like react-select pass the target directly.
const target = event.target || event;
const { type, checked, value } = target;
const isCheckbox = type && type.toLowerCase() === 'checkbox';
onChange(field, isCheckbox ? checked : value);
},
};
}
state = {
model: null,
};
fields: Object;
values: any;
unsubscribe: () => void;
onFieldChange = (field, value) => {
const normalizedPath = Fields.getNormalizePath(this.props).concat(field);
this.context.store.dispatch(setField(normalizedPath, value));
};
createFields() {
const formFields = options.fields.reduce((fields, field) => ({
...fields,
[field]: Fields.createFieldObject(field, this.onFieldChange),
}), {});
this.fields = {
...formFields,
$values: () => this.values,
$setValue: (field, value) => this.onFieldChange(field, value),
$reset: () => {
const normalizedPath = Fields.getNormalizePath(this.props);
this.context.store.dispatch(resetFields(normalizedPath));
},
};
}
getModelFromState() {
const normalizedPath = Fields.getNormalizePath(this.props);
return R.path(normalizedPath, this.context.store.getState().fields);
}
setModel(model) {
this.values = Fields.lazyJsonValuesOf(model, this.props);
options.fields.forEach((field) => {
this.fields[field].value = this.values[field];
});
this.fields = { ...this.fields }; // Ensure rerender for pure components.
this.setState({ model });
}
componentWillMount() {
this.createFields();
this.setModel(this.getModelFromState());
}
componentDidMount() {
const { store } = this.context;
this.unsubscribe = store.subscribe(() => {
const newModel = this.getModelFromState();
if (newModel === this.state.model) return;
this.setModel(newModel);
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<WrappedComponent {...this.props} fields={this.fields} />
);
}
};
};
export default fields;