如何使用Material UI使下拉菜单在应用栏下方打开?

105

我刚接触Material UI,开始尝试使用他们的带菜单的应用栏示例。当切换菜单下拉列表时,它会在应用栏本身上方打开,而我希望它在导航栏下方打开。

从文档中,我了解到可以通过 anchorEl 更改位置,如Popover定位指南中所述。但是,当我将其实施到我的 menu 组件时,没有任何反应。在Material UI中,处理这个问题的“正确方法”是什么?

class Header extends React.Component {
  state = {
    auth: true,
    anchorEl: null,
    anchorOriginVertical: 'bottom',
    anchorOriginHorizontal: 'right',
    transformOriginVertical: 'top',
    transformOriginHorizontal: 'right',
    anchorReference: 'anchorEl',
  };

  handleChange = (event, checked) => {
    this.setState({ auth: checked });
  };

  handleMenu = event => {
    this.setState({ anchorEl: event.currentTarget });
  };

  handleClose = () => {
    this.setState({ anchorEl: null });
  };

  render() {
    const { classes } = this.props;
    const { auth, anchorEl } = this.state;
    const open = Boolean(anchorEl);

    return (
      <div className={classes.root}>
        <AppBar position="static">
          <Toolbar>
            <Typography type="title" color="inherit" className={classes.flex}>
              Title
            </Typography>
            {auth && (
              <div>
                <IconButton
                  aria-owns={open ? 'menu-appbar' : null}
                  aria-haspopup="true"
                  onClick={this.handleMenu}
                  color="contrast"
                >
                  <AccountCircle />
                </IconButton>
                <Menu
                  id="menu-appbar"
                  anchorEl={anchorEl}
                  open={open}
                  onClose={this.handleClose}
                >
                  <MenuItem onClick={this.handleClose}>Profile</MenuItem>
                  <MenuItem onClick={this.handleClose}>My account</MenuItem>
                </Menu>
              </div>
            )}
          </Toolbar>
        </AppBar>
      </div>
    );
  }
}
8个回答

219

我让它工作的方式是通过像这样在菜单本身上设置属性:

  <Menu
    id="menu-appbar"
    anchorEl={anchorEl}
    getContentAnchorEl={null}
    anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
    transformOrigin={{ vertical: 'top', horizontal: 'center' }}
    open={open}
    onClose={this.handleClose}
    className={props.classes.menu}
  >

定位属性(anchorOrigintransformOrigin)来源于 Popover 组件,请参见演示:https://mui.com/material-ui/react-popover/#anchor-playground

我还必须添加 getContentAnchorEl={null} 才能使垂直方向的属性起作用,这一点我是从这个问题中学到的:https://github.com/mui/material-ui/issues/7961
编辑: 在 Material UI v5 中已删除 getContentAnchorEl 属性,不再需要。

如果使用状态来设置锚元素的属性,我不确定该怎么做,但或许这可以帮助你入门。


13
这个解决方案仍然适用于2020年4月,尽管MUI应该更加明确。另外@Emmy,你可以编辑问题并修复链接 :)我已经为你修复了链接到弹出框示例。 - Dan Dascalescu
1
getContentAnchorEl 属性对我很有用。当我在同一行使用两个菜单弹出框的时候,菜单弹出框会在期望的锚点下方弹出。 - Waleed93
1
为了让菜单出现在按钮下方,我使用anchorOrigin={{ vertical: 'bottom',horizontal: 'left' }} transformOrigin={{ vertical: 'top', horizontal: 'left' }} - Dave Howson
1
非常感谢,我为这个问题苦苦挣扎了几个小时,直到找到了这个答案! - T.arar

10
这是我的解决方案,希望能帮助到他人。
<Menu
  open={this.state.openProps}
  anchorEl={this.state.anchorEl}
  onClose={onClose}
  className={classes.styles}
  disableAutoFocusItem
  PaperProps={{
    style: {
      left: '50%',
      transform: 'translateX(-77%) translateY(32%)',
    }
  }}
  MenuListProps={{
    style: {
      padding: 0,
    },
  }}
>

1
谢谢,我尝试了锚点但是反弹了,这个方法对我有用。 - SkySoft

6
Matheus 的回答中有一个错误,anchorEl 的类型不是布尔型,在 React Hooks 中需要使用以下类型:
  const [menuOpen, setMenuOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = useState()

  const recordButtonPosition = (event: any) => {
      setAnchorEl(event.currentTarget);
      setMenuOpen(true);
  }

  let closeMenu = () => {
      setMenuOpen(false);
  }

  return (
      <React.Fragment>
          <Button onClick={recordButtonPosition}>
              OPEN MENU
          </Button>
          <Menu
              anchorEl={anchorEl}
              open={menuOpen}
              onClose={closeMenu}>
              <MenuItem onClick={closeMenu}> ExampleMenuItem </MenuItem> 
          </Menu>
      </React.Fragment>
  );

5

使用useRef钩子是实现这一目标的简单方法

  1. 创建ref
  2. 在要显示菜单的地方插入您的ref,例如一个Div或其他
  3. 将ref插入菜单的anchor属性中

输入代码

const inputEl = useRef(null);
<Mycomponent ref={inputEl}/>
<Menu
  id="simple-menu"
  anchorEl={inputEl.current}
  keepMounted
  open={Boolean(anchorEl)}
  onClose={handleClose}
>

对我来说,这绝对是更容易的方法。


5

对我来说,当我第一次点击后出现了跳动的效果,我必须要设置 keepMounted={false}

你可以通过调整开发者工具中的 translateX(10px) translateY(50px) 来获取 x 和 y 值。

图片描述在此输入

                      <div>
                         <Menu
                                    id="simple-menu"
                                    anchorEl={anchorEl}
                                    keepMounted={false}
                                    open={Boolean(anchorEl)}
                                    onClose={this.handleClose}
                                    PaperProps={{
                                        style: {
                                            transform: 'translateX(10px) translateY(50px)',
                                        }
                                    }}
                                >
                                    <MenuItem onClick={this.handleClose}>My Account</MenuItem>
                                    <Divider variant="middle"/>
                                    <MenuItem onClick={this.handleClose}>Logout</MenuItem>
                                </Menu>
                            </div>


2

这是因为您没有定义锚点。

菜单属性 - anchorEl,负责传递调用它的按钮的位置,这并不完全正确,但只是为了更容易理解。

因此,每当有点击事件时,您应该引用它。我建议您使用反应钩子,这样可以使组件更加简洁。

React 有状态组件

const [menuOpen, setMenuOpen] = useState(false)
const [anchorEl, setAnchorEl] = useState(false)

const handleClick = (event) => {
const anchorEl = event.currentTarget
this.setState({ ...this.state, menuOpen: !menuOpen , anchorEl })

React Hooks

const [menuOpen, setMenuOpen] = useState(false)
const [anchorEl, setAnchorEl] = useState(false)

const handleClick = (event) => {
setAnchorEl(event.currentTarget)
}

渲染

return (
 <Menu
   anchorEl={anchorEl}
   open={menuOpen}
   onClose={handleClick }              
 </Menu 
)

6
难怪 OP 没有定义锚点。菜单组件页面上没有提供有关如何实际使用它的信息。 - Dan Dascalescu

0
在新的 Material-ui 5 版本中,我不明白为什么会发生这种奇怪的事情。但是当我在应用栏中创建一个单独的菜单组件时,它可以正常工作。
import * as React from 'react';
import { useRouter } from 'next/router'
import { Menu, IconButton, } from '@mui/material';
import MenuItem from '@mui/material/MenuItem';
import AccountCircle from '@mui/icons-material/AccountCircle';

export default function AppBarMenu() {
    const router = useRouter()
    const [anchorEl, setAnchorEl] = React.useState(null);
    const open = Boolean(anchorEl);
    const handleClick = (event) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };
    const handleLogout = () => {
        localStorage.clear()
        router.push('/auth/login')
    }
    return (
        <>
            <IconButton
                id="basic-button"
                size="large"
                aria-controls="basic-menu"
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
                onClick={handleClick}
            >
                <AccountCircle sx={{ color: 'white' }} />
            </IconButton>
            <Menu
                id="basic-menu"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                MenuListProps={{
                    'aria-labelledby': 'basic-button',
                }}
            >
                <MenuItem onClick={handleClose}>Profile</MenuItem>
                <MenuItem onClick={handleClose}>My account</MenuItem>
                <MenuItem onClick={handleLogout}>Logout</MenuItem>
            </Menu>
        </>
    );
}

-1

所有那些东西对我都没有用。所以我给按钮一个ID,并使用JavaScript将其放在菜单上:

<IconButton id="btnPerfil" onClick={this.toggleMenuPerfilOpen}>
    <AccountCircle color="inherit" />
</IconButton>
<Menu
  anchorPosition={{
    vertical: 'top',
    horizontal: 'center'
  }}
  anchorEl={document.getElementById('btnPerfil')}
  keepMounted
  open={this.state.isMenuPerfilOpen}
  onClose={this.toggleMenuPerfilOpen}
>...</Menu>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接