I need to make my current draggable component's dropdown to not render on specific cell (specifically the first Row). However, it will still show the cell just without the dropdown. I tried to search for a way however could not find any. Appreciate any help / directions given. Thanks! I also also attached the screenshot of current and expected. (Just take note of the dropdown being empty)
*Current Interface - all dropdown rendered
*Expected New Interface - first dropdown not rendered
Main code with ant design component Row/Col and Select/Option for the dropdown:
render() {
return (
<div>
<Table
scroll={{ x: 300 }}
pagination={{
defaultPageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '25', '50'],
}}
columns={this.state.columns}
dataSource={this.state.datas}
></Table>
<Modal
destroyOnClose={true}
width={'80%'}
title='Approval Sequence'
visible={this.state.isVisible}
onOk={() => {
updateFlowType(this.state.currentItem.id, {
name: this.state.currentItem.name,
sites: this.state.initTags.map((x) => ({
idSite: x.id,
returnToStep: x.returnToStep,
})),
})
.then((r) => {
notification['sucess']({
message: 'Success',
description: 'Save data success',
});
this.setState({ isVisible: false });
this.getData();
})
.catch(() => {
notification['failed']({
message: 'Failed',
description: 'Failed to save',
});
});
}}
onCancel={() => {
this.setState({ isVisible: false });
}}
>
<Row gutter={[2, 2]}>
<Col style={{ textAlign: 'center' }} span={8}>
<Text strong>Role</Text>
</Col>
<Col style={{ textAlign: 'center' }} span={8}>
<Text strong> Return to Department: </Text>
</Col>
</Row>
<div className='list-area'>
<div className='drag-list'>
<DraggableArea
onChange={(initTags) => this.setState({ initTags })}
isList
tags={this.state.initTags}
render={({ tag }) => (
<Row>
<Col span={8}>
<Button style={{ width: '100%' }}>{tag.name}</Button>
</Col>
<Col span={16}>
<Select
onChange={(e) => {
//create clone
let clone = [...this.state.initTags];
let item = clone.find((x) => x.id === tag.id);
let itemReject = clone.find((x) => x.name === e);
console.log('itemReject', itemReject);
//create returnToStep in item
item.returnToStep = itemReject.id;
this.setState({
initTags: clone,
});
}}
//placeholder = 'Select return step'
style={{ width: '100%' }}
>
{this.getReject(tag.name).map((newTag) => (
//getReject function will slice to get only items before the current iteration object (e.g. if current is index 3, only get index 0~2)
<Option key={newTag.name} value={newTag.name}>
{newTag.name}
</Option>
))}
</Select>
</Col>
</Row>
)}
></DraggableArea>
</div>
</div>
</Modal>
</div>
);
}
You can do "conditional render" using a condition and a component like this:
const someBoolean = true;
retrun (
{ someBoolean && <SomeComponent /> }
)
if someBoolean is true, then <SomeComponent/> will show.
if someBoolean is false, then <SomeComponent/> will not show.
So, just use that to conditionally render your dropdown column or any other component.
If, the table rows are based upon content and dynamically rendered, then I would modify the content accordingly. (i.e. don't provide the rows you don't want to render).
Related
I am facing an issue of implementing Draggable row using react-draggable-tag library and ant design. Currently the onChange inside of the "Option" tag (ant-design component) is causing an inifinite loop.
I am doing that onChange in an attempt to update the Return property inside of initialTags state. So that when the user submit the Modal (ant design component), the i will be able to call the update axios api and input the information using initialTag's return property. However,currently it does not work.
Any help will be appreciated !! because i am stuck for a few days now.
import statement:
import {
Button,
Modal,
Col,
Row,
Select
} from 'antd';
import { DraggableAreasGroup } from 'react-draggable-tags';
const group = new DraggableAreasGroup();
const DraggableArea = group.addArea();
const { Option } = Select;
class component constructor:
export default class flowSeq extends React.Component {
constructor(props) {
super(props);
this.state = {
initialTags: []
...
Javascript code with draggable-tag library and ant design:
<Modal
destroyOnClose={true}
width={'40%'}
title='Approval Sequence'
visible={this.state.isModalVisible}
onOk={() => {
updateFlow(
... axios api update (still trying to implement)....
),
})
.then((r) => {
notification['success']({
message: 'Success',
description: 'Save successfully',
});
this.setState({ isModalVisible: false });
this.getData();
})
.catch(() => {
notification['error']({
message: 'Error',
description: 'Save failed',
});
});
}}
onCancel={() => {
this.setState({ isModalVisible: false });
}}
>
<Row gutter={[2, 2]}>
<Col style={{ textAlign: 'left' }} span={8}>
<Text strong>Departments</Text>
</Col>
<Col style={{ textAlign: 'left' }} span={8}>
<Text strong> Return to Department: </Text>
</Col>
</Row>
<DraggableArea
onChange={(initialTags) => this.setState({ initialTags })}
isList
tags={this.state.initialTags}
render={({ tag }) => (
<Row>
<Col span={8}>
<Button style={{ width: '100%' }}>{tag.name}</Button>
</Col>
<Col span={16}>
<Select
placeholder="Select a location to Return Flow"
style={{ width: '100%' }}
>
{this.getReturnDropdown(tag.name).map((tagName) => (
<Option
onChange={
this.setState(prevState => ({
initialTags: prevState.initialTags.map(eachItem =>
eachItem.name === tag.name
? { ...eachItem, return: tagName.name }
: eachItem)
}))
}
value={tagName.name}>{tagName.name}
</Option>
))}
</Select>
</Col>
</Row>
)}
></DraggableArea>
</Modal>
Array of objects for initialTags:
[
{
"name": "AQS",
"projectId": "1",
"projectName": proj1,
"return":[]
},
{
"name": "ED",
"projectId": 2,
"projectName": proj2,
"return":[]
},
{
"name": "PAID",
"projectId": 3,
"projectName": proj3,
"return":[]
},
{
"name": "QS",
"projectId": 4,
"projectName": proj4,
"return":[]
}
]
Error Message in console
The Modal does not open up and code goes into inifinite looping.
I dynamically create checkboxes in item forms, when I submit a form in item forms, I get an array of checkbox IDs. How can I either bind true / false to these IDs or overwrite the object, create a new one or something else by clicking on the checkbox? How to understand which checkbox was clicked and bind a value to it true / false?
const [checkboxData, setCheckboxData] = useState(undefined);
useEffect(() => {
(async () => {
const data = await request(`/document-type/typeDocuments`, { method: 'get', cancelTokenKey: 'typeDocuments' });
if (data) { setCheckboxData(data.content); }
})();
}, []);
function onChangeCheckbox(checkedValues) {
setCheckboxData(prevState => {
prevState.map(item => ...)
}) // tried this, but then when you click on the checkbox everything breaks
}
<Form
onFinish={onFormFinish}
onFinishFailed={onFormFailed}
>
<Form.Item
name="typesDocuments"
valuePropName="checked"
>
<Checkbox.Group style={{ width: '100%' }}>
<Row>
{checkboxData?.map(item => <Col span={12}><Checkbox key={item?.id} value={item?.id} onChange={onChangeCheckbox}>{item?.Name}</Checkbox></Col>)}
</Row>
</Checkbox.Group>
</Form.Item>
<Form.Item>
<Space style={{ display: 'flex', justifyContent: 'end' }}>
<Button type="primary" htmlType="submit"> {intl("modal-btn-ok")} </Button>
</Space>
</Form.Item>
</Form>
For ease of viewing, I created an example of a project in a
sandbox
this problem may have already been solved but the examples that I found did not help me much.
downshift version:6.1.0
node version:14.15.4
npm version:6.14.10
react version: 17.0.1
What you did:
Tried to show an object attribute on input field, but i want save entire object
What happened:
The object is saved well but I can't show only one property in the input
<Field
name={`${name}.product`}
items={productList}
index={index}
component={DownShiftInput}
placeholder="Name"
/>;
const itemToString = item => {
return item ? item : '';
};
const DownShiftInput = ({
input,
meta,
placeholder,
items,
index,
...rest
}) => (
<Control name={placeholder} my={4}>
<FormLabel htmlFor={placeholder}>{placeholder}</FormLabel>
<Downshift
{...input}
onInputValueChange={inputValue => {
input.onChange(inputValue);
}}
itemToString={itemToString}
selectedItem={input.value}
>
{({
getInputProps,
getItemProps,
getLabelProps,
isOpen,
inputValue,
highlightedIndex,
selectedItem,
}) => {
const filteredItems = matchSorter(items, inputValue, {
keys: ['name'],
maxRanking: matchSorter.rankings.STARTS_WITH,
});
return (
<div className="downshift" style={{ position: 'relative' }}>
<Input
{...getInputProps({
name: input.name,
placeholder,
})}
/>
{isOpen && !!filteredItems.length && (
<div
className="downshift-options"
style={{
background: 'white',
position: 'absolute',
top: '100%',
left: 15,
right: 0,
zIndex: 4,
}}
>
{filteredItems.map((item, index) => {
return (
<div
{...getItemProps({
key: item.id,
index,
item,
style: {
backgroundColor:
highlightedIndex === index ? 'lightgray' : 'white',
fontWeight: selectedItem === item ? 'bold' : 'normal',
},
})}
>
{item.name}
</div>
);
})}
</div>
)}
</div>
);
}}
</Downshift>
<Error name={placeholder} />
</Control>
);
Thanks!
The solution for the user lcordier42 on downshift github:
<Input {...getInputProps({ name: input.name, placeholder, value: selectedItem.name, })} />
Using codes from the example: https://ant.design/components/table/#components-table-demo-custom-filter-panel
getColumnSearchProps = dataIndex => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
ref={node => {
this.searchInput = node;
}}
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
style={{ width: 188, marginBottom: 8, display: 'block' }}
/>
<Space>
<Button
type="primary"
onClick={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button onClick={() => this.handleReset(clearFilters)} size="small" style={{ width: 90 }}>
Reset
</Button>
</Space>
</div>
),
filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
onFilter: (value, record) =>
record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => this.searchInput.select());
}
},
render: text =>
this.state.searchedColumn === dataIndex ? (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[this.state.searchText]}
autoEscape
textToHighlight={text.toString()}
/>
) : (
text
),
});
Error Uncaught TypeError: Cannot read property 'toString' of undefined thrown when trying to pass nested values in the ANTD Table:
<Table bordered size='small' dataSource={data} rowKey='_id'>
....
<Column
title='Name'
dataIndex={['profile', 'name']}
{...this.getColumnSearchProps(['profile', 'name'])}
/>
....
</Table>
Here is how the structure of the data (dataSource) for the table:
[
{_id: 'xxx1', profile : { name : 'username1' }, roles: ['xxx1']},
{_id: 'xxx2', profile : { name : 'username2' }, roles: ['xxx2']}
]
As per outlined in the documentation: https://ant.design/components/table/#Migrate-to-v4 :
Besides, the breaking change is changing dataIndex from nest string
path like user.age to string array path like ['user', 'age']. This
help to resolve developer should additional work on the field which
contains ..
hence the dataIndex={['profile', 'name']}, but this is not the same case for the getColumnSearchProps.
Anyone can help?
Since the dataIndex could be an array now, you need to take care of that case as well.
An example is provided below:
You basically have to
Replace
record[dataIndex]
with
get(record, dataIndex) // import get from "lodash.get";
and
this.state.searchedColumn === dataIndex
with
isequal(this.state.searchedColumn, dataIndex) // import isequal from "lodash.isequal";
I am using react Ref in custom input field but it is not getting input content.
I am creating some ref in React class component and using them in form custom input field refs. then i have a button in the form which has "onClick" event to get that data. I am always getting undefined in console when i am getting "ref.current.value"
class LandingAfterSignIn extends React.Component {
titleTextFieldRef = React.createRef();
shortDescriptionMetaTextFieldRef = React.createRef();
longDescriptionMetaTextFieldRef = React.createRef();
imageFieldRef = React.
createRef();
webFieldRef = React.createRef();
contactTextFieldRef = React.createRef();
constructor(props) {
super(props);
// we use this to make the card to appear after the page has been rendered
this.state = {
cardAnimaton: "cardHidden",
isLoading: false,
error: null
};
// super(props);
this.state = {
errors: []
};
}
render() {
let image= require("assets/img/landing-bg.jpg") ;
const { classes, ...rest } = this.props;
//let titleTextFieldRef= this.titleTextFieldRef ;
//let shortDescriptionMetaTextFieldRef = this.shortDescriptionMetaTextFieldRef ;
return (
<div
className={classes.pageHeader}
style={{
backgroundImage: "url(" + image + ")",
backgroundSize: "cover",
backgroundPosition: "top center",
width: "100%"
}}
>
<Header
color="transparent"
routes={dashboardRoutes}
brand="StockNap"
rightLinks={<HeaderLinks />}
fixed
changeColorOnScroll={{
height: 400,
color: "white"
}}
{...rest}
/>
<div>
<div className={classes.container}>
<div className={classes.container}>
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<h1 className={classes.title}>Welcome {firebase.auth().currentUser.displayName} </h1>
<FirebaseDatabaseMutation type="push" path="user_bookmarks">
{({ runMutation }) => (
<form>
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="Company Name/Title"
id="titleTextField"
formControlProps={{
fullWidth: true
}}
inputRef={this.titleTextFieldRef}
>
</CustomInput>
</GridItem>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="short Description"
id="shortDescription"
formControlProps={{
fullWidth: true,
className: classes.textArea
}}
inputRef={this.shortDescriptionMetaTextFieldRef}
/>
</GridItem>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="long Description"
id="longDescription"
formControlProps={{
fullWidth: true,
className: classes.textArea
}}
inputProps={{
multiline: true,
rows: 2
}}
inputRef={this.longDescriptionMetaTextFieldRef}
/>
</GridItem>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="contact"
id="contactTextField"
formControlProps={{
fullWidth: true,
}}
inputRef={this.contactTextFieldRef}
/>
</GridItem>
<Button
style={{
width: 50,
height: 50,
alignSelf: "center",
background: "#039BE5",
color: "white"
}}
variant="fab"
type="submit"
onClick={async ev => {
console.log("submit") ;
ev.preventDefault();
ev.stopPropagation();
ev.nativeEvent.stopImmediatePropagation();
const titleTextField = get(
this.titleTextFieldRef,
"current.value",
""
);
const shortDescriptionMetaTextField = get(
this.shortDescriptionMetaTextFieldRef,
"current.value",
""
);
const longDescriptionkMetaTextField = get(
this.longDescriptionMetaTextFieldRef,
"current.value",
""
);
const contactTextField = get(
this.contactTextFieldRef,
"current.value",
""
);
console.log(this.titleTextFieldRef);
console.log(this.shortDescriptionMetaTextFieldRef);
await runMutation({
titleTextField: titleTextField,
shortDescriptionMetaTextField: shortDescriptionMetaTextField,
created_at: firebase.database.ServerValue.TIMESTAMP,
updated_at: firebase.database.ServerValue.TIMESTAMP
});
set(this.titleTextFieldRef, "current.value", "");
set(this.shortDescriptionMetaTextFieldRef, "current.value", "");
}}
>
+
</Button>
</GridContainer>
</form>
)}
</FirebaseDatabaseMutation>
</GridItem>
</GridContainer>
</div>
</div>
</div>
<div className={classNames(classes.main, classes.mainRaised)}>
<div className={classes.container}>
</div>
</div>
<br/>
<Footer />
</div>
);
}
}
I want to add one more information , customInput are functional components
i want reference from parents to go into that
function CustomInput({ ...props }) {
const {
classes,
formControlProps,
labelText,
id,
labelProps,
inputProps,
error,
white,
inputRootCustomClasses,
success
} = props;
const labelClasses = classNames({
[" " + classes.labelRootError]: error,
[" " + classes.labelRootSuccess]: success && !error
});
const underlineClasses = classNames({
[classes.underlineError]: error,
[classes.underlineSuccess]: success && !error,
[classes.underline]: true,
[classes.whiteUnderline]: white
});
const marginTop = classNames({
[inputRootCustomClasses]: inputRootCustomClasses !== undefined
});
const inputClasses = classNames({
[classes.input]: true,
[classes.whiteInput]: white
});
var formControlClasses;
if (formControlProps !== undefined) {
formControlClasses = classNames(
formControlProps.className,
classes.formControl
);
} else {
formControlClasses = classes.formControl;
}
return (
<FormControl {...formControlProps} className={formControlClasses}>
{labelText !== undefined ? (
<InputLabel
className={classes.labelRoot + " " + labelClasses}
htmlFor={id}
{...labelProps}
>
{labelText}
</InputLabel>
) : null}
<Input
classes={{
input: inputClasses,
root: marginTop,
disabled: classes.disabled,
underline: underlineClasses
}}
id={id}
{...inputProps}
/>
</FormControl>
);
}
From the official docs:
In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props.
Your first inclination may be to use refs to “make things happen” in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to “own” that state is at a higher level in the hierarchy. See the Lifting State Up guide for examples of this.
Currently, you're exactly doing what you should not be doing: using refs to access component methods. That is anti-pattern and make your code suceptible to bugs like you're experiencing right now. You should either:
Lift the state up if you need to pass data from one component to another
Use state container libraries like Redux if you need to trigger actions
In either way I strongly advise you to refactor your code in either direction.