I am using redux-form in order to submit a new user signup page in my React Web App, but when I try to grab the input field values on the form of the parameter values, it comes back undefined in mapDispatchToProps. I have read many posts of people claiming their values variable comes back undefined, but that is not the case for me. Only when I console.log(values.[inputName]) do I get undefined. According to the documentation, I cannot tell that I am doing anything incorrectly.
Can someone please help determine what is causing values.email to be undefined? Thanks.
// presentational component
class Signup extends Component {
render() {
const { handleSubmit } = this.props;
return (
<div>
<h1>Signup</h1>
<form onSubmit={handleSubmit}>
<div>
<label>
Email:
<Field component="input" type="email" name="email" />
</label>
</div>
<div>
<label>
Password:
<Field component="input" type="password" name="password" />
</label>
</div>
<button type="submit" >Submit</button>
</form>
</div>
)
}
}
export default reduxForm({
form: 'Signup',
enableReinitialize: true
})(Signup)
// redux container component
const mapDispatchToProps = (dispatch) => {
return {
handleSubmit: (values) => {
console.log(values.email); // outputs undefined!
}
};
}
const SignupContainer = connect(
null,
mapDispatchToProps
)(Signup)
export default SignupContainer;
// reducer index.js
import { reducer as formReducer } from 'redux-form';
export const appReducer = combineReducers({
form: formReducer
})
// store.js
export const store = createStore(
appReducer,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
);
Side Note
I have noticed that whenever I first click the submit button on the form, the page refreshes (so I do not see the console.log). All proceeding times does not cause a refresh, but I am unsure as to why. Any explanation of this would be greatly appreciated, too.
Update
After changing my Router from HashHistory to BrowserRouter (react-router), the page refreshes every time I click submit. Seems that I have two problems now.
Related
I have a form that updates a product's information using a form with use:enhance and actions defined in +page.server.ts - However, whenever I submit the form, use:enhance resets the form elements and they all become blank, which is unexpected as the value for these elements is specified by $page.data.product, and the docs state that use:enhance runs invalidateAll.
Nonetheless, is there a way to stop this reset from occurring within the use:enhance function?
If you return a function from the use:enhance action, that function will be called when you get a response from the form submit. This function in turn gets an update function which takes an option reset that you can give the value false to not reset the form:
<script>
import { enhance } from '$app/forms'
</script>
<form
method="POST"
use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
};
}}
>
<input type="text" name="name" />
<button>Submit</button>
</form>
As of #7326, you can pass a reset: false option to the update function in your enhance callback:
<script>
import { enhance } from '$app/forms';
</script>
<form
method="POST"
use:enhance={() => {
return async ({ update }) => {
update({ reset: false });
};
}}>
<input type="text" name="name" />
<button>Submit</button>
</form>
I have a very simple form for selecting a payment method built with Nextjs :
<form className='mx-auto max-w-screen-md' onSubmit={submitHandler}>
{
["Credit Card", "Paypal"].map((payment: string) => (
<div key={payment} className="mb-4">
<input
name='paymentMethod'
className='p-2 outline-none focus:ring-0'
id={payment}
type="radio"
checked={selectedPaymentMethod === payment}
onChange={() => setSelectedPaymentMethod(payment)}
/>
<label className='p-2' htmlFor={payment}>
{payment}
</label>
</div>
)
)}
<div className='mb-4, flex justify-between'>
<button
onClick={() => router.push('/shipping')}
type='button'
className='default-button'
>
Back
</button>
<button
className='primary-button'
>
Confirm
</button>
</div>
</form>
Then here's the javascript for the submitHandler :
const router = useRouter();
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState('');
const { state, dispatch } = useContext(Store);
const submitHandler = (e: any) => {
e.prevent.default();
dispatch({ type: 'SAVE_PAYMENT_METHOD', payload: selectedPaymentMethod });
Cookies.set({
...cart,
paymentMethod: selectedPaymentMethod
});
router.push('/placeorder');
};
The problem occurs when it comes to push the new route '/placeorder'.
Clicking on the confirmation button doesn't bring me to the '/placeorder' route but instead, it pushes a question mark, the input name and "=on" in the url, like so :
http://localhost:3000/payment?paymentMethod=on
It's a bit different if I don't select anything prior clicking on the confirm button, it just pushes a question mark :
http://localhost:3000/payment?
There's nothing I can find related to this issue on internet.
The piece of code is from a tutorial, I compared it , but it's the exact same thing.
replace e.prevent.default() with e.preventDefault()
I have a simple login form written in Angular 2 reactive (data-driven) template. It's working perfectly but when I refresh the page and browser fills e-mail+password (autocomplete), my form's valid property seems false.
But when I press any key or click anywhere in my page while form is invalid, angular updates some states (I guess) and my form become valid.
How can I trigger that state? How can I say angular "Hey, check my form again."? I can't trigger my own validation script because some validation messages are alerts. If I do this, when user open this page, he/she will see these alerts.
I remember, I use trigger('input') trick to update ng-model.
updateValueAndValidity() is what you are looking for. The document is here: AbstractControl. It can force recalculate the value and validation status of the control.
Here's a demo:
form.component.ts
export class FormComponent implements OnInit {
myform: FormGroup;
// explicit validation of each field
emailValid: AbstractControl;
passwordValid: AbstractControl;
constructor(private fb: FormBuilder) {
this.myform = fb.group({
'email': ['', Validators.required],
'password': ['', Validators.required],
});
this.emailValid = this.myform.controls['email'];
this.passwordValid = this.myform.controls['password'];
}
ngOnInit() {
this.myform.get('email').valueChanges.subscribe(()=>forceValidAgain());
this.myform.get('password').valueChanges.subscribe(()=>forceValidAgain());
}
forceValidAgain(){
this.emailValid.updateValueAndValidity();
this.passwordValid.updateValueAndValidity();
}
}
form.component.html
<form [formGroup]="myform" (ngSubmit)="onSubmit(myform.value)">
<div class="form-group">
<label for="email">Email</label>
<input type="email"
class="form-control"
id="email"
name="email"
[formControl]="myform.controls['email']"
[ngClass]="{'is-invalid': !emailValid.valid && emailValid.touched }">
<div
class="invalid-feedback"
*ngIf="emailValid.hasError('required')">
This field is required
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password"
class="form-control"
id="password"
name="password"
[formControl]="myform.controls['password']"
[ngClass]="{'is-invalid': !passwordValid.valid && passwordValid.touched}">
<div
class="invalid-feedback"
*ngIf="passwordValid.hasError('required')">
This field is required
</div>
</div>
</form>
I would suggest creating a method like onValueChanged (that is mentioned in Angular2 docs), bind it to your form, and execute it while the component is initialized. So the binding to the form changes should be done in this way:
this.form.valueChanges.subscribe((data) => {
this.onValueChanged(data)
});
And execution for example just the line after like this:
this.onValueChanged();
This execution should solve your problem via validation during component initialization.
I think that the method implementation from the docs (below) is pretty clear.
onValueChanged(data?: any) {
if (!this.heroForm) { return; }
const form = this.heroForm;
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
Docs I am referring to: https://angular.io/docs/ts/latest/cookbook/form-validation.html
I have a huge form with 20+ fields. i feel so much redundancy on the code i write now. What is the best way ?
<script>
new Vue({
data : {
user : {
first_name : "",
last_name : "",
username : "",
and 20+.........
}
}
});
</script>
<form>
<input name="first_name" v-model="first_name">
<input name="last_name" v-model="last_name">
<input name="username" v-model="username">
and 20+......... input fields
</form>
i feel something like this would be nice. the user object will be created dynamically.. is this possible ?
<script>
new Vue({
data : {
user : Object
}
});
</script>
<form v-model="user">
<input name="first_name">
<input name="last_name">
<input name="username">
and 20+......... input fields
</form>
Thank you in advance
Completely Redone in Vue 2
Your approach is the reverse of the usual Vue approach, in that you want to lay out your data structure in the view and have Vue pick it up, rather than laying it out in the data and having Vue render it. I can see how that would be desirable if you have a customized layout you want to achieve.
Unconventional needs require unconventional solutions, so this approach is unconventional. In particular, it is not generally recommended that a child component modify data in a parent.
That said, the approach is to create a component that will accept the form inputs as a slot and the parent object as a prop. In mounted, it gets all the input fields with name attributes and
Creates the member in the parent object, using $set
Sets a watch on the newly-created member
Adds an input event listener to complete the two-way binding
You would probably want to add more props to the component, to make the form itself more versatile, but this gives you the functionality you're looking for.
Vue.component('autoBindingForm', {
template: '<form><slot></slot></form>',
props: ['createIn'],
mounted() {
const inputs = this.$el.querySelectorAll('input[name]');
for (const i of inputs) {
this.$set(this.createIn, i.name, i.value);
this.$watch(`createIn.${i.name}`, (newVal, oldVal) => {
i.value = newVal;
});
i.addEventListener('input', (e) => {
this.createIn[i.name] = i.value;
});
}
}
});
const vm = new Vue({
el: '#app',
data: {
user: {}
}
});
// Testing that binding works both ways
setTimeout(() => {
vm.user.last_name = 'Set it';
}, 800);
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<auto-binding-form :create-in="user">
<input name="first_name" value="hi">
<input name="last_name">
<input name="username">
<div>{{Object.keys(user)}}</div>
<div>{{Object.values(user)}}</div>
</auto-binding-form>
</div>
How about
data: {
fields: { name: {v:''}, surname: {v:''}, ... }
}
and
<input v-for="(val, prop) in fields" :name="prop" v-model="val.v" />
?
https://jsfiddle.net/gurghet/dhdxqwjv/1/
This is my form
<form className="input-group" onSubmit={this.handleSubmit}>
<input className="form-control"
type="text"
placeholder="Insert name"
autofocus="true" />
<span className="input-group-btn">
<button type="submit" className={classNames}>Add</button>
</span>
</form>
This is my event handler:
handleSubmit(e) {
e.preventDefault();
let name = e.target[0].value;
if (name.length > 0) {
this.props.dispatch(createClassroom(name));
}
}
My question is:
what's the proper "redux way" to clearing the form after submitting it?
Do I need to dispatch a different action or should I use the existing createClassroom action?
Note: I'd rather not use redux-form package.
First, you have to make sure that the <input> is a controlled component by passing its respective value from the state:
const { classroom } = this.props;
// in return:
<input type="text" value={ classroom.name } />
Then, the form can be cleared by ideally submitting a RESET action that your classroom reducer acts upon:
const initialState = {};
function classroomReducer(state = initialState, action) {
switch (action.type) {
// ...
case 'RESET_CLASSROOM':
return initialState;
default:
return state;
}
}