I am using state to get all form values.
const [CheckoutInput, setCheckoutInput] = useState({ name: "", street: "", postal: "", city: "", });
On form submit i would like to verify all javascript object values (By Looping CheckoutInput state), if it was empty i need update error value state.
const [ErrVal, setErrVal] = useState(CheckoutInput);
const checkErr = Object.values(CheckoutInput);
checkErr.forEach((entry) => {
if (CheckoutInput[entry]) {
setErrVal({ entry: "PLease fill the form" });
}
});
I'm failing to loop over all values after submit the form
I Cannot able to setErrVal state, if object has empty values.
Thank you.
Related
I'm using formik to handle form state and pass it on to a function that will send a patch request to the API, but when the form is sent all the fields(including empty and or null) fields so the API treats it as an input and overwrites the previous data.
my formik initialValues:
enableReinitialize
initialValues={{
firstname: userdata?.firstname ?? "",
lastname: userdata?.lastname ?? "",
age: userdata?.age ?? "",
phone: userdata?.phone ?? "",
secondary_phone: userdata?.secondary_phone ?? "",
image: null,
sex: userdata?.sex ?? "",
height: userdata?.height ?? "",
wereda: userdata?.wereda ?? "",
kebele: userdata?.kebele ?? "",
address: userdata?.address ?? "",
}}
in the example above most of the fields will not be null since I am setting the initial data from the server but making this the only guard and putting hope on the user to not clear the input before sending is a big risk, also I cannot send the image URL as a field value to the server since it needs an image file and I don't like the idea of converting the URL to a js File object, so how can I omit all the fields that are "" or null before passing it over to the function that will take care of the API request?? thanks in advance.
Never mind, I googled it and found some code, modified it to look like this:
Object.keys(values).forEach(key => {
if (values[key] === '' || values[key] === null) {
delete values[key];
}
});
It iterates trough the object and deletes any that has a value of either "" or null
I am new to Vue and want to achieve below result.
I have a form of data and a save button, before the page loads, it will fetch database and fill the form data. Because all the form data are filled, the save button is disabled and the user can not click unless the user change some data, then it knows the form data has changed, the save button will no longer be disabled and can be saved.
I know that should use watch property, but actually how I can implement this?
Thank you guys!
The form data is like this
data(){
return {
form: {
firstName: "",
lastName: ""
}
}
}
You can do something as below by having the two different objects actual and modified.
Here I have used underscore js for deep clone or isEqual, so don't forget to import.
computed: {
// Use is property for the save button to enable or disable
isDataChanged: function() {
return _.isEqual(this.actualForm, this.modifiedForm);
}
},
data() {
return {
actualForm: {
firstName: "",
lastName: ""
},
modifiedForm: {
firstName: "",
lastName: ""
}
}
},
methods: {
fetchData: function() {
// get data and assign it to actual form
this.actualForm = responseData;
this.modifiedForm = _.cloneDeep(this.actualForm) // this will create the new instance
}
}
You can use a watch on form like this below. You can also use deep:true if you need to watch nested property within form.
watch: {
form: {
deep: true,
handler(val) {
// Enable save button here. You can also evaluate any other condition to enable
}
}
}
I creating a register form and the problems accurs while try validating password confirmation. I am using the last version of JOI-Browser.
I tried the code below and the validation error was triggered even though password and password confirmation have the same values.
password: Joi.string()
.min(5)
.required(),
passwordConfirmation: Joi.ref("password")
Here is my state object:
password: "12345"
passwordConfirmation: "12345"
username: ""
errors: {…}
passwordConfirmation: "\"passwordConfirmation\" must be one of [ref:password]"
I passed several hours trying several approaches and reading the documentation, but still no luck, the validation is still triggering,
I have other validations in this form and they work fine.
I don't think Joi.ref should be used that way.
I usually tend to do this way:
const passwordConfirmation = Joi.string()
.required()
.valid(Joi.ref('password'))
.options({
language: {
any: {
allowOnly: '!!Passwords do not match',
}
}
})
If you refer to the docs, you will see:
Note that references can only be used where explicitly supported such as in valid() or invalid() rules. If upwards (parents) references are needed, use object.assert().
If anyone has encountered a similar problem, this is the solution I have used:
validateProperty = (input) => {
let obj = { [input.name]: input.value };
let schema = { [input.name]: this.schema[input.name] };
if (input.name.endsWith("_confirm")) {
const dependentInput = input.name.substring(
0,
input.name.indexOf("_confirm")
);
obj[dependentInput] = this.state.data[dependentInput];
schema[dependentInput] = this.schema[dependentInput];
}
const { error } = Joi.validate(obj, schema);
return error ? error.details[0].message : null;
};
In my case, I have looked for _confirm because I have the field names as password and password_confirm. You need to make changes here as per your requirements.
Main logic, you just need to add value and schema of password when you are validating password_confirm
I find out what was happening. My code above was right, the problem was in my validate function.
Gabriele's Petrioli comment help me out. this is the function that cause me problems:
validateProperty = ({ name: propertyName, value }) => {
const obj = { [propertyName]: value };
const schema = { [propertyName]: this.schema[propertyName] };
const { error } = Joi.validate(obj, schema);
return error ? error.details[0].message : null;};
Has you can see i tried validate each property individually, so i can make the form more dynamic.
This trigger the validation error because when i try to validate confirmPassword there was no value in password because i passed only the value the correspond to confirmaPassword, it also needed the password value to make the comparison.
Rookie mistake
My requirement is from page1 to page2(on form submission) user navigates on some actions. if User navigates to page2 to page1(backward) then all the form fields in page1 should be filled. So i have stored all the data in session but on backward navigation not able to assign all the state values from session.
page1 componentmount code:
componentWillMount() {
if (sessionStorage.getItem("regData")) {
let formdata = sessionStorage.getItem("regData");
JSON.parse(formdata, (key, value) => {
this.setState({key:value});});
}
}
//state variables
this.state = {
username: "",
password: "",
email: "",
name: "",
mobile: "",
city: "",
redirectToReferrer: false,
error: {
email_error: "",
password_error: "",
name_error: "",
username_error: "",
mobile_error: "",
showError: false,
errorMessage: engTranslations.global.faild
},
value: "",
suggestions: [],
selectedCity: []
};
Let me know how to assign all state variables at once in component mount method. Thanks in advance.
You don't use the callback for JSON.parse (that's a "reviver" function which is to help with deserializing the data). Instead, take the result of JSON.parse and pass it into setState:
this.setState(JSON.parse(formdata));
More:
JSON.parse
setState
Important information for setting state when it's based on other state or props (yours isn't, so we can just pass an object into setState, but many state updates are based on state or props, and for those, you have to use the callback form of setState)
It's also worth noting that componentWillMount isn't the right lifecycle method to be doing this in. Since there's no asynchronous operation involved, the right place would be your constructor, and your constructor is the one place you can assign directly to this.state, so:
constructor() {
this.state = Object.assign(
{/*...default state here...*/},
JSON.parse(sessionStorage.getItem("regData")) // see note
);
}
(If there's no regData, getItem will return null, which will pass through JSON.parse [it gets converted to "null" and then back to null] and ignored by Object.assign, so we don't need to have the if statement.)
(If there were an asynchronous operation involved, it would be componentDidMount and you'd use setState instead..)
I'm not entirely sure if I got right the requirement but I guess what you need to do, assuming the session storage value contains all the fields you care of, is instead of doing this:
JSON.parse(formdata, (key, value) => {
this.setState({key:value});});
});
Do this:
this.setState(JSON.parse(formdata));
Also as side notes, take into account that using componentWillMount for setting state is considered an anti-pattern(https://vasanthk.gitbooks.io/react-bits/anti-patterns/04.setState-in-componentWillMount.html).
I also think it's not a great idea to store a user password in the session storage.
You can try this, setting state once:-
if (sessionStorage.getItem("regData")) {
let formdata = sessionStorage.getItem("regData");
let obj = {};
JSON.parse(formdata, (key, value) => {
obj.key = value;
});
let newState= {...this.state, ...obj};
this.setState(newState);
}
I have an order model that I’m collecting data for over a multi-page form. I’ve created some validations like this:
const Validations = buildValidations({
// these should only be used when we’re on checkout.info route
name: validator('presence', true),
email: [
validator('presence', true),
validator('format', { type: 'email' })
],
// these should only be used when we’re on checkout.shipping route
address1: validator('presence', true),
address2: validator('presence', true),
city: validator('presence', true),
});
My model is set up to use them like this:
export default Model.extend(Validations, {
// model set-up in here
})
What I’d like to happen is for it to only validate name and email when I’m on checkout.info and to validate address1, address2 and city when I’m on checkout.shipping.
One of the things I’ve tried already is running the validations inside of my checkout-form component:
let { m, validations } = order.validateSync({
on: ['name', 'email']
})
const isValid = validations.get('isValid')
order.set('didValidate', isValid)
The problem is that this doesn’t seem to unblock the disabled state on my form’s next button
{{next-button disabled=(v-get model.order 'isInvalid')}}
Another thing I tried was to build a custom routed-presence validator that disables presence when it’s not on the current route. The trouble with this is that other validators will still block this (e.g. type or length).
How might I go about achieving this?
Although it's not well documented, you can enable or disable validations based on a condition that your model computes:
import { validator, buildValidations } from 'ember-cp-validations';
export default Ember.Object.extend(buildValidations({
email: {
disabled: Ember.computed.alias('model.isCheckoutPage'),
validators: [
...
],
}
}), {
// could be a computed property too
isCheckoutPage: false,
});