How to update Form options after submitting using useReducer - forms

i´m new at programming..
I'm coding a form that has a Select list of Options, which come from an array of objects (i named it initialState for useReducer use).
Now i have to use the useReducer hook to update those options when the form is submitted. The submitted option has to be eliminated after submitting.
I just started coding the hook, but i really don´t know what to do...
The app is rendering OK, and the "placeholder" initial state is showing the first option value. I'm okay with that.
Can you help me plase? Thanks!
import { useState, useReducer } from "react";
const handleSubmit = (e) => {
e.preventDefault();
}
const BookingForm = () => {
const initialState = [
{value: "17:00"},
{value: "17:30"},
{value: "18:00"},
{value: "18:30"},
{value: "19:00"},
{value: "19:30"}]
const reducer = (state, action) => {
?????? }
const [state, dispatch] = useReducer(reducer, initialState);
const [option, setOption] = useState({initialState});
return(
<div className="form_div">
<form className="form" onSubmit={handleSubmit}>
<label htmlFor="res-time" className="label">Choose time
<select id="res-time " className="input"
value={option}
onChange={(e) => setOption(e.target.value)}>
{initialState.map(item => {
return (<option key={item.value} value={item.value}>{item.value}</option>);
})}
</select>
</label>
<input type="submit" value="Make Your reservation" className="button" onClick={() => dispatch({type: "selected option"})}/>
</form>
export default BookingForm;

Related

Redirect to next page after validating with yup

I have a form that take only one user input. Also i used react formik for the form and yup for validation.
I want the user to be taken to next page after the input is validated.
Validation is working but on click of the next page button does not take to next page.
Here is my code;
.
.
.
const Home = () => {
const navigate = useNavigate()
const initialValues = {
phoneNumber: ''
}
const onSubmit = values => {
console.log('form data', values)
navigate.push("/NextPage")
}
const validationSchema = Yup.object({
phoneNumber: Yup.string()
.min(11, 'Invalid phone number format!')
.max(11, 'Invalid phone number format!')
.required('Please enter your phone number!'),
})
return (
{/*...*/}
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}>
<Form className='phone-input'>
<Field
type='tel'
id='phone-input'
name='phoneNumber'
placeholder='0000-000-0000'
/>
<ErrorMessage name='phoneNumber'>
{
(errorMsg) => <div className='error'>{errorMsg}</div>
}
</ErrorMessage>
<button type='submit' id="phoneBtn" onClick={onSubmit}>Next Page</button>
</Form>
</Formik>
</div>
)
}
export default Home
I don't think you need to do .push() to navigate. Just use-
navigate("/NextPage")

material ui picker with formik with class component

I have #material-ui/pickers with Formik in my React Class Component whenever I am trying to change date & time from picker i am getting below error
TypeError : Cannot read property 'type' of undefined
Below is my code
// Handle fields change
handleChange = input => e => {
this.props.formikHandleChange(e);
this.setState({ [input]: e.target.value });
};
<MuiPickersUtilsProvider utils={MomentUtils}>
<DateTimePicker
label="Creation Date"
name={creationDate}
onChange={handleChange('creationDate')}
value={values.creationDate}
onBlur={formikHandleBlur}
/>
</MuiPickersUtilsProvider>
you can use formik setFieldValue
onChange={(date) =>
setFieldValue(
'creationDate',
date
)}
// Handle fields change
handleChange = (e, input) => {
this.props.formikHandleChange(e);
this.setState({ [input]: e.target.value });
};
<MuiPickersUtilsProvider utils={MomentUtils}>
<DateTimePicker
label="Creation Date"
name={creationDate}
onChange={(e) => handleChange(e,input)}
value={values.creationDate}
onBlur={formikHandleBlur}
/>
</MuiPickersUtilsProvider>
You can set the property with [e.target.name]: e.target.value

Angular Form Submit

In my departments.component.html, I have this:
Focus on the #form and calling of addDepartment(form.formValues)
<app-dialog-form *ngIf="showDialogBox" [(showDialogBox)]="showDialogBox" #form [values]="department">
Register Department
<div class="action" action>
<button class="ui button basic" (click)="onToggleDialogBox()">Cancel</button>
<button class="ui button primary medium" (click)="addDepartment(form.formValues)">Save</button>
</div>
</app-dialog-form>
The getting of values in my formValues is this:
get formValues(): any {
let data;
data = this.dialogForm.value;
return data;
}
And here is my code for addDepartment()
addDepartment(department: Department) {
console.log(department.name);
if (typeof department === 'object') {
this.departmentService
.registerDepartment(department)
.subscribe(
res => { alert(res);
//this.goBackToDepartmentListPage();
},
error => alert(error)
)
}
}
But everytime, I submit the form values and try printing in the console the input, I get this error
Cannot read property 'value' of undefined at
DialogFormComponent.get [as formValues]

Autocomplete - How can I set a default value?

Does anyone know how to add a default value to the Autocomplete component?
The component have a dataSource, and I'd like to load the page with a specific item already selected(e.g. fill the text field with the selected item's text and it's value already set).
Does anyone knows how? Big thanks for any help <3
You can achieve this by setting the appropriate state in componentDidMount, for example:
componentDidMount() {
// load your items into your autocomplete
// set your default selected item
this.setState({ allItems: [itemYouWantToSet], selectedItem: item.name, selectedItemId: item.id }
}
render() {
return (
<Autocomplete
value={this.state.selectedItem}
items={this.state.allItems}
getItemValue={(item) => item.name}
onSelect={(value, item) => {
this.setState({ selectedItem: value, selectedItemId: value, allItems: [item] });
}}
/>
)
}
Then your item is correctly selected from the list of available options when it loads.
I tried all the above solutions and nothing worked. Perhaps the API has changed since then.
I finally figured out a solution. It's not so elegant, but in principle it makes sense. In my case the options are objects. I just had to set the "value" prop using the exact item from my options array. This way componentDidMount and getOptionSelected aren't needed.
Autocomplete is wrapped inside another component in our case. This is the main code:
class CTAutoComplete extends React.PureComponent {
getSelectedItem(){
const item = this.props.options.find((opt)=>{
if (opt.value == this.props.selectedValue)
return opt;
})
return item || {};
}
render() {
return (
<Autocomplete
id={this.props.id}
className={this.props.className}
style={{ width: '100%' }}
options={this.props.options}
getOptionLabel={this.props.getOptionLabel}
renderInput={params => (
<TextField {...params} label={this.props.label} variant="outlined" />
)}
onChange={this.props.onChange}
value={this.getSelectedItem()}
/>
);
}
}
IMPORTANT: When setting "value", you have to make sure to put the null case " || {} ", otherwise React complains you are changing from an uncontrolled to controlled component.
you can provide the defaultValue prop for AutoComplete.
<Autocomplete
multiple
id="tags-outlined"
options={this.state.categories}
getOptionLabel={(option) => option.category_name}
onChange={this.handleAutocomplete}
defaultValue={'yourDefaultStringValue'} //put your default value here. It should be an object of the categories array.
filterSelectedOptions
renderInput={(params) => (
<TextField
fullWidth
{...params}
variant="outlined"
label="Add Categories"
placeholder="Category"
required
/>
}
/>
This approach works for me (using hooks):
First of all define the options you need in a variable:
const genderOptions = [{ label: 'M' }, { label: 'V' }];
Second you can define a hook to store the selected value (for example store it in session storage for when the page refreshes, or use useState directly):
const age = useSessionStorage('age', '');
Next you can define your Autocomplete as follows (notice the default values in value and getOptionLabel, if you omit those you'll get those controlled to uncontrolled warnings):
<Autocomplete
id="id"
options={ageOptions}
getOptionLabel={option => option.label || ""}
value={ageOptions.find(v => v.label === age[0]) || {}} // since we have objects in our options array, this needs to be a object as well
onInputChange={(_, v) => age[1](v)}
renderInput={params => (
<TextField {...params} label="Leeftijd" variant="outlined" />
)}
/>
It is tricky specially in case of you are using along with filter option which load API on every filter. I was able to load initial value by setting up within state and onInputChange option.
Below is code that work for me or click below link for full working demo.
https://codesandbox.io/s/smoosh-brook-xgpkq?fontsize=14&hidenavigation=1&theme=dark
import React, { useState, useEffect } from "react";
import TextField from "#material-ui/core/TextField";
import Typography from "#material-ui/core/Typography";
import Autocomplete from "#material-ui/lab/Autocomplete";
export default function CreateEditStrategy({ match }) {
const [user, setUser] = useState({
_id: "32778",
name: "Magic User's Club!"
});
const [filter, setFilter] = useState("");
const [users, setUsers] = useState([]);
const [openAutoComplete, setOpenAutoComplete] = React.useState(false);
useEffect(() => {
(async () => {
//Will not filter anything for testing purpose
const response = await fetch(
`https://api.tvmaze.com/search/shows?q=${filter}`
);
const shows = await response.json();
setUsers(
shows.map((a, i) => {
return { _id: a.show.id, name: a.show.name };
})
);
})();
}, [filter]);
return (
<div>
<Typography variant="h6">Autocomplete</Typography>
<Autocomplete
open={openAutoComplete}
onOpen={() => setOpenAutoComplete(true)}
value={user}
inputValue={filter}
onClose={() => setOpenAutoComplete(false)}
onChange={(event, user) => {
if (user) setUser(user);
else setUser({ _id: "", name: "" });
}}
onInputChange={(event, newInputValue) => setFilter(newInputValue)}
getOptionSelected={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.name}
options={users}
renderInput={(params) => (
<TextField
{...params}
label="Asynchronous"
variant="outlined"
InputProps={{
...params.InputProps
}}
/>
)}
/>
</div>
);
}
Call your component like this
<SelectCountryAutosuggest searchText="My Default Value" />
Make sure you apply the default value to state on class load
class SelectCountryAutosuggest extends React.Component {
state = {
value: this.props.searchText, //apply default value instead of ''
suggestions: [],
};
...
}
The api docs suggest the best approach in the current version (June 2022) is to use value and isOptionEqualToValue.
So for example, if I have a list of users and am choosing which user this thing is assigned to:
const [assignedTo, setAssignedTo] = useState(initialOption);
return (<Autocomplete
options={users.map((i) => ({
label: i.name,
value: i._id,
}))}
isOptionEqualToValue={(o, v) => o.value === v.id}
value={assignedTo}
onChange={(evt, val) => setAssignedTo(val)}
renderInput={(params) => (
<TextField {...params} label="Assigned To" />
)}
/>);
We can setup initial value through value property of Autocomplete component
<Autocomplete
fullWidth={true}
label={'Location'}
margin={'noraml'}
multiple={false}
name={'location'}
getOptionSelected={useCallback((option, value) => option.value === value.value)}
value={formValues.location === '' ? {label: ''} : {label: formValues.location}}
options={location}
ref={locationRef}
onChange={useCallback((e, v) => handleInputChange(e, v))}
/>
You can use the searchText property.
<AutoComplete searchText="example" ... />
Try this...
componentWillReceiveProps(nextProps) {
let value = nextProps.value
if (value) {
this.setState({
value: value
})
}
}
onUpdateInput worked for me - for anyone looking through all this as I was
Have you tried setting the searchText prop dynamically? You can pass the value you want to set to the Autocomplete component as the searchText prop. Something like,
<Autocomplete
searchText={this.state.input.name} // usually value={this.state.input.name}
/>
By default, it'll have the initial value set into the TextField of the Autocomplete component but when the user makes any modifications, it calls up the Autocomplete options depending on the dataSource prop.

Form text diappears and comeing ab up again

How do I do that in symfony?
http://dorward.me.uk/tmp/label-work/example.html
When I click into the input field username. The text disappears. If I don't enter nothing and click somewhere else the text is comeing up again. How do I do that in symofny in lib/forms/.
Thanks for an advice!
Craphunter
This isnt symfony - its javascript ... just take a look at the source (uses jQuery)
HTML:
<div class="slim-control">
<label for="username"> Username </label>
<input name="username" id="username">
</div>
<div class="slim-control">
<label for="password"> Password </label>
<input name="password" id="password" type="password">
</div>
JavaScript:
(function () {
var nonEmpty = "non-empty";
var inputs = jQuery('.slim-control input');
var setLabelStyle = function setLabelStyle () {
var label = jQuery(this);
if (label.val().length) {
label.addClass(nonEmpty);
} else {
label.removeClass(nonEmpty);
}
};
inputs.focus(function () {
jQuery(this).addClass(nonEmpty);
});
inputs.blur(setLabelStyle);
inputs.each(setLabelStyle);
}());
So if you want to do this - you would need to add the javascript to the template (indexSuccess.php) for example - you would probably need to modify the ids / classes to meet your local needs
Updated
You could create the following form - this would match the above form (untested):
$this->form = new sfForm();
$this->form->setWidgets(array(
'username' => new sfWidgetFormInputText(),
'password' => new sfWidgetFormInputPassword()
));
Creating forms (indeed all of Symfony) is very well documented here -> http://www.symfony-project.org/gentle-introduction/1_4/en/10-Forms
'input' => new sfWidgetFormInputText(array(), array('size' => '100', 'maxlength' => '255', 'value' => 'Some text', 'onblur' =>"if(this.value == '') { this.value='Some text'}", 'onfocus' =>"if (this.value == 'Some text') {this.value=''}")),
This is much shorter! Be careful that "Some text" is identical!
If you need to pass more, take a look to SetWidgetsHtml in Symfony form: (slide down)
http://www.symfony-project.org/forms/1_4/en/01-Form-Creation