在使用antd
的select
组件时,已经不能满足我们对下拉菜单的显示需求,需要基于antd
进行扩展。
比如我想要实现下图的效果:
我一个label
就可能包含很多内容,那么对就会对label进行扩展。一般select
的数据会通过ajax
请求去后台请求,当数据请求回来后,需要将这些数据组装成上面的形式,就会需要对label
进行特殊处理。查看官方文档可以得知label可以是个字符串也可以是个React.Element
,那么只要是返回一个根节点的DOM树就可以了
所以,这里会对label
进行特殊处理。
this.props.ajax.get('/xxxx/list').then(res => {
const labelgroups = map(res.data.list, obj => {
let data = {
}
data.label = <p><p>{
obj.displayname}</p><p style={
{
margin:"0 2px"}}>
{
obj.grouptags_detail && map(obj.grouptags_detail, o => {
const type = o.tagtype === "application" ? "A" : (o.tagtype === "location" ? "L" : "E")
return <span style={
{
display:"inline-block",margin:"0 5px"}}><RibbonTags color={
o.tagtype} displayname={
o.displayname} tagtype={
type}/></span>
})
}
</p></p>
data.value = obj.name
return data
})
this.setState({
labelgroups })
})
处理之后,在select
就会显示你想要的结果。
但同时会出现一个问题,对于不同的select你可以有不同的需求,那么当选中后,就会出现不同的显示,就显得很乱
,比如下图,有些甚至会显示不全,你还需要额外去调整显示的宽高。
为了对样式进行统一的处理,需要对选择后的数据显示进行处理。
找到官方的Demo
可以看到,下拉列表中显示的label和实际选中后显示的不一致,也就是说我们可以自己去定制选中后的显示,将所有下拉菜单选中后的显示都改成一样的。找到官方api可以看到tagRender
这个属性,可以帮助我们去自定义tag的显示。
< Select onChange={
changeLocals} style={
{
width: "60%" }} mode="tags" value={
currentValue} tagRender={
tagRender}>
{
map(list, obj => {
return <Option value={
obj.value} id={
obj.name}>{
obj.label}</Option>
})
}
</Select>
import React from 'react'
import {
Tag } from 'antd'
/**
* Select选中后显示的样式
* @param {*} props
* @returns
*/
export default function tagRender(props) {
const {
label, closable, onClose } = props;
console.log("props",props)
return (
<Tag color="default" closable={
closable} onClose={
onClose} style={
{
marginRight: 3 }}>
{
label.props.children.props?.displayname||label.props.children[0].props?.children}
</Tag>
);
}
我们可以实际打印一下这里的props见下图。从中可以看出,如果你label封装的不深,这里是hi第一层,源码是这样的:
// 获取角色标签
this.props.ajax.get('/xxxxx/list').then(res => {
const roles = res && map(res.data.list, obj => {
let data = {
}
data.label = <p style={
{
paddingLeft:"5px"}}><RibbonTags displayname={
obj.displayname} color={
obj.color} tagtype="R"/></p>
data.value = obj.name
return data
})
this.setState({
roles })
})
那么找到的路径会是:props.label.props.children.props.displayname
如果是我最上面的那种封装的实际层级应该是props.label.props.children[0].props.children
这里举的例子就是想要说:我的写法只是在我的需求之下进行的处理,并不适合其他人,如果你也有类似的需求,需要用label展示很多信息,那么你可以采取这种方式,但是一定要看清楚你实际要显示的label在什么层级之中,然后再去处理
。
最终的显示形式就同一成下面这种,不需要对下拉菜单宽高进行处理,来显示。
关于思考:如果我能为select的options属性追加一个额外的属性比如name,去用于显示最终要显示的名称,那么我在使用tagRender的时候,主要找到这个属性就可以了直接使用props.name就可以显示了。不管你封装的label有多深,我就不用去关心你的label,我只要拿到name属性并将其绑定到tagRender中即可。
尝试的思路1:在封装数据的时候为数据源追加一个额外的属性name,即data.name = obj.displayname
// 获取角色标签
this.props.ajax.get('/managewithgroups/api/roletag/list', {
dataowner_type: ["mine", "system", "other"] }).then(res => {
const roles = res && map(res.data.list, obj => {
let data = {
}
data.label = <p style={
{
paddingLeft:"5px"}}><RibbonTags displayname={
obj.displayname} color={
obj.color} tagtype="R"/></p>
data.value = obj.name
data.name = obj.displayname
return data
})
this.setState({
roles })
})
然后在props中打印,发现,你在data中绑定的属性在select并不认,所以会找不到这个name属性。
尝试的思路2:既然绑定在data中他不认,那么我绑定到option中,它会不会认呢?
< Select onChange={
changeLocals} style={
{
width: "60%" }} mode="tags" value={
currentValue} tagRender={
tagRender}>
{
map(list, obj => {
console.log("obj", obj)
return <Option value={
obj.value} name={
obj.name} id={
obj.name}>{
obj.label}</Option>
})
}
</Select>
可以看到在实际的绑定数据的时候是有name属性的,那么在给option绑定name和id的时候,会不会识别呢?
从上面的打印来看,并不能被识别,也就是说,我们无法给option绑定其他的属性,这里不应该是option不能被绑定其他属性,而是tag不能识别这种属性。所以说也不行。
尝试的思路3:可以在tagRender函数去处理这个额外的参数tagRnder={(props,"123")=>tagRender(props,"123")}
,这样在tagRender函数中就可以通过tagRender(props,name)这两个参数
,确实能接收到123
这个值。但怎么在select中获取这个值就是个问题了?
可以通过onchange事件,去给一个变量赋值,然后复制的变量传递给tagRender。但是这样一来成本就大了,为了解决一个问题,却又引入一个更复杂的问题,似乎有点得不偿失了。
解決办法
最后还是在官方API上找到了解决办法,只能说自己大意了。官方提供了optionLabelProp
作为展示用的。就是说,select
默认显示选中的label
,但是如果你的label
是比较复杂的东西,那么你可以使用optionLabelProp
来显示你选择后想要显示的东西。
那么我们在封装数据的时候就可以多设置一个属性来表示我们想显示的东西,比如name,displayname都可以,然后将它绑定到option
上,即可在我们选择的时候获取到我们想要显示的东西。
比如我在aip获取数据的时候,重新封装的数据格式,多设置了一个name
属性
let data = {
}
data.label = <div style={
{
paddingLeft: "5px" }}><RibbonTags displayname={
obj.displayname} color={
obj.color} tagtype="R" /></div>
data.value = obj.name
data.name = obj.displayname
在实际使用的时候
<Select mode="tags" onChange={
changeW} value={
workload} optionLabelProp="name">
{
workloadsT && workloadsT.map((obj) => {
return <Option key={
obj.value} name={
obj.name} type={
obj.value} value={
obj.value} disabled={
obj.disabled}>{
obj.label}</Option>
})
}
</Select>