Skip to content

Instantly share code, notes, and snippets.

@joadr
Created October 12, 2016 19:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joadr/2941badb89935f6447492da5127a83ea to your computer and use it in GitHub Desktop.
Save joadr/2941badb89935f6447492da5127a83ea to your computer and use it in GitHub Desktop.
Create Table From Collection
import React from 'react'
import { Meteor } from 'meteor/meteor'
import { createContainer } from 'meteor/react-meteor-data'
import {grey400} from 'material-ui/styles/colors'
import ArrowDownward from 'material-ui/svg-icons/navigation/arrow-downward'
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow} from 'material-ui/Table'
import _ from 'underscore'
class NoItems extends React.Component {
render () {
return (
<div style={{ textAlign: 'center', padding: 20 }}>
<p style={{ color: grey400 }}>
No items found
</p>
</div>
)
}
}
class LoadingComponent extends React.Component {
render () {
return (
<div style={{ textAlign: 'center', padding: 20 }}>
<p style={{ color: grey400 }}>
Loading...
</p>
</div>
)
}
}
const propTypes = {
collection: React.PropTypes.object.isRequired,
publication: React.PropTypes.string.isRequired,
filter: React.PropTypes.string,
selector: React.PropTypes.object,
itemsPerPage: React.PropTypes.number,
fields: React.PropTypes.array,
sort: React.PropTypes.object,
itemComponent: React.PropTypes.any.isRequired,
loadingComponent: React.PropTypes.any,
noItemsComponent: React.PropTypes.any,
parentClassName: React.PropTypes.string,
lastComponent: React.PropTypes.any,
firstComponent: React.PropTypes.any,
localFilter: React.PropTypes.bool,
options: React.PropTypes.object
}
const defaultProps = {
itemsPerPage: 1000,
fields: ['name'],
sort: { createdAt: -1 },
filter: '',
loadingComponent: <LoadingComponent />,
noItemsComponent: <NoItems />,
parentClassName: '',
selector: {},
localFilter: false,
options: {}
}
class CollectionTable extends React.Component {
constructor (props) {
super(props)
this.state = {
items: props.items,
sortBy: -1
}
}
componentWillReceiveProps (nextProps) {
this.setState({
items: nextProps.items
})
}
columnSort (column) {
var columnName
if (this.props.fields[column]) {
columnName = this.props.fields[column]
} else {
if (!this.props.headers[column].func) {
return
}
columnName = this.props.headers[column].name
}
if (this.state.sortBy === columnName) {
this.setState({
items: this.state.items.reverse()
})
return
}
this.setState({
items: _.sortBy(this.props.items, columnName),
sortBy: columnName
})
}
applyFunction (header) {
var newItems = _.clone(this.state.items)
_.map(newItems, function (item) {
item[header.name] = header.func(item)
})
}
renderHeaders () {
return this.props.headers.map((header, index) => {
var icon
var columnName
if (this.props.fields[index]) {
columnName = this.props.fields[index]
} else {
columnName = this.props.headers[index].name
}
if (this.state.sortBy === columnName) {
icon = <ArrowDownward style={{height: '15px'}} />
}
if (_.isObject(header)) {
var styles = {}
if (header.styles) {
styles = header.styles
}
if (header.func && typeof header.func === 'function') {
this.applyFunction(header)
}
return (
<TableHeaderColumn key={index} onTouchTap={this.columnSort.bind(this, index)} style={styles}>{header.name}{icon}</TableHeaderColumn>
)
}
return (
<TableHeaderColumn key={index} onTouchTap={this.columnSort.bind(this, index)}>{header}{icon}</TableHeaderColumn>
)
})
}
renderItems () {
return this.state.items.map((item) => {
return React.createElement(this.props.itemComponent, {
key: item._id,
item: item
})
})
}
renderLoading () {
return this.props.loadingComponent
}
renderNoItems () {
return this.props.noItemsComponent
}
render () {
if (this.props.isLoading) {
return this.renderLoading()
} else if (this.props.items.length > 0) {
return (
<Table fixedHeader>
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
<TableRow>
{this.renderHeaders()}
</TableRow>
</TableHeader>
<TableBody className={this.props.parentClassName} showRowHover>
{this.renderItems()}
</TableBody>
</Table>
)
} else {
return this.renderNoItems()
}
}
}
CollectionTable.propTypes = propTypes
CollectionTable.defaultProps = defaultProps
export default createContainer(({ localFilter, filter, publication, itemsPerPage, collection, selector, options }) => {
var filter = localFilter ? null : filter
const handler = Meteor.subscribe(publication, filter, itemsPerPage)
const items = collection.find(selector, options).fetch()
const isLoading = !handler.ready()
return { items, isLoading }
}, CollectionTable)
@joadr
Copy link
Author

joadr commented Oct 12, 2016

Componente padre:

Constructor:

constructor (props) {
    super(props)
    this.state = {
      selector: {$or: [{deleted: false}, {deleted: null}]},
      fields: ['name']
    }
  }

dentro de la clase:

getSelector () {
    if (!this.state.filter) {
      return this.state.selector
    } else {
      const filters = this.state.fields.map((field) => {
        var obj = {}
        obj[field] = new RegExp(`${this.state.filter}.*`, 'i')
        return obj
      })
      return { $and: [{ $or: filters }, this.state.selector] }
    }
  }
getOptions () {
    return {
      sort: this.state.sort,
      limit: this.state.itemsPerPage
    }
  }

llamado del componente

          <CollectionTable
            collection={Contracts}
            publication='contracts.user.index'
            itemComponent={ContractsUserIndexItem}
            filter={this.state.filter}
            fields={this.state.fields}
            headers={['Nombre', {name: 'Estado', func: this.getItemState}, {name: 'Rol', func: this.getRol}, {name: 'Acciones', styles: {width: '100px'}}]}
            selector={this.getSelector()}
            options={this.getOptions()}
          />

Ejemplo de Item Component:

export default class ContractsUserIndexItem extends React.Component {
  @autobind
  handleDownload () {
    FlowRouter.go('contracts-download', { id: this.props.item._id })
  }

  @autobind
  handleDelete () {
    Meteor.call('deleteContract', this.props.item._id)
    // delete
  }

  @autobind
  handleEdit () {
    FlowRouter.go('contracts-create-id', { id: this.props.item._id })
  }

  @autobind
  handleView () {
    FlowRouter.go('contract-view', { id: this.props.item._id })
  }

  @autobind
  handleReminder () {
    Meteor.call('contractSignManualReminder', this.props.item._id, function (error, result) {
      if (error) {
        console.log('error en reminder!', error)
      }
    })
  }

  render () {
    return (
      <TableRow>
        <TableRowColumn>{this.props.item.name}</TableRowColumn>
        <TableRowColumn><ItemState state={this.props.item.Estado} /></TableRowColumn>
        <TableRowColumn>{this.props.item.Rol}</TableRowColumn>
        <TableRowColumn style={{width: '100px'}}>
          <IconMenu
            style={{float: 'right'}}
            iconButtonElement={<IconButton><MoreVertIcon /></IconButton>}
            anchorOrigin={{horizontal: 'right', vertical: 'top'}}
            targetOrigin={{horizontal: 'right', vertical: 'top'}}
          >
            <MenuItem primaryText='Ver' leftIcon={<Visibility />} onTouchTap={this.handleView} />
            <MenuItem primaryText='Editar' leftIcon={<ModeEdit />} onTouchTap={this.handleEdit} />
            <MenuItem primaryText='Descargar' leftIcon={<FileDownload />} onTouchTap={this.handleDownload} />
            <MenuItem primaryText='Recordar Firma' leftIcon={<RecordVoiceOver />} onTouchTap={this.handleReminder} />
            <Divider />
            <MenuItem primaryText='Borrar' leftIcon={<Delete />} onTouchTap={this.handleDelete} />
          </IconMenu>
        </TableRowColumn>
      </TableRow>
    )
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment