ReactJS + Material-UI: How to use Material-UI’s FlatButton and Dialog in each TableRow?

时间:2022-08-22 19:16:52

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:

这就是它现在的工作方式:

ReactJS + Material-UI: How to use Material-UI’s FlatButton and Dialog in each TableRow?

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:

这就是它现在的工作方式:

ReactJS + Material-UI: How to use Material-UI’s FlatButton and Dialog in each TableRow?

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(在列表中迭代) - 行和标签(在你的问题中缺少键)。