I want to make a button behave like a checkbox in React, so the button that has been selected/clicked can return its value again or can be unselected again. But I haven't found a way.
Here's my code
const handleSelectedValue = (reason: any) => {
setSelectedValues(item => [...item, reason]);
};
// if value selected then change style
const checkId = (id: number) => {
return selectedValues.map(item => item.id).includes(id);
};
// inside jsx
{reasons.map(item => {
return (
<button
key={item.id}
className={`mr-2 mb-2 text-xs border-2 rounded-full font-semibold border-card-primary px-3 sm:px-5 py-2 ${checkId(item.id) ? 'text-white bg-base-primary opacity-75 border-none' : 'text-base-secondary'}`}
onClick={() => handleSelectedValue(item)}
>
{item.reason}
</button>
);
})}
This code works, but the selected value cannot be unselect. Thank you.
You have to check the existence of reason in the array. If it exists remove it else add it to list. You always add it.
const handleSelectedValue = (values) => {
setSelectedValues((item) => {
let idx = item.findIndex((itm) => itm.id === values.id);
if (idx !== -1) {
return item.filter(itm => itm.id !== values.id)
} else {
return [...item, values];
}
});
};
Related
I try to get data from a textarea in a react functional component, I'using Reducer hook to fetch data from the form, i use reducer to get data from the dom I don't understand what I've done wrong help would be very appreciated.
import Success from "../../components/singleElements/Success"
import Error from "../../components/singleElements/Error"
import { useQueryClient, useMutation } from "react-query"
import { addOnePub, getPub } from "../../lib/helper"
import { useReducer } from "react"
export default function Insertpub(){
//I use this reducer to fetch the data
const formReducer = (state, event) => {
return {
...state,
[event.target.name]: event.target.value,
};
};
//then get the textfield changes from here
const [formData, setFormData] = useReducer(formReducer, {});
const queryClient = useQueryClient()
const addMutation = useMutation(addOnePub, {
onSuccess : () => {
queryClient.prefetchQuery('pub', getPub)
}
})
const handleSubmit = (e) => {
e.preventDefault();
if(Object.keys(formData).length == 0) return console.log("Don't have Form Data");
let {pub} = formData;
const model = {
pub
}
addMutation.mutate(model)
console.log("the data is correctly inserted")
}
if(addMutation.isLoading) return <div>Loading!</div>
if(addMutation.isError) return <Error message={addMutation.error.message}></Error>
if(addMutation.isSuccess) return <Success message={"Added Successfully"}></Success>
//When I insert a value in this text area it returns null object
return (
<form onSubmit={handleSubmit}>
<textarea
className="bg-gray-200 w-full rounded-lg shadow border p-2"
rows="5"
placeholder="Ecrivez votre publication ici"
OnChange={setFormData}
name="pub"
id="pub"
></textarea>
<div className="w-full flex flex-row flex-wrap mt-3">
<div className="w-2/3">
<button
type="submit"
className="float-right bg-indigo-400 hover:bg-indigo-300 text-white p-2 rounded-lg"
>
Publier
</button>
</div>
</div>
</form>
)
}
Normally when I submit the form it should return value populated from the reducer hook but i got anything
It could be because of the typo in the capitalization (case matters):
OnChange={setFormData}
Should be
onChange={setFormData}
When I start the npm code for the cart component is not working and displayed the blank page
map function did not work. When I comment the part which is not working then another component like the header is displayed
import React from 'react';
import Header from './Front/Header/Header';
const Cart = (props ) => {
const {cartitems} =props;
const{handleAddProduct}=props;
const {handleRemoveProduct}=props;
return (
<>
<Header/>
<div className="cart-items">
<div className="cart-items-header"> cartitems</div>
{!cartitems?.length ? (
<div className="cart-items-empty"> No items added in cart</div>
) : null}
<div>
//this part of code is not working
{cartitems.map((item) => (
<div key={item.id}>
<div>
<img className="cart-items-image"
src={item.image}
alt={item.name} />
</div>
<button className='cart-items-add' onClick={()=>handleAddProduct(item)}>+</button>
<button className='cart-items-remove' onClick={()=>handleRemoveProduct(item)}>-</button>
<div className='cart-items-price'>{item.quantity}* ${item.price}</div>
</div>
))}
</div>
</div>
</>
);
}
export default Cart;
here is the code of app.js in this code I got an error that cartitems.find is not a function plz let me know how to fix this issue
const { productitems } = data;
const [cartitems, setCartItems] = useState([]);
const { user } = useContext(UserContext);
const History = useHistory();
const handleAddProduct = (product) => {
// console.log(product);
const ProductExist = cartitems.find((item) => item.id === product.id)
// console.log(ProductExist);
// setCartItems(ProductExist);
if (ProductExist) {
setCartItems(
cartitems.map((item )=> item.id ===product.id ?
{...ProductExist ,quantity:ProductExist.quantity +1}:item)
)
}
else {
setCartItems([...cartitems,{...product,quantity:1}])
console.log('ni gya');
}
}
const handleRemoveProduct = (product) => {
const ProductExist = cartitems.find((item) => item.id === product.id);
if (ProductExist.quantity === 1) {
setCartItems(cartitems.filter((item) => item.id !== product.id));
}
else {
setCartItems(
cartitems.map((item) => item.id === product.id ?
{ ...ProductExist, quantity: ProductExist.quantity - 1 }
: item)
);
}
}
You can see there is a check above the code you commented out:
{
!cartitems?.length ? (
<div className="cart-items-empty">No items added in cart</div>
) : null
}
It checks to make sure cartitems is an array before attempting to use map() on it. In the ternary statement, instead of returning null if cartitems is empty, you should return the map function so it would read:
{!cartitems?.length ? (
<div className="cart-items-empty"> No items added in cart</div>
) : cartitems.map(item => (
// template code...
))}
I have found a code for a material table that accepts a list as input and applies pagination, sorting and filtering on it. The thing is I need to find a way to extract the data from the row onClick and redirect the page to a new route along with those data. How can I do that?
In the component, I call the table as follows:
export default function ViewAllUsers() {
const [filterFn, setFilterFn] = useState({ fn: items => { return items; } })
const records = ....//List of records
const {
TblContainer,
TblHead,
TblPagination,
recordsAfterPagingAndSorting
} = useTable(records, headCells, filterFn);
const handleSearch = e => {
let target = e.target;
//Handle search
}
return (
<>
<Paper className={classes.pageContent}>
<Toolbar>
<Controls.Input onChange={handleSearch}/>
</Toolbar>
<TblContainer>
<TblHead />
<TableBody>
{
recordsAfterPagingAndSorting().map(item =>
(<TableRow key={item.id}>
<TableCell>{item.id}</TableCell>
<TableCell>{item.fullName}</TableCell>
</TableRow>)
)
}
</TableBody>
</TblContainer>
<TblPagination/>
</Paper>
}
and the useTable hook is:
export default function useTable(records, headCells, filterFn) {
const pages = [5, 10, 25]
const [page, setPage] = useState(0)
const [rowsPerPage, setRowsPerPage] = useState(pages[page])
const [order, setOrder] = useState()
const [orderBy, setOrderBy] = useState()
const TblContainer = props => (
<Table className={classes.table}>
{props.children}
</Table>
)
const TblHead = props => {
const handleSortRequest = cellId => {
//Some code
}
return (<TableHead>
<TableRow>
{
headCells.map(headCell => (
<TableCell key={headCell.id}
sortDirection={orderBy === headCell.id ? order : false}>
{headCell.disableSorting ? headCell.label :
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={() => { handleSortRequest(headCell.id) }}>
{headCell.label}
</TableSortLabel>
}
</TableCell>))
}
</TableRow>
</TableHead>)
}
const TblPagination = () => (
<TablePagination
component="div"
page={page}
rowsPerPageOptions={pages}
rowsPerPage={rowsPerPage}
count={records.length}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
id="TablePagination"
/>
)
return {
TblContainer,
TblHead,
TblPagination,
recordsAfterPagingAndSorting
}
}
You can simply use an onClick handler to pass the item data through it:
export default function ViewAllUsers() {
const [filterFn, setFilterFn] = useState({ fn: items => { return items; } })
const records = ....//List of records
const {
TblContainer,
TblHead,
TblPagination,
recordsAfterPagingAndSorting
} = useTable(records, headCells, filterFn);
const handleSearch = e => {
let target = e.target;
//Handle search
}
const handleItemClick = item => {
//Redirect to new route from here with the item data
}
return (
<>
<Paper className={classes.pageContent}>
<Toolbar>
<Controls.Input onChange={handleSearch}/>
</Toolbar>
<TblContainer>
<TblHead />
<TableBody>
{
recordsAfterPagingAndSorting().map(item =>
(<TableRow key={item.id} onClick={() => handleItemClick(item)}>
<TableCell>{item.id}</TableCell>
<TableCell>{item.fullName}</TableCell>
</TableRow>)
)
}
</TableBody>
</TblContainer>
<TblPagination/>
</Paper>
</>
)
}
Iam using Ant Design for React Js UI. Am using Tree component to show up in the list. I also have 2 button to expand and collapse the Tree list. I use the defaultExpandAll prop to manage this.
On the expand and collapse button click i set a bool to true and false respectively.
Button it doesn't expand on the button click.
If I set True initially to that flag state it works.
Is there any work Around.
I have 2 components. (Expand and collapse button are in parent component)
**Parent Component**
setExpandOrCollapse(value) {
this.setState({ expandOrCollapse: value });
}
<HeaderRow>
<Button onClick={() => this.setExpandOrCollapse(true)}>Expand All</Button>
<Button onClick={() => this.setExpandOrCollapse(false)}>Collapse All</Button>
</HeaderRow>
<Card>
{ItemTree && (ItemTree.length > 0) ? (
<ItemTree
dataSource={ItemTree}
expandOrCollapse={expandOrCollapse}
/>
) : null }
</Card>
**Child Component**
<Tree
draggable={isDraggable}
defaultExpandAll={expandOrCollapse}
>
{loopitemNodes(dataSource)}
</Tree>
dataSource is obtained from Redux api call.
Is there any work around.
The states in Ant design which are prefixed with default only work when they are rendered for the first time (and hence the default).
For working out programmatic expand and collapse, you need to control the expansion of tree using expandedKeys and onExpand props.
import { flattenDeep } from "lodash";
class Demo extends React.Component {
state = {
expandedKeys: []
};
constructor(props) {
super(props);
this.keys = this.getAllKeys(treeData);
}
getAllKeys = data => {
// This function makes an array of keys, this is specific for this example, you would have to adopt for your case. If your list is dynamic, also make sure that you call this function everytime data changes.
const nestedKeys = data.map(node => {
let childKeys = [];
if (node.children) {
childKeys = this.getAllKeys(node.children);
}
return [childKeys, node.key];
});
return flattenDeep(nestedKeys);
};
onExpand = expandedKeys => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
this.setState({
expandedKeys
});
};
renderTreeNodes = data =>
data.map(item => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode key={item.key} {...item} />;
});
expandAll = () => {
this.setState({
expandedKeys: this.keys
});
};
collapseAll = () => {
this.setState({
expandedKeys: []
});
};
render() {
return (
<Fragment>
<button onClick={this.expandAll}>Expand All</button>
<button onClick={this.collapseAll}>Collapse All</button>
<Tree onExpand={this.onExpand} expandedKeys={this.state.expandedKeys}>
{this.renderTreeNodes(treeData)}
</Tree>
</Fragment>
);
}
}
Codesandbox
class Demo extends React.Component {
state = {
expandedKeys: ["0-0-0", "0-0-1"],
autoExpandParent: true,
selectedKeys: []
};
onExpand = expandedKeys => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
this.setState({
expandedKeys,
autoExpandParent: false
});
};
onSelect = (selectedKeys, info) => {
console.log("onSelect", info);
this.setState({ selectedKeys });
};
renderTreeNodes = data =>
data.map(item => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode key={item.key} {...item} />;
});
onExpandAll = () => {
const expandedKeys = [];
const expandMethod = arr => {
arr.forEach(data => {
expandedKeys.push(data.key);
if (data.children) {
expandMethod(data.children);
}
});
};
expandMethod(treeData);
this.setState({ expandedKeys });
};
onCollapseAll = () => {
this.setState({ expandedKeys: [] });
};
render() {
return (
<Fragment>
<Button onClick={this.onExpandAll} type="primary">
ExpandAll
</Button>
<Button onClick={this.onCollapseAll} type="primary">
CollapseAll
</Button>
<Tree
onExpand={this.onExpand}
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
selectedKeys={this.state.selectedKeys}
>
{this.renderTreeNodes(treeData)}
</Tree>
</Fragment>
);
}
}
please refer to the Codesandbox link
I have a Mat-Table that i want to filter. I have 1 set of checkboxes of the same variable that work. but when i wanted to add another set it won't filter the table by them as it seems it still searches for on the same column.
This is my component ngOnInit :
ngOnInit() {
this.marinService.getAllContainers().subscribe((result) => {
//Data
this.dataSource = new MatTableDataSource(result);
//Paginator
this.dataSource.paginator = this.paginator;
//AutoFilter Form 1st page
this.clientType = this.route.snapshot.queryParamMap.get('clientType');
this.storageType= this.route.snapshot.queryParamMap.get('storageTypes');
console.log('The Client name is : '+this.clientType+' '+'The storage Facility is : '+this.storageType);
//snapShot CheckBox Filter
this.dataSource.filterPredicate = (paramData: Container, paramFilter: any) => {
return paramFilter.split(',').every((itemParam: any) => paramData.LQOCH_SHM_LOEZI_QTSR.indexOf(itemParam) !== -1);
};
this.filterParamasBoxes.subscribe((newFilterValue: any[]) => {
this.dataSource.filter = newFilterValue.join(',');
});
//CheckBoxFilter
this.dataSource.filterPredicate = (data: Container, filter: any) => {
return filter.split(',').every((item: any) => data.SOG_MCOLH.indexOf(item)!== -1);
};
this.filterCheckboxes.subscribe((newFilterValue: any[]) => {
this.dataSource.filter = newFilterValue.join(',');
});
});
}
This are the functions :
addFilter(change: MatCheckboxChange) {
if (this.filterCheckboxes.value.some((a: any) => a === change.source.value)) {
this.filterCheckboxes.next(this.filterCheckboxes.value.filter((a: any) => a !== change.source.value));
} else {
this.filterCheckboxes.next(this.filterCheckboxes.value.concat(change.source.value));
}
}
paramFilter(changeParam: MatCheckboxChange){
if (this.filterParamasBoxes.value.some((a: any) => a === changeParam.source.value)) {
this.filterParamasBoxes.next(this.filterParamasBoxes.value.filter((a: any) => a !== changeParam.source.value));
} else {
this.filterParamasBoxes.next(this.filterParamasBoxes.value.concat(changeParam.source.value));
}
}
and this is my checkboxes html :
<!--Check Box Search-->
<div class="CheckBoxStyle">
<mat-checkbox class="CheckBoxClass" value="RE" (change)="addFilter($event)">refeer</mat-checkbox>
<mdb-icon class="IconClass" fas icon="snowflake"></mdb-icon>
<br>
<br>
<mat-checkbox class="CheckBoxClass" value="RG" (change)="addFilter($event)" >dry</mat-checkbox>
<mdb-icon class="IconClass" fas icon="tint" matBadge="22" matBadgePosition="above after"></mdb-icon>
<br>
<br>
<mat-checkbox class="CheckBoxClass" value="OT" (change)="addFilter($event)">Open Top</mat-checkbox>
<mdb-icon class="IconClass" fas icon="box-open"></mdb-icon>
<br>
<br>
<mat-checkbox class="CheckBoxClass" value="HC" (change)="addFilter($event)">Flat</mat-checkbox>
<mdb-icon class="IconClass" fas icon="grip-lines"></mdb-icon><br><br>
<mat-checkbox class="CheckBoxClass" value="MRR" (change)="paramFilter($event)">MRR</mat-checkbox>
</div>
Adding a working stackblitz link - > https://stackblitz.com/edit/marin-project-stackblitz
you wont be able to see any data as its an internal API.
I think the problem is with the filter in the NgOnInit as its a method of the MatTable in angular. so it might only be able to do 1 at a time and not more
then that.