Skip to content

Instantly share code, notes, and snippets.

@solace
Last active April 20, 2021 13:30
Show Gist options
  • Save solace/5bf893ef71f6ce8dbdf674e3aca17dda to your computer and use it in GitHub Desktop.
Save solace/5bf893ef71f6ce8dbdf674e3aca17dda to your computer and use it in GitHub Desktop.
Use react-bootstrap-typeahead with react-bootstrap-table
import React from 'react';
import editor from 'react-bootstrap-table/lib/Editor';
// Copied wholesale from react-bootstrap-table, modified where noted
class CustomModalBody extends React.Component {
getFieldValue() {
const newRow = {};
this.props.columns.forEach((column, i) => {
let inputVal;
if (column.autoValue) {
// when you want same auto generate value and not allow edit, example ID field
const time = new Date().getTime();
inputVal = typeof column.autoValue === 'function' ?
column.autoValue() :
(`autovalue-${time}`);
} else if (column.hiddenOnInsert || !column.field) {
inputVal = '';
} else {
// CHANGE: Not using `refs` syntax, omitted the counter.
// Add the counter back if there is risk of duplicate fields.
const dom = this[column.field];
inputVal = dom.value;
if (column.editable && column.editable.type === 'checkbox') {
const values = inputVal.split(':');
inputVal = dom.checked ? values[0] : values[1];
} else if (column.customInsertEditor) {
inputVal = inputVal || dom.getFieldValue();
}
}
newRow[column.field] = inputVal;
}, this);
return newRow;
}
render() {
const { columns, validateState, ignoreEditable } = this.props;
return (
<div className='modal-body'>
{
columns.map((column, i) => {
const {
editable,
format,
field,
name,
autoValue,
hiddenOnInsert,
customInsertEditor
} = column;
const attr = {
// CHANGE: Use function ref instead.
// If using field index above, apply here as well
ref: (ref) => {
this[field] = field === 'name' && ref && ref.instanceRef ? ref.instanceRef : ref;
},
placeholder: editable.placeholder ? editable.placeholder : name
};
let fieldElement;
const defaultValue = editable.defaultValue || undefined;
if (customInsertEditor) {
const { getElement } = customInsertEditor;
fieldElement = getElement(column, attr, 'form-control', ignoreEditable, defaultValue);
}
// fieldElement = false, means to use default editor when enable custom editor
// Becasuse some users want to have default editor based on some condition.
if (!customInsertEditor || fieldElement === false) {
fieldElement = editor(editable, attr, format, '', defaultValue, ignoreEditable);
}
if (autoValue || hiddenOnInsert || !column.field) {
// when you want same auto generate value
// and not allow edit, for example ID field
return null;
}
const error = validateState[field] ?
(<span className='help-block bg-danger'>{ validateState[field] }</span>) :
null;
return (
<div className='form-group' key={ field }>
<label>{ name }</label>
{ fieldElement }
{ error }
</div>
);
})
}
</div>
);
}
}
export default CustomModalBody;
import React from 'react';
import { Typeahead, asyncContainer } from 'react-bootstrap-typeahead';
// Install this so that you can remove editable mode from the custom field
import onClickOutside from 'react-onclickoutside';
const AsyncTypeahead = asyncContainer(Typeahead);
class CustomTypeaheadField extends React.Component {
constructor(props) {
super(props);
this.updateState = this.updateState.bind(this);
this.updateData = this.updateData.bind(this);
this.state = {
name: props.defaultValue,
isLoading: false,
options: []
}
}
// From react-onclickoutside, this is your manual 'saveOnBlur' handling
// that would otherwise be done by the table that also deselects the field
handleClickOutside(e) {
try {
if (this.props.tableRef) {
this.updateData();
this.props.tableRef.cleanSelected();
}
} catch (e) {}
}
focus() {
this._typeahead.getInstance().focus();
}
getFieldValue() {
return this.state.name;
}
updateData() {
this.props.onUpdate(this.state.name);
}
// Update internal state instead of whatever handler/redux action you would have used.
// Note: typeahead onChange returns an array, or empty array on clear, handle as needed
updateState(values) {
let value = '';
if (values.length > 0) {
value = values[0];
}
this.setState({name: value});
}
render() {
return <div>
<AsyncTypeahead
innerRef={ref => this._typeahead = ref}
allowNew={true}
clearButton={true}
isLoading={this.state.isLoading}
labelKey={'name'}
onSearch={query => {
this.setState({isLoading: true});
let URL = 'REMOTE';
fetch(URL)
.then(res => res.json())
.then(json => {
this.setState({isLoading: false, options: json});
});
}}
options={this.state.options}
// defaultSelected instead of selected, can't use it controlled in a table
defaultSelected={[this.props.defaultValue]}
onChange={this.updateState}
/>
</div>;
}
}
export default onClickOutside(CentreAgentNameField);
import React from 'react';
import styled from 'styled-components';
import { BootstrapTable as UnstyledBootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
import CustomTypeaheadField from './CustomTypeaheadField';
import CustomModalBody from './CustomModalBody';
// Resolves typeahead suggestions disappearing inside field borders
const BootstrapTable = styled(UnstyledBootstrapTable)`
& .react-bs-container-body {
overflow: inherit !important;
}
& td.typeahead-cell {
overflow: inherit !important;
}
`;
class ViewComponent extends React.Component {
constructor(props) {
this.createCustomEditField = this.createCustomEditField.bind(this);
this.createCustomInsertField = this.createCustomInsertField.bind(this);
}
createCustomModalBody(columns, validateState, ignoreEditable) {
return (
<CustomModalBody
columns={ columns }
validateState={ validateState }
ignoreEditable={ ignoreEditable }
/>
);
}
createCentreAgentNameEditField(onUpdate, props) {
return <CustomTypeaheadField onUpdate={onUpdate} {...props} tableRef={this._bootstraptable} />;
}
createCentreAgentNameInsertField(column, attr, editorClass, ignoreEditable) {
return <CustomTypeaheadField {...attr} />;
}
render() {
return <BootstrapTable
// Need this for the onBlur handling for the custom field
ref={ref => this._bootstraptable = ref}
data={ data }
remote={ true }
insertRow={ true }
deleteRow={ true }
selectRow={ { mode: 'radio' } }
cellEdit={{
mode: 'click',
blurToSave: true,
afterSaveCell: updateHandler
}}
options={{
// Override insert modal body
insertModalBody: this.createCustomModalBody,
onAddRow: addHandler,
onDeleteRow: removeHandler
}}
>
<TableHeaderColumn dataField='id' isKey={ true } autoValue={ true }>ID</TableHeaderColumn>
<TableHeaderColumn
dataField='name'
// Typeahead suggestions disappearing behind cell borders
editColumnClassName='typeahead-cell'
// Custom typeahead fields
customEditor={ { getElement: this.createCustomEditField } }
customInsertEditor={ { getElement: this.createCustomInsertField } }
editable={ { readOnly: false } }
>
Name
</TableHeaderColumn>
...
</BootstrapTable>
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment