ReactJS + Material-UI:如何在每个 TableRow 中使用 Material-UI 的 FlatButton 和 Dialog?
Posted
技术标签:
【中文标题】ReactJS + Material-UI:如何在每个 TableRow 中使用 Material-UI 的 FlatButton 和 Dialog?【英文标题】:ReactJS + Material-UI: How to use Material-UI’s FlatButton and Dialog in each TableRow? 【发布时间】:2017-06-02 08:54:35 【问题描述】:我有一个 Material-UI 的<Table>
,并且在每个<TableRow>
(动态渲染)中为<TableBody>
,我想为其中一列设置一个按钮(<FlatButton>
)。一旦点击按钮,它会打开一个<Dialog>
,在里面会有一个工作的<Tabs>
。
那么,如何为特定列的每一行显示<FlatButton>
,并在单击按钮时,在内部显示<Dialog>
以及工作中的<Tabs>
作为内容?并在外部单击时关闭<Dialog>
?
到目前为止,我有以下问题,但遇到了以下问题:打开但速度很慢,在 <Dialog>
之外单击并没有关闭它,<Tabs>
可见但不起作用:
主表:
import React, Component from 'react'
import
Subheader,
Table,
TableBody,
TableHeader,
TableHeaderColumn,
TableRow,
from 'material-ui'
import RenderedTableRow from ‘./RenderedTableRow'
export default class MainTable extends Component
constructor()
super()
render()
return (
<div>
<div>
<Subheader>Table</Subheader>
<Table
multiSelectable=true
>
<TableHeader
displaySelectAll=true
enableSelectAll=true
>
<TableRow>
<TableHeaderColumn>
Col 1
</TableHeaderColumn>
<TableHeaderColumn>
Col 2
</TableHeaderColumn>
<TableHeaderColumn>
Col 3
</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody
deselectOnClickaway=false
stripedRows
>
<RenderedTableRow ...this.props/>
</TableBody>
</Table>
</div>
</div>
)
渲染的表格行:
import React, Component from 'react'
import Dialog, FlatButton, Tabs, Tab, TableRow, TableRowColumn from 'material-ui'
import ContentAdd from 'material-ui/svg-icons/content/add';
export default class RenderedTableRow extends Component
constructor(props)
super(props)
this.state =
open: false,
this._handleOpen = this._handleOpen.bind(this)
this._handleClose = this._handleClose.bind(this)
_handleOpen()
this.setState(
open: true
)
_handleClose()
this.setState(
open: false
)
render()
const
children,
...rest
= this.props
const actions = [
<FlatButton
label="Cancel"
primary=true
onClick=this._handleClose
/>,
]
return (
<TableRow ...rest>
children[0]
<TableRowColumn>Red</TableRowColumn>
<TableRowColumn>John, Joshua</TableRowColumn>
<TableRowColumn>
<FlatButton
icon=<ContentAdd/>
onClick=this._handleOpen
/>
</TableRowColumn>
<Dialog
actions=actions
autoScrollBodyContent=true
open=this.state.open
onRequestClose=this._handleClose
modal=false
title='Test'
>
<Tabs>
<Tab label="Item One" >
<div>
<h2 >Tab One</h2>
<p>
This is an example tab.
</p>
</div>
</Tab>
<Tab label="Item Two" >
<div>
<h2>Tab Two</h2>
<p>
This is another example tab.
</p>
</div>
</Tab>
</Tabs>
</Dialog>
</TableRow>
)
提前感谢您,我们会接受/支持答案。
【问题讨论】:
对话框中会显示什么?对于每一行,您正在生成一个新对话框 - 是否有必要?正如你提到的,它会很慢。更好的方法是在主表格组件中有一个 Dialog 元素并传递必要的道具。 我会将对话框移到表格之外,将回调传递给表格行中的按钮,单击该按钮,打开对话框并将所选行传递给它 @szymonm 每行内容不同,但会在对话框中使用<Tabs>
显示,但目前选项卡不起作用。我对每一行都这样做的原因是因为每一行的每个对话框都以不同的方式表示。我怎样才能解决这个问题?
@Mateusz 您介意显示作为澄清的答案吗?所以我也可以接受并投票。
@philippspo 更新了他的答案,这正是您所需要的 ;-)
【参考方案1】:
您的MainTable
组件中的整个表格应该只有一个对话框。这更有效,因为您不需要每行一个对话框,而只需要一个对话框。
为了让RenderedTableRow
中的按钮打开模式并告诉它选择了哪一行,您需要将一个回调函数从MainTable
传递给RenderedTableRow
,当调用该函数时,设置要打开的对话框并存储选择了哪一行:
export default class MainTable extends Component
state =
selectedRow: null,
handleSelectRow(rowIndex)
this.setState(
selectedRow: rowIndex,
)
render()
return (
<div>
<div>
<Subheader>Table</Subheader>
<Table
multiSelectable=true
>
// ...
<TableBody
deselectOnClickaway=false
stripedRows
>
rows.map((row, index) => (
<RenderedTableRow
row=row
...this.props
onSelectRow=() => this.handleSelectRow(index)
/>
))
</TableBody>
</Table>
</div>
// Dialog goes here and is only rendered once per table
// it is only open when there is a row selected
<Dialog
open=Boolean(this.state.selectedRow)
>
// you can get the selected row with rows[this.state.selectedRow]
</Dialog>
</div>
)
【讨论】:
为了有动态多行,我将用一个数组填充表格,其中每一行都有特定的属性,如下所示:<TableBody>array.map(row => return(<RenderedTableRow/>));</TableBody>
。如果是这种情况,我将如何区分单击了哪一行并在单击的特定行的对话框中显示该特定内容?因为每一行都会在一个对话框中显示不同的内容。还有为什么<Tabs>
不起作用?
我扩展了代码示例以包含多行。不知道为什么Tabs
不起作用。他们在我看来很好。我什至尝试了你的Dialog
代码和Tabs
为我工作。
你介意用 codepen 或类似的东西来展示它吗?似乎没有工作.. :(
刚刚签到看看你是否看过我之前的评论。请告诉我。【参考方案2】:
下面是一个工作示例,它应该直接通过复制意大利面工作。
您的问题的答案是您需要能够区分不同的rows
,将其设置为true
将显示所有对话框,或者可能只显示最后一个。一旦你区分它,显示你想要的dialog
应该不是问题。有很多方法可以只拥有一个 dialog
并且仍然可以进行这项工作,但我会让你弄清楚。
需要注意的是,您绝对可以清理此代码。为创建 TableRows
TableColumns
等创建单独的文件。
我现在把它放在两列,但是你应该能够理解代码。如有任何其他问题,请随时提出。
import React, Component from 'react'
import Dialog, FlatButton, Tabs, Tab, TableRow, TableRowColumn from 'material-ui'
import ContentAdd from 'material-ui/svg-icons/content/add';
class MainTable extends Component
static fields = [tab1:"a", tab2:"b", tab1:"c", tab2:"d"];
state =
open: false,
handleOpen = (field) => () =>
this.setState(
open: field
)
handleClose = () =>
this.setState(
open: false
)
renderRows = (field) =>
const open = this.state;
const actions = [
<FlatButton
label="Cancel"
primary=true
onTouchTap=this.handleClose
/>,
<FlatButton
label="Submit"
primary=true
keyboardFocused=true
onTouchTap=this.handleClose
/>,
];
return (<TableRow key=field.tab1>
<TableRowColumn>field.tab1</TableRowColumn>
<TableRowColumn>
<FlatButton
icon=<ContentAdd/>
onClick=this.handleOpen(field.tab1)
/>
</TableRowColumn>
<Dialog
title="Dialog With Actions"
actions=actions
modal=false
open=open === field.tab1
onRequestClose=this.handleClose
>
<Tabs>
<Tab label=field.tab1 >
<div>
<h2>field.tab1</h2>
<p>
This is one tab.
</p>
</div>
</Tab>
<Tab label=field.tab2>
<div>
<h2>field.tab2</h2>
<p>
This is another example tab.
</p>
</div>
</Tab>
</Tabs>
</Dialog>
</TableRow>);
render()
const rows = MainTable.fields.map(this.renderRows);
return (
<div>
rows
</div>
)
export default MainTable;
【讨论】:
为了有动态多行,我将用一个数组填充表格,其中每一行都有特定的属性,如下所示:tabs
。我把那部分省略了。
顺便说一句,你试过上面的代码吗?单击不同的rows
会显示不同类型的数据。我唯一没有研究的是tabs
,我不确定他们是做什么的,但我可以稍后查看文档。
给你@JoKo。这是一个带有标签的完整工作示例。希望对您有所帮助。【参考方案3】:
正如我之前在 cmets 中提到的,您应该只有一个 Dialog
元素和表格组件。在每一行中嵌入Dialog
会影响性能,通常是一种不好的做法。这是模拟标签的解决方案:
import React, Component from 'react';
import find from 'lodash';
import Dialog, FlatButton, Tabs, Tab, TableRow, TableRowColumn from 'material-ui';
import ContentAdd from 'material-ui/svg-icons/content/add';
class MainTable extends Component
// mocked data to show you the example:
static fields = [
id: 1,
name: 'John',
tabs: [
header: 'Tab 1 John',
content: 'Content of tab 1 for John'
,
header: 'Tab 2 John',
content: 'Content of tab 2 for John'
]
,
id: 2,
name: 'George',
tabs: [
header: 'Tab 1 George',
content: 'Content of tab 1 for George'
,
header: 'Tab 2 George',
content: 'Content of tab 2 for George'
]
];
state =
activeRowId: null // we will store the `id` of the active row (opened dialog)
;
handleOpen = (rowId) => () =>
this.setState(
activeRowId: rowId // set `id` taken from the row
);
;
handleClose = () =>
this.setState(
activeRowId: null // reset active `id`
);
;
renderRows = (field) => (
<TableRow key=`row-$field.id`>
<TableRowColumn>field.name</TableRowColumn>
<TableRowColumn>
<FlatButton
icon=<ContentAdd />
onClick=this.handleOpen(field.id)
/>
</TableRowColumn>
</TableRow>
);
render()
const rows = MainTable.fields.map(this.renderRows);
const activeRowId = this.state;
const actions = [
<FlatButton
label="Cancel"
primary
onTouchTap=this.handleClose
/>,
<FlatButton
label="Submit"
primary
keyboardFocused
onTouchTap=this.handleClose
/>,
];
const activeRow = find(MainTable.fields, id: activeRowId ); // find the data for this active row `id`
return (
<div>
rows
activeRow ? (
<Dialog
title="Dialog title"
actions=actions
modal=false
open
onRequestClose=this.handleClose
>
<Tabs>
activeRow.tabs.map((tab, index) => (
<Tab label=tab.header key=`tab-$index`>
<div>
<h2>tab.header</h2>
<p>tab.content</p>
</div>
</Tab>
))
</Tabs>
</Dialog>
) : null
</div>
);
export default MainTable;
现在是这样工作的:
几点说明:
您应该将此代码拆分为更小的组件,我将其全部编写在一个组件中以便于测试, 记得在列表的所有元素中使用key
属性(迭代列表的位置) - 行和选项卡(您的问题中缺少 key
)。
【讨论】:
以上是关于ReactJS + Material-UI:如何在每个 TableRow 中使用 Material-UI 的 FlatButton 和 Dialog?的主要内容,如果未能解决你的问题,请参考以下文章
ReactJS + Material-UI:如何在 Material-UI <Table/> 的 <TableRow/> 之间交替颜色?
ReactJS + Material-UI:如何减小 Material-UI 的 <TableRow/> 的列宽?
ReactJS + Material-UI:如何使用 Material-UI 的 <DatePicker> 将当前日期设置为默认值?
数据变化时如何在ReactJS中刷新Material-Table(Material-ui)组件
如何结合 ReactJs Router Link 和 material-ui 组件(如按钮)?
ReactJS with Material-UI:如何按字母顺序对 Material-UI 的 <TableRow> 数组进行排序?