I have a Material-UI’s <Table>
, and in each <TableRow>
(which is dynamically rendered) for the <TableBody>
, I would like to have a button (<FlatButton>
) for one of the columns. And once the button is clicked on, it will open up a <Dialog>
and inside it would like to have a working <Tabs>
.
我有一个Material-UI的
So how can I display a <FlatButton>
for each row for a particular column, and when the button is clicked on, display the <Dialog>
along with a working <Tabs>
on the inside as the content? And have the <Dialog>
close when clicked on outside?
那么如何为特定列的每一行显示
So far I have the following, but came across the following issues: the opens up but it is slow and clicking outside the <Dialog>
is not closing it, the <Tabs>
is visible but it is not working:
到目前为止,我有以下几点,但遇到了以下问题:打开但是很慢并且在
Main Table:
主表:
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>
)
}
}
Rendered Table Row:
渲染表行:
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>
)
}
}
Thank you in advance and will accept/upvote answer.
提前谢谢你,并接受/ upvote回答。
3 个解决方案
#1
2
You should probably only have one dialog for the whole table that lives in your MainTable
component. This is more efficient because you don't need a dialog per row but only one dialog.
您应该只有一个对话框,用于存在于MainTable组件中的整个表。这样更有效,因为您不需要每行一个对话框,而只需要一个对话框。
In order for the button in the RenderedTableRow
to open the modal and tell it which row is selected you need to pass down a callback function from MainTable
to RenderedTableRow
that when called, sets the dialog to be opened and stores which row was selected:
为了让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>
)
}
}
#2
2
Here is a working example below, it should work straight via copy pasta.
下面是一个工作示例,它应该通过复制意大利面直接工作。
The answer to your question is that you need to be able to differentiate between the different rows
, setting it to true
will display all dialogs, or possibly just the last. Once you differentiate it, displaying the dialog
you want shouldn't be a problem. There are ways to just have one dialog
and still have this work, but I'll let you figure it out.
您的问题的答案是您需要能够区分不同的行,将其设置为true将显示所有对话框,或者可能只显示最后一行。区分它后,显示所需的对话框应该不是问题。有一些方法只有一个对话框仍然有这项工作,但我会让你搞清楚。
Somethings to note, is that you can definitely clean this code up. Create separate files for creating TableRows
TableColumns
etc.
需要注意的一点是,你绝对可以清理这段代码。创建单独的文件以创建TableRows TableColumns等。
I left it at two columns for now, however you should be able to understand the code. Feel free to ask any additional questions.
我现在把它留在两列,但你应该能够理解代码。随意提出任何其他问题。
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;
#3
1
As I mentioned earlier in the comments, you should have only one Dialog
element along with the table component. Embedding Dialog
in each row will impact the performance and is general a bad practice. Here is the solution for mocked tabs:
正如我在前面的评论中提到的,你应该只有一个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;
This is how it works right now:
这就是它现在的工作方式:
Few remarks:
几句话:
- you should split this code into smaller components, I wrote this all in one to make it easy to test it,
- 你应该把这个代码拆分成更小的组件,我把它写在一起,以便于测试,
- remember to use
key
prop in all the elements of the list (where you iterate over a list) - both rows and tabs (key
is missing in your question). - 记得在列表的所有元素中使用key prop(在列表中迭代) - 行和标签(在你的问题中缺少键)。
#1
2
You should probably only have one dialog for the whole table that lives in your MainTable
component. This is more efficient because you don't need a dialog per row but only one dialog.
您应该只有一个对话框,用于存在于MainTable组件中的整个表。这样更有效,因为您不需要每行一个对话框,而只需要一个对话框。
In order for the button in the RenderedTableRow
to open the modal and tell it which row is selected you need to pass down a callback function from MainTable
to RenderedTableRow
that when called, sets the dialog to be opened and stores which row was selected:
为了让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>
)
}
}
#2
2
Here is a working example below, it should work straight via copy pasta.
下面是一个工作示例,它应该通过复制意大利面直接工作。
The answer to your question is that you need to be able to differentiate between the different rows
, setting it to true
will display all dialogs, or possibly just the last. Once you differentiate it, displaying the dialog
you want shouldn't be a problem. There are ways to just have one dialog
and still have this work, but I'll let you figure it out.
您的问题的答案是您需要能够区分不同的行,将其设置为true将显示所有对话框,或者可能只显示最后一行。区分它后,显示所需的对话框应该不是问题。有一些方法只有一个对话框仍然有这项工作,但我会让你搞清楚。
Somethings to note, is that you can definitely clean this code up. Create separate files for creating TableRows
TableColumns
etc.
需要注意的一点是,你绝对可以清理这段代码。创建单独的文件以创建TableRows TableColumns等。
I left it at two columns for now, however you should be able to understand the code. Feel free to ask any additional questions.
我现在把它留在两列,但你应该能够理解代码。随意提出任何其他问题。
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;
#3
1
As I mentioned earlier in the comments, you should have only one Dialog
element along with the table component. Embedding Dialog
in each row will impact the performance and is general a bad practice. Here is the solution for mocked tabs:
正如我在前面的评论中提到的,你应该只有一个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;
This is how it works right now:
这就是它现在的工作方式:
Few remarks:
几句话:
- you should split this code into smaller components, I wrote this all in one to make it easy to test it,
- 你应该把这个代码拆分成更小的组件,我把它写在一起,以便于测试,
- remember to use
key
prop in all the elements of the list (where you iterate over a list) - both rows and tabs (key
is missing in your question). - 记得在列表的所有元素中使用key prop(在列表中迭代) - 行和标签(在你的问题中缺少键)。