Formik - Is there a way to get fresh values object on Form change? - formik

I have a Formik form with onChange handler:
<Formik initialValues={values}>
{({ values }) => (
<Form onChange={() => passUpdatedValues(values)}>
// ...
</Form>
)}
</Formik>
values is an object with multiple properties, and Form's inputs are responsible for each of those properties. Parent component of this one uses those values to compute other stuff, and I want to pass the whole updated values up there on each field change.
.
With current approach it will pass an outdated obviously (the values right before onChange event fired).
I can manually create an object to pass, taking e.target.value, but it's kind of messy and grows the code base a lot, especially when there are many fields in the form.
My question is if there is a simple way to get updated values after onChange event, from that handler?

This is the solution I've found so far, looks like there are no any updatedValues prop in Formik.
<Form
onChange={e => {
values[e.target.name] = e.target.value
passUpdatedValues(values)
}}
>

Related

Redux-form validation breaks when using multiple components with the same form name

I run into the validation issue using multiple components decorated with same form name.
Let's say we have SimpleForm1 and SimpleForm2. When rendering Only SimpleForm1 with the name field validation works as expected, as well as when rendering SimpleForm2 with the surname field. But when rendering them both on a single page validation for SimpleForm1 is broken.
The question is how to avoid such behaviour and make both validation functions work.
Here is a fiddle which illustrates my problem
It's not a good idea to use same names for multiple forms.
As i understand you need to dynamically add form inputs(SimpleForm2 in your example) and have possibility to submit both forms with one button.
If yes, so you can add just an input to first form, you don't need second form.
Form:
const SimpleFormComponent1 = props => {
const {handleSubmit, pristine, reset, submitting, renderBoth} = props;
const onSubmit = (values) => {
alert(JSON.stringify(values));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
...
{
renderBoth &&
<Field
name="surname"
type="text"
component={renderField}
label="Surname"
validate={validateSurname}
/>
}
...
</form>
)
};
Render function:
render() {
const {renderBoth} = this.state;
return (
<div>
<div className='forms'>
<SimpleForm renderBoth={renderBoth}/>
</div>
<button
type='button'
onClick={this.handleClick}>
{renderBoth ? 'Show Single' : 'Show Both'}
</button>
</div>
);
}
Updated example
SOLUTION 1
I was having interference with invalid and valid props of the Redux Form because I wrote the validate function on ReduxForm() in every file of my main form component.
You haven't to change the form name to solve this. You have to put the validation function on the parent component.
Here is an example:
[CONTEXT]
I have 3 Components:
1. Main component to put the form ('editUserForm') elements (, , ...)
2. Field1 of the form ('editUserForm') that changes user's complete name.
3. Field2 of the form ('editUserForm') that changes user's email.
[SOLUTION]
MAIN COMPONENT:
Inside the main frame, you call reduxForm() (it creates a decorator with which you use redux-form to connect your form component to Redux. More info here Redux Form docs). Your code would be like this:
import...
class MainFrame ... {
...
<form ...>
<Field1 />
<Field2 />
</form>
...
}
const validate ({ name, email }, props) => {
errors={}
// Validation around Field1
if (name === ...) errors.name = "Name error passed to Field1 component";
// Validation around Field2
if (email === ...) errors.email= "Email error passed to Field2 component";
return errors;
}
...
export default reduxForm({
form: 'editUserForm',
validate // <--------- IMPORTANT: Only put this function on the parent component.
})(MainComponent);
FIELD1 & FIELD2 COMPONENTS:
This code is for the 2 children components. IMPORTANT: You call reduxForm() without validate Redux Form docs synchronous function.
import...
class MainFrame ... {
...
const { invalid } = this.props;
...
<inputs
error={invalid}
>
...
</input>
...
}
// IMPORTANT: Don't put the validation function in Field1 and Field2 components, because their local validations will interfere with the validation of the other Field component.
reduxForm({
form: 'editUserForm'
})
Now, the props: valid and invalid will work perfectly inside the children components (Field1 and Field2).
SOLUTION 2
User Redux Form FormSection (docs) to split forms into smaller components that are reusable across multiple forms.

Client side validation not working for hidden field in asp.net mvc 3

I have got a hidden field with a validation for it as below
#Html.HiddenFor(m => m.Rating)
#Html.ValidationMessageFor(m => m.Rating)
The Rating property has Range validator attribute applied with range being 1-5. This is put inside a form with a submit button.
I have then got following jquery that sets the value in hidden field on some user event (Basically user clicks on some stars to rate)
$(".star").click(function(){
$("#Rating").val(2);
});
Now if I submit the form without the user event that sets the hidden field, the validation works. The error messages is displayed properly and it works all client side.
Now, in this situation, if I click on stars, that invokes the above javascript a sets the hidden field, the validation error message would not go away. I can submit the form after the hidden variable has some valid value. But I'm expecting that the client side validation should work. (When the hidden variable has been set with some valid value, the validation error should go away)
Initially I thought, the jquery validation would be invoked on some special events so I tried raising click, change, keyup, blur and focusout events myself as below
$(".star").click(function(){
$("#Rating").val(2);
$("#Rating").change();
});
But this is still not working. The error messages once appeared, does not go away at all.
You can wrap your hidden field with a div put somewhere but still inside the <form>. Add css to kick it to outer space.
<div style="position:absolute; top:-9999px; left:-9999px">
<input id="Rating" type="hidden" name="rating" >
</div>
Then add the following label to where you want to show the error:
<label for="rating" class="error" style="display:none">I am an an error message, please modify me.</label>
Client-side validation ignores hidden fields. You can set the "ignore" option dynamically but just to get it to work I did the following directlyl in the .js file.
For now this should do the trick.
In my aspx...
<%: Html.HiddenFor(model => model.age, new { #class="formValidator" }) %>
In jquery.validate.js
ignore: ":hidden:not('.formValidator')",
This turned out to be a very interesting issue. the default "ignore" setting is ignores hidden fields. The field was hidden in a jQuery ui plug-in. I simply added a class called "includeCheckBox" to the rendered input I wanted to validate and put the following line of code in...
var validator = $('#formMyPita').validate();
validator.settings.ignore = ':hidden:not(".includeCheckBox")';
if ($('#formMyPita').valid()) {....
In the code which sets the hidden field's value, manually invoke validation for the form, like so:
$("form").validate().form();
I think it is because hidden inputs don't fire any of these events.
What you could do instead would be to use a <input type="text" style="display:none" /> instead of the hidden field;
#html.TextBoxFor(m => m.Rating, new {display = "display:none"})

jQuery unobtrusive validation to validate part of a form

We have a ASP.NET MVC 3 application that uses unobtrusive jQuery validation. The page allows to add children objects to the model in the same go. The <form> contains a grid for the children, and some input fields for adding new children.
Simplified example with Issue as the Model and Subtasks as the children:
Issue.cshtml -> Defines the form and includes fields for the issue as well as its subtasks.
#model Issue
#using (Html.BeginForm("Create", "Issues", FormMethod.Post, new { id = "mainForm" })
{
#Html.TextBoxFor(model => model.Summary)
#Html.Partial("SubtaskFields", new Subtask())
#Html.Partial("SubtasksGrid", model.Subtasks)
}
SubtaskFields.cshtml:
#model Subtask
#Html.TextBoxFor(model => model.Summary)
<button id="add">Add</button>
SubtasksGrid.cshtml:
#model IEnumerable<Subtask>
<table>
#foreach (var subtask in Model)
{
<tr>
<td>
#subtask.Name
<input type="hidden" name="Subtasks[#subtask.Index].Name" value="#subtask.Name"/>
</td>
</tr>
}
</table>
The point is, when submitting the form, only the properties of the issue (Issue.Name, e.g.), plus the hidden fields for the children (Subtask.Name, e.g.) should be validated and submitted.
We have some javascript code that hooks on the "add" button, and adds a new subtask based on the values in the SubtaskFields.cshtml partial view. That script validates the input fields first. In order for this to work, we use the TextBoxFor etc. html helpers for the SubtaskFields.cshtml, too, rendering a dummy/default Subtask object (new Subtask()). Our javascript the uses $("#mainForm").validate().element(...) to validate the SubtaskFields before adding a new subtask.
The big problem with this approach is that the jQuery unobtrusive validation framework automatically hooks on the submit button and validates all fields within the form before submitting the form. I.e., even the subtask fields are validated. This does not make any sense. Say that the subtask name is mandatory (which means the user can only click on "add" if he has filled in a subtask name). But if the user does not click on "add", the values in the Subtask Fields don't have any meaning and can in particular be left blank. In this case, in our current setting, jQuery validation fails because a mandatory field was left blank.
How can this be solved?
This is what we've come up with:
Add an attribute to all subtask fields (which should not be validated when submitting the form), e.g. "data-val-ignore".
Set the ignore setting on the form's validator to "[data-val-ignore]"
For the add button, in order to validate the subtask fields (which are normally ignored), iterate over them, and for each field, remove the attribute, re-parse to genereate the rules, execute validation, add the attribute, parse one more time.
Ad 2:
$(document).ready(function () {
$.data($('form')[0], 'validator').settings.ignore = "[data-val-ignore]";
});
Ad 3:
$(allSubtaskFields).each(function() {
$(this).removeAttr("data-val-ignore");
$.validator.unobtrusive.parseElement(this, false);
if (!$("mainForm").validate().element($(this))) { result = false; }
$(this).attr("data-val-ignore", "true");
$.validator.unobtrusive.parseElement(this, false);
});
I would suggest moving #Html.Partial("SubtasksGrid", model.Subtasks) outside of your form, and either having it in a single separate form, or have the partial generate a form for each grid row.
This will address your validation problems with your main form, and should also permit you to simplify validation of each row in SubTasksGrid.
To validate part of the form, wrap the section or the controls you want to validate into a div with an #id or .class and do the following:
var validator = $("#myForm").validate();
var isValid = true;
$("myDivToBeValidated").find("*[data-val]").each(function (indx, elem) {
if (!validator.element(elem)) {
isValid = false;
}
});
//this part of form is valid however there might be some other invalid parts
if (isValid)
//do your action, like go to next step in a wizard or any other action
goToNextStep();
I hope it is clear, if not please leave a comment. For more info about jQuery validation plugin and element() function, check this
Looks like you are working against the MVC egine here.
I would use Editor templates and Display templates, EditorFor template for the stuff you wanna validate and post, and Display template for the stuff you dont wanna post and validate.. If you have a TextBoxFor in the display template make sure its binding property has no Required attribute, and if its a value type make it nullable.

After button disabled its value did not posted to controller

I have an controller which has check like that
if (form["submit"].ToString() == "Continue")
{
}
and i have button which is doing submit
<button name="submit" value="Continue">Continue</button>
It was all working well until i decided to disable Continue button on submit to prevent double click using this function:
$('form').submit(function () {
if ($(this).valid()) {
$(':submit', this).attr('disabled', 'disabled');
}
});
So now i don't get value form["submit"] posted on controller.
Any thoughts how may i fix that?
I want still prevent second click but be able to get form["submit"] value posted on controller.
Can you control the submit value in a hidden field in the form? I can't tell what other logic you might need, but when the form renders, you could set the hidden field's value to the submit button's value and change it when necessary using the first script below. As long as it has a name attribute and is enabled (which you'd rarely disable a hidden field) then it will post when the form is submitted.
$(function() {
// this assumes your button has id="myButton" attribute
$(':hidden[name="submit"]').val($('#myButton').val());
});
And of course in your form, you would need a hidden field with name="submit"
<input type="hidden" name="submit" value="Continue" />
Then, whenever the state of your form changes, modify the disabled state of the button and the value of the hidden field to reflect the value (if it changed at all).
There are also frameworks you may find useful for UI features like this. KnockoutJS comes to mind. It can be used to "value" bind input elements. It's probably overkill for this small example, but it could be useful if your UI expands. I've added markup, script and comments below if you're interested.
$(function () {
var viewModel = {
submitValue: ko.observable("Continue")
};
ko.applyBindings(viewModel);
$('form').submit(function() {
if($(this).valid()) {
// the following line will change the both the hidden field's value
// as well as the button's value attribute
viewModel.submitValue("some other value");
// I couldn't follow your selector here, but please note I changed
// the name of the submit button in the markup below.
$(':submit, this).attr('disabled', 'disabled');
}
});
});
KnockoutJS requires you use the data-bind attribute to setup your elements. In your case, you'd bind one property to multiple elements like this:
<button name="submitButton" data-bind="value: submitValue"/>Continue</button>
<!-- and bind the same value similarly in the hidden field-->
<input type="hidden" name="submit" data-bind="value: submitValue"/>

DropDownList MVC3

I am doing a edit operation on a record in Grid . One of the column is DropDownValue.
When I go to Edit View , depending upon this dropdownvalue , I make few fields editable and readable. And , One more point is here, I didnt select the dropdown Yet, But whatever its value selected before is the one which I should retrieve. I know I have to use jQuery .But I didnt exact Syntax to do tht.
Here is my dropdown
<div id="dvstatus">
#Html.DropDownListFor(model => model.Study.StudyStatusId, Model.StatusSelectList, new { id = "ddlStatus" })
</div>
NOT SELECTED VALUE, BUT THE VALUE WITH WHICH IT IS LOADED
My requirement is how to get the dropdown value item , when it is loaded onto .cshtml
If you're not referring to the selected value of the dropdown then just pass the value from the controller to your view using your model if you're using a strongly-typed view or pass it some other way like using ViewBag and just set the value when it's passed on view.
You can add a hidden field to save the initially loaded value. Eg
<div id="dvstatus">
#Html.HiddenFor(model => model.Study.StudyStatusId)
#Html.DropDownListFor(model => model.Study.StudyStatusId, Model.StatusSelectList, new { id = "ddlStatus" })
</div>
Then you can use java script to compare current value of the drop down and the value of the hidden field(which has the initial value).

Resources