So I am using the Antd Cascader component (for the first time, having used Antd Select for most other things for the past few months). However, it isn't quite working yet. I have the options basically like this (but with a lot more options, only 3 levels deep tho like this):
const options = [
{
label: 'Base 1',
value: 'base_1',
children: [
{
label: 'Middle a',
value: 'middle_a',
children: [
{
label: 'Foo',
value: 'd57b2b75-4afa-4a16-8991-fc736bce8cd5',
},
{
label: 'Bar',
value: 'd12b2b75-4afa-4a16-8991-fc736bce8cec',
}
]
},
{
label: 'Middle b',
value: 'middle_b',
children: [
{
label: 'Baz',
value: 'd32b2b75-4afa-4a16-8991-fc736bce8cdd',
},
{
label: 'Quux',
value: 'dabb2b75-4afa-4a16-8991-fc736bce8ced',
}
]
}
]
},
{
label: 'Base 2',
value: 'base_2',
children: [
{
label: 'Middle a',
value: 'middle_a',
children: [
{
label: 'Helo',
value: 'd32bce75-4afa-4a16-8991-fc736bce8cdd',
},
{
label: 'World',
value: 'dabbac75-4afa-4a16-8991-fc736bce8ced',
}
]
}
]
}
]
I configure the component like this:
<Cascader
options={options}
getPopupContainer={trigger => trigger.parentNode}
multiple={multiple}
displayRender={label => {
return label.join(' / ');
}}
value={defaultValue}
// eslint-disable-next-line #typescript-eslint/no-explicit-any
onChange={(val: any, options: any) => {
if (multiple) {
const ids = serializeCascaderOptionValues(options);
onChange?.(ids, options);
} else {
onChange?.(val[2] ? String(val[2]) : undefined, options);
}
}}
showSearch={{
filter: (input: string, path) => {
return path.some(
option =>
String(option?.label).toLowerCase().indexOf(input.toLowerCase()) >
-1
);
},
}}
/>
Where, here, value or defaultValue I am passing in an array like ['d32bce75-4afa-4a16-8991-fc736bce8cdd'], which maps to Base 2 / Middle a / Helo. When I select the value Base 2 / Middle a / Helo from the UI, it shows correctly in the input like that. But when I save it and refresh the page (persisting to the backend, and pass in value={value} like value={['d32bce75-4afa-4a16-8991-fc736bce8cdd']}, it shows the ID hash in the input instead of the nice string Base 2 / Middle a / Helo. When I log displayRender={label...}, the label is showing the [ID-hash], rather than an array of like ['Base 2', 'Middle a', 'Helo']. What am I doing wrong, how do I get it to show properly?
Related
I am trying to use a navigation component from Antd (https://ant.design/components/menu), but it's giving me this error:
Parameter 'e' implicitly has an 'any' type.
I have tried doing e: React.MouseEvent<HTMLElement> but then the error shifts to e.key: Property 'key' does not exist on type 'MouseEvent<HTMLInputElement, MouseEvent>'.
Following is my App.tsx:
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '#ant-design/icons';
import type { MenuProps } from 'antd';
import { Menu } from 'antd';
const items: MenuProps['items'] = [
{
label: 'Navigation One',
key: 'mail',
icon: <MailOutlined />,
},
{
label: 'Navigation Two',
key: 'app',
icon: <AppstoreOutlined />,
disabled: true,
},
{
label: 'Navigation Three - Submenu',
key: 'SubMenu',
icon: <SettingOutlined />,
children: [
{
type: 'group',
label: 'Item 1',
children: [
{
label: 'Option 1',
key: 'setting:1',
},
{
label: 'Option 2',
key: 'setting:2',
},
],
},
{
type: 'group',
label: 'Item 2',
children: [
{
label: 'Option 3',
key: 'setting:3',
},
{
label: 'Option 4',
key: 'setting:4',
},
],
},
],
},
{
label: (
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Navigation Four - Link
</a>
),
key: 'alipay',
},
];
const App: React.FC = () => {
const [current, setCurrent] = useState('mail');
const onClick: MenuProps['onClick'] = (e) => {
console.log('click ', e);
setCurrent(e.key);
};
return <Menu onClick={(onClick)} selectedKeys={[current]} mode="horizontal" items={items} />;
};
export default App;
I am new to this and have already spent many hours trying to fix this, any help is highly appreciated! Thank you!
The correct type of e is the MenuInfo interface defined in the react-component library (rc-menu). Unfortunately, this type isn't exported. As a workaround, you can use the following code to indirectly reference the MenuInfo interface:
const onClick: MenuProps['onClick'] = (e: Parameters<MenuProps['onClick']>[0]) => {
The Parameters utility type returns an array type containing the parameter types of the specified function, and the [0] indexer returns the type of the first (and only) parameter.
I'm trying to implement a categories input with this data returned from my DB
[
{
_id: '63e59f91bd2a21368188ff4b',
title: 'Uncategorized',
slug: 'uncategorized',
categoryType: 'blog',
createdAt: '2023-02-10T01:36:17.704Z',
updatedAt: '2023-02-10T01:36:17.704Z',
},
{
_id: '63e5984028745af5bad2c015',
parentCategory: {
_id: '63e5974a786719dd4bb2d37b',
title: 'Projects',
},
title: 'YTDownloader',
slug: 'ytdownloader',
categoryType: 'blog',
createdAt: '2023-02-10T01:05:04.919Z',
updatedAt: '2023-02-10T01:05:04.919Z',
},
{
_id: '63e597c3786719dd4bb2d387',
parentCategory: {
_id: '63e5974a786719dd4bb2d37b',
title: 'Projects',
},
title: 'Song Finder',
slug: 'song-finder',
categoryType: 'blog',
createdAt: '2023-02-10T01:02:59.742Z',
updatedAt: '2023-02-10T01:02:59.742Z',
},
]
What I'm trying is to create the example given in the documentation since my categories are pretty much 'parents' or 'childrens' and don't want to have them unorganized.
So far this is what I've been trying but to not success:
<Select
placeholder="Select category"
defaultValue={category}
onChange={(e) => {
setObjectData({
...objectData,
category: e,
})
}}
value={category}
options={[
categories.map((c, i) => [
{
label: c.parentCategory ? c.parentCategory.title : c.title,
},
]),
]}
/>
This returns literally nothing, not even an error. What I was expecting is the following:
<Select
defaultValue={category}
onChange={(e) => {
setObjectData({
...objectData,
category: e,
})
}}
value={category}
options={[
{
label: 'Projects',
options: [
{
label: 'YTDownloader',
value: '63e5984028745af5bad2c015',
},
{
label: 'Song Finder',
value: '63e597c3786719dd4bb2d387',
},
],
},
{
label: 'Uncategorized',
value: '63e59f91bd2a21368188ff4b'
],
},
]}
/>
Has anyone done something like this before? It will be great if you guys can help me solve this little issue that's been giving a headache for the last 2 hours, LOL
My guess would be you have only passed objects to options that only have a label and no value. This is speculation, but it's possible nothing is rendered in the dropdown if there is no corresponding value to each label.
The label, which is what the user sees for each option, isn't necessarily its underlying value.
Keep in mind the value is ultimately the thing that will be passed to onChange, so to only have a label with no value could explain why it just does nothing -- because a valid option must have a value.
Map each option such that its value represents that option uniquely:
categories.map((c, i) => [
{
label: c.parentCategory ? c.parentCategory.title : c.title,
value: c._Id
},
]),
I'm sure if you're using ant design menu component you've got the same error:
Warning: [antd: Menu] children will be removed in next major version. Please use items instead.
the new way to make this is:
const items = [
{ label: 'item 1', key: 'item-1' },
{ label: 'item 2', key: 'item-2' },
{
label: 'sub menu',
key: 'submenu',
children: [{ label: 'item 3', key: 'submenu-item-1' }],
},
];
return <Menu items={items} />
and you can define your function and use it like this:
return <Menu onClick={onClick} selectedKeys={[current]} mode="horizontal" items={items} />;
how can i have different functions on my items?
i'll be glad if someone help me with this
in the old way i could easily define any functions i want on any items
Actually, we can add functions to the items array as well
const [current, setCurrent] = useState();
const func = () => {
setCurrent(e.key);
}
const func2 = () => {
setCurrent(e.key);
}
const items = [
{
label: 'item 1',
key: 'item-1'
onClick: func,
className: class1,
},
{
label: 'item 1',
key: 'item-1'
onClick: func2,
className: class2,
},,
{
label: 'sub menu',
key: 'submenu',
children: [{ label: 'item 3', key: 'submenu-item-1' }],
},
];
return <Menu selectedKeys={[current]} mode="horizontal" items={items} />;
I have two dropdown menus using React-Select:
<Select
className="type-select"
options={type_options}
onChange={filterType}
/>
<label id='filter-label'>Filter By Generation:</label>
<Select
className="gen-select"
options={generation_options}
onChange={filterGen}
/>
and the options for the two of these are as follows:
{ value: 'none', label: 'None'},
{ value: 'normal', label: 'Normal' },
{ value: 'fire', label: 'Fire' },
{ value: 'water', label: 'Water' },
{ value: 'grass', label: 'Grass' },
{ value: 'electric', label: 'Electric' },
{ value: 'ice', label: 'Ice' },
{ value: 'fighting', label: 'Fighting' },
{ value: 'poison', label: 'Poison' },
{ value: 'ground', label: 'Ground' },
{ value: 'flying', label: 'Flying' },
{ value: 'psychic', label: 'Psychic' },
{ value: 'bug', label: 'Bug' },
{ value: 'rock', label: 'Rock' },
{ value: 'ghost', label: 'Ghost' },
{ value: 'dark', label: 'Dark' },
{ value: 'dragon', label: 'Dragon' },
{ value: 'steel', label: 'Steel' },
{ value: 'fairy', label: 'Fairy' },
];
const generation_options = [
{ value: 'none', label: 'None'},
{ value: 'i', label: 'I'},
{ value: 'ii', label: 'II'},
{ value: 'iii', label: 'III'},
{ value: 'iv', label: 'IV'},
{ value: 'v', label: 'V'},
{ value: 'vi', label: 'VI'},
{ value: 'vii', label: 'VII'},
{ value: 'viii', label: 'VIII'},
]
Both of these dropdowns are stored in state using React Hooks, and in the onChange methods for both, the value for the given Select is updated and the other is reset to "none". Ex: If my first select is set to "Fire" then when I change my second select to Gen IV, the first select sets back to "none". Functionally, I already have this working and am able to use the React-Selects as I want, but the issue I'm having is that when updating one select, the other select won't change to show that it's been changed. I've tried adding value={type} where type is the state the first select corresponds to, but that just causes the React-Select component to never show the selected value. Does anyone know how to get these React-Selects to operate in the way I'm describing? I can elaborate on parts of this more if more explanation is needed.
Thanks!
Edit: Here are the filterType and filterGen methods:
function filterType(selectedOption) {
setType(selectedOption.value)
setGen("none")
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`https://pokeapi.co/api/v2/type/${selectedOption.value}`)
}
}
function filterGen(selectedOption) {
setGen(selectedOption.value)
setType("none")
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`generation-${selectedOption.value}`)
}
}
Figured it out. The issue was I was setting the state to just the value of the selected item, instead of the whole object (ie: {value: 'fire', label: 'Fire}. Fixed pieces of code below, with the changes in bold:
function filterType(selectedOption) {
**setType(selectedOption)**
**setGen({ value: 'none', label: 'None'})**
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`https://pokeapi.co/api/v2/type/${selectedOption.value}`)
}
}
function filterGen(selectedOption) {
**setGen(selectedOption)**
**setType({ value: 'none', label: 'None'})**
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`generation-${selectedOption.value}`)
}
}
<div className="select-container">
<label id='filter-label'>Filter By Type:</label>
<Select
className="type-select"
options={type_options}
onChange={filterType}
**value={type}**
/>
<label id='filter-label'>Filter By Generation:</label>
<Select
className="gen-select"
options={generation_options}
onChange={filterGen}
**value={gen}**
/>
</div>
I have Antd's Cascader like this:
When I click on Edit button it open modal as below.
const showModal = (record) => {
console.log('record', record); // this is current record which has existing value to be displayed in cascader
form.setFieldsValue(record);
setVisible(true);
};
how do I feed initial values or existing values to Cascader?
<Cascader
defaultValue={['zhejiang', 'hangzhou', 'xihu']}
options={propertyOptions}
loadData={loadCompanies}
onChange={onChange}
changeOnSelect
/>
using defaultValue prop like does not seem to work.
When I load propertyOptions, it loads with values like
property1,
property2
When I click on property1 it loads children dynamically.
so selected value is e.g. property1 > company1
but When I click on Edit modal it has only parent values. (children won't be there as I load them on parent click).
When I click on modal it has the whole item data so maybe I can use it to show like hardcoded value or something?
UPDATE:
This is how I fill propertyOptions
function setPropertiesAsOptions(propertiesQuerySnapshot) {
propertiesQuerySnapshot.forEach((doc) => {
options.push({
value: doc.id,
label: doc.data().propertyName,
isLeaf: false,
});
});
setPropertyOptions(options);
}
Is there propertyOptions has value names like zhejiang or other?
In your case this data should work properly as options
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
So it's mean that value name should be the same as params in defaultValue