How to set props value as selected checked in dropdown - javascript

I am creating a dropdown edit form where on edit user will able to see the option value as selected from props on edit. I tried with defaultValue and using selected but it seems my conditions are wrong.
Any suggestions which I can utilized to check the props value in option and display as selected on dropdown when user click edit.
Here selectedPropsName is what we are getting from props and I want to map and display this as selected.
Thanks...
Code
const results = [
{
'Name': 'apple',
'description': 'apple is good for health'
},
{
'Name': 'mango',
'description': 'mango is very juicy'
}
]
const props = {
'Name': 'apple'
}
const CreateForm=()=> {
const [spaceName, setSpaceName] = React.useState('');
const [, setSpaceDescriptionName] = React.useState('');
const handleChangeSpaceName = (event: React.ChangeEvent<HTMLInputElement>) => {
setSpaceName(event.target.value);
setSpaceDescriptionName(event.target.value)
};
const selectedPropsName = props.Name
return (
<>
<div className='create-dropdown'>
<TextField
className='create-textfield'
id='create-textfield'
select={true}
label='create Name'
value={spaceName}
onChange={handleChangeSpaceName}
SelectProps={{
native: true,
}}
variant='standard'
>
{results && results.length > 0 && results.map((optionNames:any) => (
<option key={optionNames.Name}
className='textfield-option'
value={`${optionNames.Name}, ${optionNames.description}`}>
{optionNames.Name}
</option>
))}
</TextField>
</div>
</>
)
}

If I understand you correctly, you should be able to check if optionNames.Name equals selectedPropsName and then set the option's selected attribute to true, conditionally.
So,
{results && results.length > 0 && results.map((optionNames:any) => (
<option key={optionNames.Name}
className='textfield-option'
value={`${optionNames.Name}, ${optionNames.description}`}>
{optionNames.Name}
</option>
))}
becomes
{results && results.length > 0 && results.map((optionNames:any) => (
<option key={optionNames.Name}
selected={optionNames.Name === selectedPropsName ? true : false}
className='textfield-option'
value={`${optionNames.Name}, ${optionNames.description}`}>
{optionNames.Name}
</option>
))}
EDIT:
My mistake. I've taken another look. It looks like you are using Material-UI and so, the select component is controlled. You would have to set the initial value through the spaceName hook. And you would have to change the props.Name variable to include a "name, description" since that is what your option value={} field is expecting. So, these changes should make it work:
const props = {
Name: "mango, mango is very juicy",
};
const [spaceName, setSpaceName] = React.useState(props.Name);

Related

How can I do dynamic badge on React CoreUI?

I have a problem with a badge on Core UI. I have a Sidebar and one of the elements is Chat. When a new message comes, the badge must show new message. But the old developer have written different ways and I can not change it. I cannot find anything for this.
My codes
Sidebar elements
const _nav = [
{
_tag: "CSidebarNavItem",
name: "Chat",
to: "/chat",
filter: "feedbacks",
icon: "cil-speech",
badge: {
color: "info",
text: "NEW MESSAGE",
},
},
]
My React component
import navigation from "./_nav";
const [filteredNav, setFilteredNav] = useState([]);
const [chatBadge, setChatBadge] = useState(false);
const handleChatBadge = () => {
setChatBadge(true)
}
// it is a sidebar element for user-role
useLayoutEffect(() => {
allwedMenu().then((res) => {
const arr = [navigation[0]];
res.data.items.forEach((element) => {
arr.push(element.name);
});
setFilteredNav(navigation.filter((item) => arr.includes(item.filter)));
});
}, []);
<CSidebarNav>
<CCreateElement
items={filteredNav}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle,
}}
/>
</CSidebarNav>
I need the badge to show when chatBadge is true. But I don't know how can I write this.
You can only add a condition to show the badge when chatBadge is true.
Based on the Value of ChatBadge, you can use the property of the Component CSideBarNavItem to display the badge and pass the colour and text properties.
Here's the updated code:
<CSidebarNav>
<CCreateElement
items={filteredNav}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle,
}}
/>
{filteredNav.map((item, index) => (
<CSidebarNavItem
key={index}
name={item.name}
to={item.to}
icon={item.icon}
badge={
item.name === "Chat" && chatBadge
? { color: "info", text: "NEW MESSAGE" }
: null
}
/>
))}
</CSidebarNav>
Hope it helps.

React MUI Multiple Select with Checkboxes not Check marking Initial Selected Options

I am still new in React and MUI, so please spare my life. I am learning to build Multiple Select Options with Checkboxes and I have populated the Dropdown Options from an Array.
I also have set up the initial/default state options. The initial/default state options (Nintendo & XBoX) are showing up in the main Input Select Field but unfortunately not being checked marked in the Dropdown Checkboxes, such as in the screenshot below.
Screenshot 1
What I'm trying to achieve is to be like this (all the selected options (Nintendo & XBoX) from the default state is showing on the checkboxes at initial page load):
Screenshot 2
This is my Live Demo coding in https://stackblitz.com/edit/react-rahqrq?file=demo.js
I'm confused, I can tick all the other option checkboxes in the dropdown except the default Initial Options from the State. It's unclickable and unchangeable, I have no idea why. Any tips and clues are greatly appreciated, Thanks in advance, please pardon my poor Grammar and Best Regards.
import * as React from 'react';
import OutlinedInput from '#mui/material/OutlinedInput';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import ListItemText from '#mui/material/ListItemText';
import Select from '#mui/material/Select';
import Checkbox from '#mui/material/Checkbox';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
const variants = [
{
id: 3,
name: 'Voucher',
},
{
id: 1,
name: 'Top Up',
},
{
id: 2,
name: 'Game Key',
},
{
id: 12,
name: 'Other',
},
{
id: 11,
name: 'Nintendo',
},
{
id: 10,
name: 'Xbox',
},
];
export default function MultipleSelectCheckmarks() {
const [variantName, setVariantName] = React.useState([{
id: 11,
name: 'Nintendo',
},{
id: 10,
name: 'Xbox'
},]);
const handleChange = (event) => {
const {
target: { value },
} = event;
const preventDuplicate = value.filter((v, i, a) => a.findIndex((t) => t.id === v.id) === i);
setVariantName(
// On autofill we get a the stringified value.
typeof preventDuplicate === 'string' ? preventDuplicate.split(',') : preventDuplicate
);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-multiple-checkbox-label"
id="demo-multiple-checkbox"
multiple
value={variantName}
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.map((x) => x.name).join(', ')}
MenuProps={MenuProps}
>
{variants.map((variant) => (
<MenuItem key={variant.id} value={variant}>
<Checkbox checked={variantName.indexOf(variant) > -1} />
<ListItemText primary={variant.name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
You need to findIndex of each item in the initial list to make them checked.
Try like below
{
variants.map(variant => (
<MenuItem key={variant.id} value={variant}>
<Checkbox
checked={
variantName.findIndex(item => item.id === variant.id) >= 0
}
/>
<ListItemText primary={variant.name} />
</MenuItem>
));
}
Code sandbox => https://stackblitz.com/edit/react-rahqrq-sd2m2b?file=demo.js

How to update an array by index using the useState hook?

I have a select component that I would like to use to update the values of an array of objects based on the index.
I use the hook like this:
const [areas, setAreas] = useState(product.areas);
This returns the following for "areas":
[
0: {de: "Getraenke", en: "Drinks"},
1: {de: "Snacks", en: "Snacks"}
]
My select component looks like this:
{areas?.map((area: {de: string, en: string}, index: number) => (
<Wrapper key={index}>
<Select
label="Area"
name="product-area"
value={area[lang] || ''}
onChange={e => setArea([...area, { [lang]: e.target.value }])}
>
{areaOptions.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</Select>
</InputWrapper>
))}
In this way I unfortunately get the following for "area" after selecting an option (here "Sweets"):
[
0: {de: "Getraenke", en: "Drinks"},
1: {de: "Snacks", en: "Snacks"}
2: {en: "Sweets" }
]
So the first object of the array is not updated as intended, but a further object is appended, which unfortunately also lacks the "de" language.
My goal is to update the first object (or the object based on the index of the select component) of the array so that instead of:
0: {de: "Getraenke", en: "Drinks"}
..the updated object looks like this:
0: {de: "Suessigkeiten", en: "Sweets"}
The objects within the array should therefore be updated based on the index of the current select component and also take into account the selected language (default "en").
The easiest way to do this is to clone the array, update the specific array item by index and then replace the old array with it using useState, like this.
const updateArea = (e, lang, index) => {
const updatedAreas = [...areas];
updatedArea[index][lang] = e.target.value;
setAreas(updatedAreas);
}
...
{areas?.map((area: {de: string, en: string}, index: number) => (
<Wrapper key={index}>
<Select
label="Area"
name="product-area"
value={area[lang] || ''}
onChange={e => updateArea(e, lang, index)}
>
{areaOptions.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</Select>
</InputWrapper>
))}
Let's solve this step by step
1. A further object is appended
That's because you're telling it to do so :)
onChange={e => setArea([...area, { [lang]: e.target.value }])}
This means, copy the entire array ...area and add a new object at the end { [lang]: e.target.value }
Another mistake you're making is not using a callback function, proper way to access current ...area should be this:
onChange={e => setArea((currentArea) => [...currentArea, { [lang]: e.target.value }]}
This way you always have the most up-to-date version of area.
Now, to update the specific object by the index, you would do the following:
onChange={e => setArea((currentArea) => {
const { value } = e.target;
const newArea = currentArea.map((singleArea, areaIndex) => {
if (areaIndex === index) {
return { [lang]: value }
}
return singleArea;
});
return newArea;
}}
2. Lacks the "de" language
Again, you're explicitly only adding one language to it :)
{ [lang]: value } // { en: 'something' }
Not sure how to fix this one for you, but at least you can understand why its wrong, It should be something like (just a concept, this won't work):
{ [langDe]: value, [langEn]: value } // { de: 'Guten Tag', en: 'something' }

Update Select Option list based on other Select field selection ant design

<Form
layout="vertical"
size="medium"
className="test-form"
requiredMark={false}
onFinish={onFinish}
>
<Form.Item
name="companyId"
label="Company/Customer"
rules={[{ required: true, message: "Please select Company!"}]}
>
<Select
onChange={this.handleSelectCompanyOnchange}
style={{ width: "50%" }}
name="companyId"
>
{users.map((user, index) => {
return (
<Option key={index} value={user.companyID}>
{user.companyName}
</Option>
);
})}
</Select>
</Form.Item>
<Form.Item
label="Products"
name="products"
rules={[{ required: true, message: "Please select Products!"}]}
>
<Select mode="multiple" allowClear style={{ width: "70%" }}>
{products.map((product, index) => {
if (this.state.companyId == product.companyId) {
return (
<Option key={index} value={product.id}>
{product.productName}
</Option>
);
}
})}
</Select>
</Form.Item>
</Form>
I am trying to achieve Options in Products Select element changes according to the Company Select onChange selection.
I have specified onChange in Select and calling this.handleSelectCompanyOnchange. In which I get selected companyId.
In this.state.companyId I had set companyId manually which I will remove.
I am really new to ant design and not able to figure out how to update the Products list once Company is selected.
Here, users and products are json as below.
users:
[{
companyID: 2
companyName: "TEST1"
},{
companyID: 7
companyName: "TEST2"
}]
products:
[{
companyId: 2
id: 1
productName: "TESTProduct1"
},{
companyId: 7
productName: "TESTProduct2"
id: 2
},{
companyId: 7
id: 3
productName: "TESTProduct3"
},{
companyId: 7
id: 4
productName: "TESTProduct4"
}]
However, I have tried getValueFromEvent but not able to achieve this. I am using Ant design Form and Select for this. Also I did referred to https://github.com/ant-design/ant-design/issues/4862 and how to get field value on change for FormItem in antd
Here is what you need to achieve it.
Use onValuesChange prop of the Form. This is the best place to perform setState when it comes to antd Form field changes, not on Select or Input onChange.
<Form onValuesChange={handleFormValuesChange}>
...
</Form>
A form instance (hook). This is optional in your case, but this is useful on setting and getting form values. See more here about it.
const [form] = Form.useForm();
<Form form={form} onValuesChange={handleFormValuesChange}>
...
</Form>
This is the product options render looks like, a combination of map and filter where selectedCompanyId comes from state. Take note that don't use index as key if the fixed length of the list is unknown, the react will confuse on this and you will get some logical error. Use some unique id.
<Form.Item label="Products" name="product">
<Select>
{products
.filter((product) => product.companyId === selectedCompanyId)
.map((product) => (
<Option key={product.id} value={product.id}>
{product.productName}
</Option>
))}
</Select>
</Form.Item>
And here is the handleFormValuesChange
const handleFormValuesChange = (changedValues) => {
const formFieldName = Object.keys(changedValues)[0];
if (formFieldName === "company") {
setSelectedCompanyId(changedValues[formFieldName]); // perform setState here
form.setFieldsValue({product: undefined}) //reset product selection
}
};
Here is the complete working code in react hooks:
the idea here is that you only need to watch the value change in the state.
For example, your select should "watch" a value of state and then you can easily update the state via its own setState method of React. Inside of an onChange event, you can simply just do something with our state.
<Select
value={state.productId}
onChange={e => {// Do something}}
>
{...}
<Select/>
Below is my example code how did I update the day every time I reselect week selection. Hopefully that this can help you.
import { Divider, Select } from 'antd';
import React, { useState } from 'react';
export function Example(): JSX.Element {
const [state, setState] = useState<{week: number, day: number}>({
day: 1,
week: 1
});
const weeks = [1,2,3,4];
const days = [1,2,3,4,5];
return <>
<Select
value={state.week}
onChange={(value) => setState({ week: value, day: 1})}
>
{
weeks.map(week => {
return <Select.Option
key={week}
value={week}
>
{week}
</Select.Option>;
})
}
</Select>
<Divider/>
<Select
value={state.day}
onChange={(value) => setState({...state, day: value})}
>
{
days.map(day => {
return <Select.Option
key={day}
value={day}
>
{day}
</Select.Option>;
})
}
</Select>
</>;
}
Lets say you want to update your option list based on data you get from backend;
note:categories is an array object. From this array you take out label and value for each option, see below:
const newOptions = categories.map((item, index) => {
return {
label: item.name,
value: item._id,
};
});
Then use newOptions inside your form like that:
<Form.Item label="anyName" name="anyName">
<Select style={{ width: 220 }} onChange={ handleChange } options={ newOptions } />
</Form.Item>

React Semantic UI - add key to options in Dropdown menu

I have this Dropdown menu instance:
<Dropdown
selection
options={this.state.options}
search
value={value}
onChange={this.handleChange}
onSearchChange={this.handleSearchChange}
/>
and when my backend returns response, which is then set as state and it is structured like this:
"options": [
{
"text": "New York,All Airports (NYC) , USA",
"value": "NYC"
},
{
"text": "New York,Newark Liberty Intl (EWR), USA",
"value": "EWR"
},
{
"text": "New York,John F Kennedy (JFK), USA",
"value": "JFK"
},
{
"text": "New York,La Guardia (LGA), USA",
"value": "LGA"
}
]
...I get this warning:
Warning: flattenChildren(...): Encountered two children with the same
key, 1:$BLZ. Child keys must be unique; when two children share a
key, only the first child will be used.
in select (created by Dropdown)
in div (created by Dropdown)
in Dropdown (created by SearchForm)
How do I add keys to these elements to prevent this warning?
So looking at the code for the Semantic UI source for the dropdown component, the render options function converts your passed in options into a array of DropdownItem components:
renderOptions = () => {
const { multiple, search, noResultsMessage } = this.props
const { selectedIndex, value } = this.state
const options = this.getMenuOptions()
if (noResultsMessage !== null && search && _.isEmpty(options)) {
return <div className='message'>{noResultsMessage}</div>
}
const isActive = multiple
? optValue => _.includes(value, optValue)
: optValue => optValue === value
return _.map(options, (opt, i) => (
<DropdownItem
key={`${opt.value}-${i}`}
active={isActive(opt.value)}
onClick={this.handleItemClick}
selected={selectedIndex === i}
{...opt}
// Needed for handling click events on disabled items
style={{ ...opt.style, pointerEvents: 'all' }}
/>
))
}
the key for this array is set by taking the value prop and appending the index to it:
key={`${opt.value}-${i}`}
which should always be unique since the index is used but there is another part of the code for hidden inputs
renderHiddenInput = () => {
debug('renderHiddenInput()')
const { value } = this.state
const { multiple, name, options, selection } = this.props
debug(`name: ${name}`)
debug(`selection: ${selection}`)
debug(`value: ${value}`)
if (!selection) return null
// a dropdown without an active item will have an empty string value
return (
<select type='hidden' aria-hidden='true' name={name} value={value} multiple={multiple}>
<option value='' />
{_.map(options, option => (
<option key={option.value} value={option.value}>{option.text}</option>
))}
</select>
)
}
in this one the key is set to only the value, not the value plus index.
<option key={option.value} value={option.value}>{option.text}</option>
this might be your problem, if you have duplicate values then the key will not be unique. Double check the options list to make sure you don't have duplicate values.

Categories

Resources