How to use search filter in sidenav bar in react js - javascript

I want to use search filter in sidebar so i created search filter and implemented but its now working shown only input field. and i stuck with how to map the items to search filter.
here i attached some of my working code:
state = {
search : ""
}
onchange = e =>{
this.setState({search : e.target.val })
}
const Menu = ({ resources, onMenuTap, translate }) => {
const {search}=this.state;
if (search !== "" && resources.name.toLowerCase().indexof(search.toLowerCase()) === -1 ){
return null
}
onchange = e =>{
this.setState({search : e.target.val })
}
return (
<Card className={classes.sidebarCard}>
{/* Search */}
<input placeholder="Search" onChange={this.onchange} />
//
....
//
);
};

for your onchange, try to use that :
onchange = e =>{
this.setState({search : e.target.value })
}
and in your search input :
{/* Search */}
<input placeholder="Search" value={this.state.search} onChange={(e) => this.onchange(e)} />

Because you're using functional component. You need to use useState hook:
const [search, setState] = useState("");
if (
search !== "" &&
resources.name.toLowerCase().indexof(search.toLowerCase()) === -1
) {
return null;
}
onchange = (e) => {
setState(e.target.val);
};
The attached sanbox code is based on your code. But it seems it doesn't render:

You are using functional component. So you need to change your code in this way:
const Menu = ({ resources, onMenuTap, translate }) => {
const [search, setSearch] = useState(""); //<-- this is your state
const onchange = e =>{
setSearch(e.target.val); //set the state
}
if (search !== "" && resources.name.toLowerCase().indexof(search.toLowerCase()) === -1 ){
return null
}
return (
<Card className={classes.sidebarCard}>
{/* Search */}
<input placeholder="Search" onChange={onchange} value={search} /> //<-- set value equal to search state
{permissions === "Administrator" && (
<>
<MenuItemLink
className={classes.MenuItemLink}
activeClassName={classes.active}
to="/_apps"
primaryText={translate("easyadmin.apps")}
leftIcon={
<AppsOutlined fontSize="small" className={classes.iconColor} />
}
onClick={onMenuTap}
/>
<MenuItemLink
className={classes.MenuItemLink}
activeClassName={classes.active}
to="/_entitys"
primaryText={translate("easyadmin.customobjects")}
leftIcon={
<SettingsOutlinedIcon
fontSize="small"
className={classes.iconColor}
/>
}
onClick={onMenuTap}
/>
)}
);
};

Related

Save Form values in ReactJS using checkboxes

I created a form component using react hook forms. The component is composed from a group of checkboxes and a text input. The text input appears when user click on the last checkbox custom. The idea of this one is: when the user will click on it appears a text input and the user can add a custom answer/option. Ex: if user type test within the input then when the user will save the form, there should appear in an array test value, but custom text should't be in the array. In my application i don't have access to const onSubmit = (data) => console.log(data, "submit");, so i need to change the values within Component component. Now when i click on submit i get in the final array the custom value. Question: how to fix the issue described above?
const ITEMS = [
{ id: "one", value: 1 },
{ id: "two", value: 2 },
{ id: "Custom Value", value: "custom" }
];
export default function App() {
const name = "group";
const methods = useForm();
const onSubmit = (data) => console.log(data, "submit");
return (
<div className="App">
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<Component ITEMS={ITEMS} name={name} />
<input type="submit" />
</form>
</FormProvider>
</div>
);
}
export const Component = ({ name, ITEMS }) => {
const { control, getValues } = useFormContext();
const [state, setState] = useState(false);
const handleCheck = (val) => {
const { [name]: ids } = getValues();
const response = ids?.includes(val)
? ids?.filter((id) => id !== val)
: [...(ids ?? []), val];
return response;
};
return (
<Controller
name={name}
control={control}
render={({ field, formState }) => {
return (
<>
{ITEMS.map((item, index) => {
return (
<>
<label>
{item.id}
<input
type="checkbox"
name={`${name}[${index}]`}
onChange={(e) => {
field.onChange(handleCheck(e.target.value));
if (index === ITEMS.length - 1) {
setState(e.target.checked);
}
}}
value={item.value}
/>
</label>
{state && index === ITEMS.length - 1 && (
<input
{...control.register(`${name}[${index}]`)}
type="text"
/>
)}
</>
);
})}
</>
);
}}
/>
);
};
demo: https://codesandbox.io/s/winter-brook-sml0ww?file=/src/Component.js:151-1600
Assuming that the goal is to keep all the selections in the same group field, which must be an array that logs the selected values in provided order, with the custom input value as the last item if specified, perhaps ideally it would be easier to calculate the values in onSubmit before submitting.
But since the preference is not to add logic in onSubmit, maybe an alternative option could be hosting a local state, run the needed calculations when it changes, and call setValue manually to sync the calculated value to the group field.
Forked demo with modification: codesandbox
import "./styles.css";
import { Controller, useFormContext } from "react-hook-form";
import React, { useState, useEffect } from "react";
export const Component = ({ name, ITEMS }) => {
const { control, setValue } = useFormContext();
const [state, setState] = useState({});
useEffect(() => {
const { custom, ...items } = state;
const newItems = Object.entries(items).filter((item) => !!item[1]);
newItems.sort((a, b) => a[0] - b[0]);
const newValues = newItems.map((item) => item[1]);
if (custom) {
setValue(name, [...newValues, custom]);
return;
}
setValue(name, [...newValues]);
}, [name, state, setValue]);
const handleCheck = (val, idx) => {
setState((prev) =>
prev[idx] ? { ...prev, [idx]: null } : { ...prev, [idx]: val }
);
};
const handleCheckCustom = (checked) =>
setState((prev) =>
checked ? { ...prev, custom: "" } : { ...prev, custom: null }
);
const handleInputChange = (e) => {
setState((prev) => ({ ...prev, custom: e.target.value }));
};
return (
<Controller
name={name}
control={control}
render={({ field, formState }) => {
return (
<>
{ITEMS.map((item, index) => {
const isCustomField = index === ITEMS.length - 1;
return (
<React.Fragment key={index}>
<label>
{item.id}
<input
type="checkbox"
name={name}
onChange={(e) =>
isCustomField
? handleCheckCustom(e.target.checked)
: handleCheck(e.target.value, index)
}
value={item.value}
/>
</label>
{typeof state["custom"] === "string" && isCustomField && (
<input onChange={handleInputChange} type="text" />
)}
</React.Fragment>
);
})}
</>
);
}}
/>
);
};
Ok, so after a while I got the solution. I forked your sandbox and did little changes, check it out here: Save Form values in ReactJS using checkboxes
Basically, you should have an internal checkbox state and also don't register the input in the form, because this would add the input value to the end of the array no matter if that value is "".
Here is the code:
import "./styles.css";
import { Controller, useFormContext } from "react-hook-form";
import { useEffect, useState } from "react";
export const Component = ({ name, ITEMS }) => {
const { control, setValue } = useFormContext();
const [state, setState] = useState(false);
const [checkboxes, setCheckboxes] = useState(
ITEMS.filter(
(item, index) => index !== ITEMS.length - 1
).map(({ value }, index) => ({ value, checked: false }))
);
useEffect(() => {
setValue(name, []); //To initialize the array as empty
}, []);
const [inputValue, setInputValue] = useState("");
const handleChangeField = (val) => {
const newCheckboxes = checkboxes.map(({ value, checked }) =>
value == val ? { value, checked: !checked } : { value, checked }
);
setCheckboxes(newCheckboxes);
const response = newCheckboxes
.filter(({ checked }) => checked)
.map(({ value }) => value);
return state && !!inputValue ? [...response, inputValue] : response;
};
const handleChangeInput = (newInputValue) => {
const response = checkboxes
.filter(({ checked }) => checked)
.map(({ value }) => value);
if (state) if (!!newInputValue) return [...response, newInputValue];
return response;
};
return (
<Controller
name={name}
control={control}
render={({ field, formState }) => {
return (
<>
{ITEMS.map((item, index) => {
return (
<>
<label>
{item.id}
<input
type="checkbox"
name={`${name}[${index}]`}
onChange={(e) => {
if (index === ITEMS.length - 1) {
setState(e.target.checked);
return;
}
field.onChange(handleChangeField(e.target.value));
}}
value={item.value}
/>
</label>
{state && index === ITEMS.length - 1 && (
<input
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
field.onChange(handleChangeInput(e.target.value));
}}
type="text"
/>
)}
</>
);
})}
</>
);
}}
/>
);
};

TextInput gets unfocused after typing each character

I'm using React to build a form and I'm trying to filter a list with the SearchInput (which works the same as TextInput) located in the child component Header. But everytime I type a character the SearchInput gets unfocused
function index() {
const list = [//data\\]
const [search, setSearch] = useState("");
const [filteredResults, setFilteredResults] = useState([]);
const searchItems = (searchValue) => {
setSearch(searchValue);
if (search !== "") {
const filteredData = partners.filter((item) => {
return Object.values(item)
.join("")
.toLowerCase()
.includes(search.toLowerCase());
});
setFilteredResults(filteredData);
} else {
setFilteredResults(partners);
}
};
const Header = () => (
<Box>
<SearchInput
placeholder="Search"
value={search}
onChange={(e) => searchItems(e.target.value)}
/>
</Box>
);
return (
<Parent
headerContent={<Header />}
>
<Box>
<Table data={search.length > 1 ? filteredResults : list} />
</Box>
</Parent>
);
}
export default index;
Oh, I think I can see the problem now - it's the way you're rendering the <SearchInput /> component. You're inadvertantly creating a new functional component on every render. Either inline the Header directly into the Parent control's headerContent property, or create an entirely separate component:
const Header = ({ search, onSearchChange }) => {
const handleChange = (e) => onSearchChange(e.target.value);
return (
<Box>
<SearchInput
placeholder="Search"
value={search}
onChange={handleChange}
/>
</Box>
);
}
function index() {
// ----- 8< -----
return (
<Parent
headerContent={<Header search={search} onSearchChange={searchItems} />}
>
{/* ... */}
</Parent>
);
}
While you're there, you have a subtle bug with your comparison - it looks like you're searching your partners effectively as a list of strings; but, since you're joining them, if you had partners with the names:
'one'
'two'
You're creating a search string as 'onetwo' - so searching for 'et' would match, even though you don't actually have a partner matching that. You can fix that by just checking each partner individually... something like:
const searchItems = (searchValue) => {
setSearch(searchValue);
if (search !== "") {
const searchValueLower = searchValue.toLowerCase();
const filteredData = partners.filter((item) => {
return Object.values(item)
.some(item => item.toLowerCase().includes(searchValueLower);
});
setFilteredResults(filteredData);
} else {
setFilteredResults(partners);
}
};

clear search box after onclick

I have a search box in my header. i can clear my state after searching but the input doesn't get cleared.
I'm purely using the Searchbox to generate a dropdown that contains links to their respective field. So the input field is purely used to mimic a searc
I tried targeting it with refs but when i finally reach the value i can't use the search anymore.
There is a ref for SearchBarHeader, SearchBox and SearchField. But i'm not sure if that is the correct way to do it.
clearSearchBar = () => {
this.searchBarHeader.current.searchBox.current.searchField.current.value = '';
};
and the code for the searchbox.
class Search extends Component {
state = {
organisationNames: [],
errorMessage: null,
};
searchField = React.createRef();
async componentDidMount() {
const organisations = await getSearch();
this.setState({
organisationNames: organisations,
});
}
searchHandler = (e) => {
const searchValue = e.target.value.toLowerCase();
if (!searchValue) {
this.props.clearSearchResult();
} else {
const result = this.state.organisationNames.filter((organisationName) => {
return organisationName.toLowerCase().includes(searchValue);
});
this.props.setSearchResult(result, () => {
if (this.props.searchResult.length === 0) {
this.setState({
errorMessage: "No Results...",
});
} else {
this.setState({
errorMessage: null,
});
}
});
}
};
clearSearchInput = () => {
this.props.clearSearchResult();
};
render() {
return (
<div className="search">
<div className="form-group">
<input
ref={this.searchField}
type="search"
placeholder="Search for company"
onChange={this.searchHandler}
/>
</div>
<div className="search-result-wrapper">
<ul className="search-results">
{this.props.searchResult === undefined ? (
<Skeleton />
) : (
this.props.searchResult.map((res, id) => {
return (
<Link
key={id}
to={"/r/" + res}
onClick={this.clearSearchInput}
>
<li className="search-item">{res || <Skeleton />} </li>
</Link>
);
})
)}
{this.state.errorMessage === null ? (
""
) : (
<li>{this.state.errorMessage}</li>
)}
</ul>
</div>
</div>
);
}
}
export default Search;
It seems to me that you're missing the "value" attribute on your input that makes it reactive to changes in your state. Grabbing one example from react docs, here's the ideal setup:
this.state = {value: ''};
(...)
handleChange(event) {
this.setState({value: event.target.value});
}
(...)
<input type="text" value={this.state.value} onChange={this.handleChange} />
By following the method above, you won't need to use refs to manually clear the input value. Once the form is submitted, you can simply clear your state...
this.setState({value: ''});
... and your input should be cleared.
Here's the link for the docs: https://reactjs.org/docs/forms.html
You are clearing the ref, not the state. There is also not a value attached to your input, so even if the state was cleared, it will not reflect.
You will of course be able to make the form data more dynamic, without having to set and keep companyName constant.
Here is a simple working example is here: https://codesandbox.io/s/flamboyant-voice-oj85u?file=/src/App.js
export default function App() {
const [formData, setFormData] = useState({ companyName: "" });
const handleChange = (e) => {
setFormData({ companyName: e.target.value });
};
const handleClear = () => {
setFormData({ companyName: "" });
};
return (
<div className="search">
<div className="form-group">
<input
type="search"
name="companyName"
value={formData.companyName}
placeholder="Search for company"
onChange={handleChange}
/>
<button onClick={handleClear}>Clear</button>
</div>
<pre>{JSON.stringify(formData, null, 2)}</pre>
</div>
);
}

React final form disables fields without redux

I have the following functions which renders a set of fields:
const renderFields = (data: CustomerDetails) => {
return Object.keys(data).map((s: string) => {
const key = s as keyof CustomerDetails
return Object.keys(data[key]).map(fieldKey => {
const name = `${key}.${fieldKey}`
const id = `customer-details-form-${fieldKey}`
return (
<FormItem key={name}>
<Label htmlFor={id}>{camelCaseToTitleCase(fieldKey)}</Label>
<Field name={`${key}.${fieldKey}.value`} validate={validate(fieldKey)}>
{props =>
<TextField
disabled={
data.contact[fieldKey] !== undefined
? data.contact[fieldKey].disabled
: true
}
// disabled={
// data.contact[fieldKey]?.disabled ?? true
// }
{...props}
data-bdd={`customer_details_field_${fieldKey}`}
id={id}
/>
}
</Field>
</FormItem>
)
})
})
}
however the disabled status is dependent on the redux structure at the moment. Is there a way to make fields disabled/enabled on click without having to dispatch an action saying which fields should be enabled or not?
Using local state here would be appropriate. Here's a simplified example:
function WrappedField(props){
const [disabled, setDisabled] = useState(false);
return <Field {...props} disabled={disabled} onClick={() => setDisabled(!disabled)} />
}
You can use the wrapped version wherever you would have used the Field component.

How to set max number of items that can be selected in react-select?

I am using CreatableSelect component from react-select. Now users can select as many items as they want, but I want users to select no more than 5 items. How to limit max number of options that can be selected?
<CreatableSelect
classes={classes}
styles={selectStyles}
textFieldProps={{
label: "Tags"
}}
options={suggestions}
components={components}
value={this.state.multi}
onChange={this.handleChange("multi")}
placeholder=""
isMulti
/>
I recommend you to use a combination of custom component Menu and isValidNewOption like the following code:
// For this example the limite will be 5
const Menu = props => {
const optionSelectedLength = props.getValue().length || 0;
return (
<components.Menu {...props}>
{optionSelectedLength < 5 ? (
props.children
) : (
<div>Max limit achieved</div>
)}
</components.Menu>
);
};
function App() {
const isValidNewOption = (inputValue, selectValue) =>
inputValue.length > 0 && selectValue.length < 5;
return (
<div className="App">
<Creatable
components={{ Menu }}
isMulti
isValidNewOption={isValidNewOption}
options={options}
/>
</div>
);
}
Here a live example.
The idea is to prevent user to access the options after the limit X (5 in the example) and also to prevent the enter keyboard event on create through isValidNewOption prop.
a very easy way to do this is:
<Select
value={tags}
onChange={(v) => v.length < 4 ? setTags(v): null}
isMulti
name='tags'
options={options}
className='basic-multi-select'
classNamePrefix='select'
/>
just add a simple ternary check for how many items you wants
<CreatableSelect
classes={classes}
styles={selectStyles}
options={this.state.multi.length > 4 ? this.state.multi : suggestions}
components={Components}
value={this.state.multi}
placeholder="Tags"
onChange={(values) => this.setState({ multi: values })}
isValidNewOption={isValidNewOption} //Look at Marked Answer
isMulti
/>
Main documentation on how to resolve this issue can be found here:
https://github.com/JedWatson/react-select/issues/1341
const MultiCreatable = ({ options, handleChange, handleCreate, value, maxOptions }) => {
return (
<CreatableSelect
isMulti
placeholder={placeholder}
onChange={handleChange}
options={value.length === maxOptions ? [] : options}
noOptionsMessage={() => {
return value.length === maxOptions ? 'You have reached the max options value' : 'No options available' ;
}}
onCreateOption={handleCreate}
value={value}
/>
)
}
I am sharing my complete working component I think it can help>>
import React, { useState } from 'react';
import Select from 'react-select';
import makeAnimated from 'react-select/animated';
const animatedComponents = makeAnimated();
const ReactSelect = ({ data }) => {
const maxOptions = 5;
const [selectedOption, setSelectedOption] = useState([]);
const handleTypeSelect = e => {
setSelectedOption(e);
};
return (
<Select
onChange={handleTypeSelect}
getOptionLabel={x => x.name}
getOptionValue={x => x.slug}
components={animatedComponents}
isMulti
options={selectedOption.length === maxOptions ? [] : data}
noOptionsMessage={() => {
return selectedOption.length === maxOptions
? 'You have reached the max options value'
: 'No options available';
}}
label='tags'
/>
);
};
export default ReactSelect;
I found the more simple and clean way, without extra manipulations.
This way based on disabling an input component of 'react-select'.
Take a closer look at the parameter inputProps.
It can look like:
import Select from 'react-select';
import useField from 'client/components/hooks/useField';
const MultiSelect = ({
async,
creatable,
maxItems,
...restProps,
}) => {
const selectProps = {
...restProps,
// "inputProps: {disabled: boolean}" - our goal
...(typeof maxItems === 'number' && maxItems === restProps.value?.length ? {inputProps: {disabled: true}} : {})
};
const creatableTag = async ? Select.CreatableAsync : Select.Creatable;
const SelectTag = creatable ? creatableTag : selectTag;
return (
<div>
<SelectTag {...selectProps} />
</div>
);
};
const SomeComponentWithMultiSelect = () => {
const field = useField('data.name'); // field contains: {value: string[], ...}
const items = [
{
label: 'firstValue',
value: 1,
},
{
label: 'secondValue',
value: 2,
},
];
return (
<MultiSelect
items={items}
{...field}
creatable
maxItems={1} // {1} as our limit
/>
)
};
export default SomeComponentWithMultiSelect;
So you don't need to manage excess components.
For my case I used normal Select Component from react-select.
<Select
options={industries}
value={industry}
getOptionLabel={ x => x.id}
getOptionValue={ x => x.industry}
onChange={(e) => this.handleSelectChange(e, "industry")}
isMulti
/>
and handleSelectChange-
handleSelectChange = (e, name) => {
console.log(e)
if(e.length < 6){
return this.setState({
[name]: e
})
}
}
and state -
this.state = { industry: [] }

Categories

Resources